123cd1385SRemy Bohmer /* 223cd1385SRemy Bohmer * Copyright (C) 2003 David Brownell 323cd1385SRemy Bohmer * 423cd1385SRemy Bohmer * This program is free software; you can redistribute it and/or modify 523cd1385SRemy Bohmer * it under the terms of the GNU Lesser General Public License as published 623cd1385SRemy Bohmer * by the Free Software Foundation; either version 2.1 of the License, or 723cd1385SRemy Bohmer * (at your option) any later version. 823cd1385SRemy Bohmer * 923cd1385SRemy Bohmer * Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and 1023cd1385SRemy Bohmer * Remy Bohmer <linux@bohmer.net> 1123cd1385SRemy Bohmer */ 1223cd1385SRemy Bohmer 1323cd1385SRemy Bohmer #include <common.h> 1423cd1385SRemy Bohmer #include <asm/errno.h> 1523cd1385SRemy Bohmer #include <linux/usb/ch9.h> 1623cd1385SRemy Bohmer #include <linux/usb/gadget.h> 1723cd1385SRemy Bohmer 1823cd1385SRemy Bohmer #include <asm/unaligned.h> 1923cd1385SRemy Bohmer 2023cd1385SRemy Bohmer 2123cd1385SRemy Bohmer static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) 2223cd1385SRemy Bohmer { 2323cd1385SRemy Bohmer int count = 0; 2423cd1385SRemy Bohmer u8 c; 2523cd1385SRemy Bohmer u16 uchar; 2623cd1385SRemy Bohmer 27*6142e0aeSVitaly Kuzmichev /* 28*6142e0aeSVitaly Kuzmichev * this insists on correct encodings, though not minimal ones. 2923cd1385SRemy Bohmer * BUT it currently rejects legit 4-byte UTF-8 code points, 3023cd1385SRemy Bohmer * which need surrogate pairs. (Unicode 3.1 can use them.) 3123cd1385SRemy Bohmer */ 3223cd1385SRemy Bohmer while (len != 0 && (c = (u8) *s++) != 0) { 3323cd1385SRemy Bohmer if ((c & 0x80)) { 34*6142e0aeSVitaly Kuzmichev /* 35*6142e0aeSVitaly Kuzmichev * 2-byte sequence: 36*6142e0aeSVitaly Kuzmichev * 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx 37*6142e0aeSVitaly Kuzmichev */ 3823cd1385SRemy Bohmer if ((c & 0xe0) == 0xc0) { 3923cd1385SRemy Bohmer uchar = (c & 0x1f) << 6; 4023cd1385SRemy Bohmer 4123cd1385SRemy Bohmer c = (u8) *s++; 4223cd1385SRemy Bohmer if ((c & 0xc0) != 0x80) 4323cd1385SRemy Bohmer goto fail; 4423cd1385SRemy Bohmer c &= 0x3f; 4523cd1385SRemy Bohmer uchar |= c; 4623cd1385SRemy Bohmer 47*6142e0aeSVitaly Kuzmichev /* 48*6142e0aeSVitaly Kuzmichev * 3-byte sequence (most CJKV characters): 49*6142e0aeSVitaly Kuzmichev * zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx 50*6142e0aeSVitaly Kuzmichev */ 5123cd1385SRemy Bohmer } else if ((c & 0xf0) == 0xe0) { 5223cd1385SRemy Bohmer uchar = (c & 0x0f) << 12; 5323cd1385SRemy Bohmer 5423cd1385SRemy Bohmer c = (u8) *s++; 5523cd1385SRemy Bohmer if ((c & 0xc0) != 0x80) 5623cd1385SRemy Bohmer goto fail; 5723cd1385SRemy Bohmer c &= 0x3f; 5823cd1385SRemy Bohmer uchar |= c << 6; 5923cd1385SRemy Bohmer 6023cd1385SRemy Bohmer c = (u8) *s++; 6123cd1385SRemy Bohmer if ((c & 0xc0) != 0x80) 6223cd1385SRemy Bohmer goto fail; 6323cd1385SRemy Bohmer c &= 0x3f; 6423cd1385SRemy Bohmer uchar |= c; 6523cd1385SRemy Bohmer 6623cd1385SRemy Bohmer /* no bogus surrogates */ 6723cd1385SRemy Bohmer if (0xd800 <= uchar && uchar <= 0xdfff) 6823cd1385SRemy Bohmer goto fail; 6923cd1385SRemy Bohmer 70*6142e0aeSVitaly Kuzmichev /* 71*6142e0aeSVitaly Kuzmichev * 4-byte sequence (surrogate pairs, currently rare): 72*6142e0aeSVitaly Kuzmichev * 11101110wwwwzzzzyy + 110111yyyyxxxxxx 73*6142e0aeSVitaly Kuzmichev * = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 74*6142e0aeSVitaly Kuzmichev * (uuuuu = wwww + 1) 75*6142e0aeSVitaly Kuzmichev * FIXME accept the surrogate code points (only) 76*6142e0aeSVitaly Kuzmichev */ 7723cd1385SRemy Bohmer } else 7823cd1385SRemy Bohmer goto fail; 7923cd1385SRemy Bohmer } else 8023cd1385SRemy Bohmer uchar = c; 8123cd1385SRemy Bohmer put_unaligned_le16(uchar, cp++); 8223cd1385SRemy Bohmer count++; 8323cd1385SRemy Bohmer len--; 8423cd1385SRemy Bohmer } 8523cd1385SRemy Bohmer return count; 8623cd1385SRemy Bohmer fail: 8723cd1385SRemy Bohmer return -1; 8823cd1385SRemy Bohmer } 8923cd1385SRemy Bohmer 9023cd1385SRemy Bohmer 9123cd1385SRemy Bohmer /** 9223cd1385SRemy Bohmer * usb_gadget_get_string - fill out a string descriptor 9323cd1385SRemy Bohmer * @table: of c strings encoded using UTF-8 9423cd1385SRemy Bohmer * @id: string id, from low byte of wValue in get string descriptor 9523cd1385SRemy Bohmer * @buf: at least 256 bytes 9623cd1385SRemy Bohmer * 9723cd1385SRemy Bohmer * Finds the UTF-8 string matching the ID, and converts it into a 9823cd1385SRemy Bohmer * string descriptor in utf16-le. 9923cd1385SRemy Bohmer * Returns length of descriptor (always even) or negative errno 10023cd1385SRemy Bohmer * 10123cd1385SRemy Bohmer * If your driver needs stings in multiple languages, you'll probably 10223cd1385SRemy Bohmer * "switch (wIndex) { ... }" in your ep0 string descriptor logic, 10323cd1385SRemy Bohmer * using this routine after choosing which set of UTF-8 strings to use. 10423cd1385SRemy Bohmer * Note that US-ASCII is a strict subset of UTF-8; any string bytes with 10523cd1385SRemy Bohmer * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 10623cd1385SRemy Bohmer * characters (which are also widely used in C strings). 10723cd1385SRemy Bohmer */ 10823cd1385SRemy Bohmer int 10923cd1385SRemy Bohmer usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf) 11023cd1385SRemy Bohmer { 11123cd1385SRemy Bohmer struct usb_string *s; 11223cd1385SRemy Bohmer int len; 11323cd1385SRemy Bohmer 11423cd1385SRemy Bohmer /* descriptor 0 has the language id */ 11523cd1385SRemy Bohmer if (id == 0) { 11623cd1385SRemy Bohmer buf[0] = 4; 11723cd1385SRemy Bohmer buf[1] = USB_DT_STRING; 11823cd1385SRemy Bohmer buf[2] = (u8) table->language; 11923cd1385SRemy Bohmer buf[3] = (u8) (table->language >> 8); 12023cd1385SRemy Bohmer return 4; 12123cd1385SRemy Bohmer } 12223cd1385SRemy Bohmer for (s = table->strings; s && s->s; s++) 12323cd1385SRemy Bohmer if (s->id == id) 12423cd1385SRemy Bohmer break; 12523cd1385SRemy Bohmer 12623cd1385SRemy Bohmer /* unrecognized: stall. */ 12723cd1385SRemy Bohmer if (!s || !s->s) 12823cd1385SRemy Bohmer return -EINVAL; 12923cd1385SRemy Bohmer 13023cd1385SRemy Bohmer /* string descriptors have length, tag, then UTF16-LE text */ 13123cd1385SRemy Bohmer len = min((size_t) 126, strlen(s->s)); 13223cd1385SRemy Bohmer memset(buf + 2, 0, 2 * len); /* zero all the bytes */ 13323cd1385SRemy Bohmer len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len); 13423cd1385SRemy Bohmer if (len < 0) 13523cd1385SRemy Bohmer return -EINVAL; 13623cd1385SRemy Bohmer buf[0] = (len + 1) * 2; 13723cd1385SRemy Bohmer buf[1] = USB_DT_STRING; 13823cd1385SRemy Bohmer return buf[0]; 13923cd1385SRemy Bohmer } 14023cd1385SRemy Bohmer 141