diff options
Diffstat (limited to 'drivers/usb/gadget/usbstring.c')
-rw-r--r-- | drivers/usb/gadget/usbstring.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c new file mode 100644 index 000000000000..b1735767660b --- /dev/null +++ b/drivers/usb/gadget/usbstring.c | |||
@@ -0,0 +1,136 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003 David Brownell | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU Lesser General Public License as published | ||
6 | * by the Free Software Foundation; either version 2.1 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/list.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/init.h> | ||
16 | |||
17 | #include <linux/usb_ch9.h> | ||
18 | #include <linux/usb_gadget.h> | ||
19 | |||
20 | #include <asm/unaligned.h> | ||
21 | |||
22 | |||
23 | static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) | ||
24 | { | ||
25 | int count = 0; | ||
26 | u8 c; | ||
27 | u16 uchar; | ||
28 | |||
29 | /* this insists on correct encodings, though not minimal ones. | ||
30 | * BUT it currently rejects legit 4-byte UTF-8 code points, | ||
31 | * which need surrogate pairs. (Unicode 3.1 can use them.) | ||
32 | */ | ||
33 | while (len != 0 && (c = (u8) *s++) != 0) { | ||
34 | if (unlikely(c & 0x80)) { | ||
35 | // 2-byte sequence: | ||
36 | // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx | ||
37 | if ((c & 0xe0) == 0xc0) { | ||
38 | uchar = (c & 0x1f) << 6; | ||
39 | |||
40 | c = (u8) *s++; | ||
41 | if ((c & 0xc0) != 0xc0) | ||
42 | goto fail; | ||
43 | c &= 0x3f; | ||
44 | uchar |= c; | ||
45 | |||
46 | // 3-byte sequence (most CJKV characters): | ||
47 | // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx | ||
48 | } else if ((c & 0xf0) == 0xe0) { | ||
49 | uchar = (c & 0x0f) << 12; | ||
50 | |||
51 | c = (u8) *s++; | ||
52 | if ((c & 0xc0) != 0xc0) | ||
53 | goto fail; | ||
54 | c &= 0x3f; | ||
55 | uchar |= c << 6; | ||
56 | |||
57 | c = (u8) *s++; | ||
58 | if ((c & 0xc0) != 0xc0) | ||
59 | goto fail; | ||
60 | c &= 0x3f; | ||
61 | uchar |= c; | ||
62 | |||
63 | /* no bogus surrogates */ | ||
64 | if (0xd800 <= uchar && uchar <= 0xdfff) | ||
65 | goto fail; | ||
66 | |||
67 | // 4-byte sequence (surrogate pairs, currently rare): | ||
68 | // 11101110wwwwzzzzyy + 110111yyyyxxxxxx | ||
69 | // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx | ||
70 | // (uuuuu = wwww + 1) | ||
71 | // FIXME accept the surrogate code points (only) | ||
72 | |||
73 | } else | ||
74 | goto fail; | ||
75 | } else | ||
76 | uchar = c; | ||
77 | put_unaligned (cpu_to_le16 (uchar), cp++); | ||
78 | count++; | ||
79 | len--; | ||
80 | } | ||
81 | return count; | ||
82 | fail: | ||
83 | return -1; | ||
84 | } | ||
85 | |||
86 | |||
87 | /** | ||
88 | * usb_gadget_get_string - fill out a string descriptor | ||
89 | * @table: of c strings encoded using UTF-8 | ||
90 | * @id: string id, from low byte of wValue in get string descriptor | ||
91 | * @buf: at least 256 bytes | ||
92 | * | ||
93 | * Finds the UTF-8 string matching the ID, and converts it into a | ||
94 | * string descriptor in utf16-le. | ||
95 | * Returns length of descriptor (always even) or negative errno | ||
96 | * | ||
97 | * If your driver needs stings in multiple languages, you'll probably | ||
98 | * "switch (wIndex) { ... }" in your ep0 string descriptor logic, | ||
99 | * using this routine after choosing which set of UTF-8 strings to use. | ||
100 | * Note that US-ASCII is a strict subset of UTF-8; any string bytes with | ||
101 | * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 | ||
102 | * characters (which are also widely used in C strings). | ||
103 | */ | ||
104 | int | ||
105 | usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) | ||
106 | { | ||
107 | struct usb_string *s; | ||
108 | int len; | ||
109 | |||
110 | /* descriptor 0 has the language id */ | ||
111 | if (id == 0) { | ||
112 | buf [0] = 4; | ||
113 | buf [1] = USB_DT_STRING; | ||
114 | buf [2] = (u8) table->language; | ||
115 | buf [3] = (u8) (table->language >> 8); | ||
116 | return 4; | ||
117 | } | ||
118 | for (s = table->strings; s && s->s; s++) | ||
119 | if (s->id == id) | ||
120 | break; | ||
121 | |||
122 | /* unrecognized: stall. */ | ||
123 | if (!s || !s->s) | ||
124 | return -EINVAL; | ||
125 | |||
126 | /* string descriptors have length, tag, then UTF16-LE text */ | ||
127 | len = min ((size_t) 126, strlen (s->s)); | ||
128 | memset (buf + 2, 0, 2 * len); /* zero all the bytes */ | ||
129 | len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len); | ||
130 | if (len < 0) | ||
131 | return -EINVAL; | ||
132 | buf [0] = (len + 1) * 2; | ||
133 | buf [1] = USB_DT_STRING; | ||
134 | return buf [0]; | ||
135 | } | ||
136 | |||