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