1 /* 2 * Copyright (C) 2003 David Brownell 3 * 4 * SPDX-License-Identifier: LGPL-2.1+ 5 * 6 * Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and 7 * Remy Bohmer <linux@bohmer.net> 8 */ 9 10 #include <common.h> 11 #include <asm/errno.h> 12 #include <linux/usb/ch9.h> 13 #include <linux/usb/gadget.h> 14 15 #include <asm/unaligned.h> 16 17 18 static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) 19 { 20 int count = 0; 21 u8 c; 22 u16 uchar; 23 24 /* 25 * this insists on correct encodings, though not minimal ones. 26 * BUT it currently rejects legit 4-byte UTF-8 code points, 27 * which need surrogate pairs. (Unicode 3.1 can use them.) 28 */ 29 while (len != 0 && (c = (u8) *s++) != 0) { 30 if ((c & 0x80)) { 31 /* 32 * 2-byte sequence: 33 * 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx 34 */ 35 if ((c & 0xe0) == 0xc0) { 36 uchar = (c & 0x1f) << 6; 37 38 c = (u8) *s++; 39 if ((c & 0xc0) != 0x80) 40 goto fail; 41 c &= 0x3f; 42 uchar |= c; 43 44 /* 45 * 3-byte sequence (most CJKV characters): 46 * zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx 47 */ 48 } else if ((c & 0xf0) == 0xe0) { 49 uchar = (c & 0x0f) << 12; 50 51 c = (u8) *s++; 52 if ((c & 0xc0) != 0x80) 53 goto fail; 54 c &= 0x3f; 55 uchar |= c << 6; 56 57 c = (u8) *s++; 58 if ((c & 0xc0) != 0x80) 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 /* 68 * 4-byte sequence (surrogate pairs, currently rare): 69 * 11101110wwwwzzzzyy + 110111yyyyxxxxxx 70 * = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 71 * (uuuuu = wwww + 1) 72 * FIXME accept the surrogate code points (only) 73 */ 74 } else 75 goto fail; 76 } else 77 uchar = c; 78 put_unaligned_le16(uchar, cp++); 79 count++; 80 len--; 81 } 82 return count; 83 fail: 84 return -1; 85 } 86 87 88 /** 89 * usb_gadget_get_string - fill out a string descriptor 90 * @table: of c strings encoded using UTF-8 91 * @id: string id, from low byte of wValue in get string descriptor 92 * @buf: at least 256 bytes 93 * 94 * Finds the UTF-8 string matching the ID, and converts it into a 95 * string descriptor in utf16-le. 96 * Returns length of descriptor (always even) or negative errno 97 * 98 * If your driver needs stings in multiple languages, you'll probably 99 * "switch (wIndex) { ... }" in your ep0 string descriptor logic, 100 * using this routine after choosing which set of UTF-8 strings to use. 101 * Note that US-ASCII is a strict subset of UTF-8; any string bytes with 102 * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 103 * characters (which are also widely used in C strings). 104 */ 105 int 106 usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf) 107 { 108 struct usb_string *s; 109 int len; 110 111 if (!table) 112 return -EINVAL; 113 114 /* descriptor 0 has the language id */ 115 if (id == 0) { 116 buf[0] = 4; 117 buf[1] = USB_DT_STRING; 118 buf[2] = (u8) table->language; 119 buf[3] = (u8) (table->language >> 8); 120 return 4; 121 } 122 for (s = table->strings; s && s->s; s++) 123 if (s->id == id) 124 break; 125 126 /* unrecognized: stall. */ 127 if (!s || !s->s) 128 return -EINVAL; 129 130 /* string descriptors have length, tag, then UTF16-LE text */ 131 len = min((size_t) 126, strlen(s->s)); 132 memset(buf + 2, 0, 2 * len); /* zero all the bytes */ 133 len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len); 134 if (len < 0) 135 return -EINVAL; 136 buf[0] = (len + 1) * 2; 137 buf[1] = USB_DT_STRING; 138 return buf[0]; 139 } 140