xref: /linux/drivers/platform/wmi/string.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*b990a06fSArmin Wolf // SPDX-License-Identifier: GPL-2.0-or-later
2*b990a06fSArmin Wolf /*
3*b990a06fSArmin Wolf  * WMI string utility functions.
4*b990a06fSArmin Wolf  *
5*b990a06fSArmin Wolf  * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6*b990a06fSArmin Wolf  */
7*b990a06fSArmin Wolf 
8*b990a06fSArmin Wolf #include <linux/build_bug.h>
9*b990a06fSArmin Wolf #include <linux/compiler_types.h>
10*b990a06fSArmin Wolf #include <linux/err.h>
11*b990a06fSArmin Wolf #include <linux/export.h>
12*b990a06fSArmin Wolf #include <linux/nls.h>
13*b990a06fSArmin Wolf #include <linux/limits.h>
14*b990a06fSArmin Wolf #include <linux/types.h>
15*b990a06fSArmin Wolf #include <linux/wmi.h>
16*b990a06fSArmin Wolf 
17*b990a06fSArmin Wolf #include <asm/byteorder.h>
18*b990a06fSArmin Wolf 
19*b990a06fSArmin Wolf static_assert(sizeof(__le16) == sizeof(wchar_t));
20*b990a06fSArmin Wolf 
21*b990a06fSArmin Wolf /**
22*b990a06fSArmin Wolf  * wmi_string_to_utf8s - Convert a WMI string into a UTF8 string.
23*b990a06fSArmin Wolf  * @str: WMI string representation
24*b990a06fSArmin Wolf  * @dst: Buffer to fill with UTF8 characters
25*b990a06fSArmin Wolf  * @length: Length of the destination buffer
26*b990a06fSArmin Wolf  *
27*b990a06fSArmin Wolf  * Convert as WMI string into a standard UTF8 string. The conversion will stop
28*b990a06fSArmin Wolf  * once a NUL character is detected or when the buffer is full. Any invalid UTF16
29*b990a06fSArmin Wolf  * characters will be ignored. The resulting UTF8 string will always be NUL-terminated
30*b990a06fSArmin Wolf  * when this function returns successfully.
31*b990a06fSArmin Wolf  *
32*b990a06fSArmin Wolf  * Return: Length of the resulting UTF8 string or negative errno code on failure.
33*b990a06fSArmin Wolf  */
34*b990a06fSArmin Wolf ssize_t wmi_string_to_utf8s(const struct wmi_string *str, u8 *dst, size_t length)
35*b990a06fSArmin Wolf {
36*b990a06fSArmin Wolf 	/* Contains the maximum number of UTF16 code points to read */
37*b990a06fSArmin Wolf 	int inlen = le16_to_cpu(str->length) / 2;
38*b990a06fSArmin Wolf 	int ret;
39*b990a06fSArmin Wolf 
40*b990a06fSArmin Wolf 	if (length < 1)
41*b990a06fSArmin Wolf 		return -EINVAL;
42*b990a06fSArmin Wolf 
43*b990a06fSArmin Wolf 	/* We must leave room for the NUL character at the end of the destination buffer */
44*b990a06fSArmin Wolf 	ret = utf16s_to_utf8s((__force const wchar_t *)str->chars, inlen, UTF16_LITTLE_ENDIAN, dst,
45*b990a06fSArmin Wolf 			      length - 1);
46*b990a06fSArmin Wolf 	if (ret < 0)
47*b990a06fSArmin Wolf 		return ret;
48*b990a06fSArmin Wolf 
49*b990a06fSArmin Wolf 	dst[ret] = '\0';
50*b990a06fSArmin Wolf 
51*b990a06fSArmin Wolf 	return ret;
52*b990a06fSArmin Wolf }
53*b990a06fSArmin Wolf EXPORT_SYMBOL_GPL(wmi_string_to_utf8s);
54*b990a06fSArmin Wolf 
55*b990a06fSArmin Wolf /**
56*b990a06fSArmin Wolf  * wmi_string_from_utf8s - Convert a UTF8 string into a WMI string.
57*b990a06fSArmin Wolf  * @str: WMI string representation
58*b990a06fSArmin Wolf  * @max_chars: Maximum number of UTF16 code points to store inside the WMI string
59*b990a06fSArmin Wolf  * @src: UTF8 string to convert
60*b990a06fSArmin Wolf  * @src_length: Length of the source string without any trailing NUL-characters
61*b990a06fSArmin Wolf  *
62*b990a06fSArmin Wolf  * Convert a UTF8 string into a WMI string. The conversion will stop when the WMI string is
63*b990a06fSArmin Wolf  * full. The resulting WMI string will always be NUL-terminated and have its length field set
64*b990a06fSArmin Wolf  * to and appropriate value when this function returns successfully.
65*b990a06fSArmin Wolf  *
66*b990a06fSArmin Wolf  * Return: Number of UTF16 code points inside the WMI string or negative errno code on failure.
67*b990a06fSArmin Wolf  */
68*b990a06fSArmin Wolf ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8 *src,
69*b990a06fSArmin Wolf 			      size_t src_length)
70*b990a06fSArmin Wolf {
71*b990a06fSArmin Wolf 	size_t str_length;
72*b990a06fSArmin Wolf 	int ret;
73*b990a06fSArmin Wolf 
74*b990a06fSArmin Wolf 	if (max_chars < 1)
75*b990a06fSArmin Wolf 		return -EINVAL;
76*b990a06fSArmin Wolf 
77*b990a06fSArmin Wolf 	/* We must leave room for the NUL character at the end of the WMI string */
78*b990a06fSArmin Wolf 	ret = utf8s_to_utf16s(src, src_length, UTF16_LITTLE_ENDIAN, (__force wchar_t *)str->chars,
79*b990a06fSArmin Wolf 			      max_chars - 1);
80*b990a06fSArmin Wolf 	if (ret < 0)
81*b990a06fSArmin Wolf 		return ret;
82*b990a06fSArmin Wolf 
83*b990a06fSArmin Wolf 	str_length = (ret + 1) * sizeof(u16);
84*b990a06fSArmin Wolf 	if (str_length > U16_MAX)
85*b990a06fSArmin Wolf 		return -EOVERFLOW;
86*b990a06fSArmin Wolf 
87*b990a06fSArmin Wolf 	str->length = cpu_to_le16(str_length);
88*b990a06fSArmin Wolf 	str->chars[ret] = '\0';
89*b990a06fSArmin Wolf 
90*b990a06fSArmin Wolf 	return ret;
91*b990a06fSArmin Wolf }
92*b990a06fSArmin Wolf EXPORT_SYMBOL_GPL(wmi_string_from_utf8s);
93