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