123cd1385SRemy Bohmer /* 223cd1385SRemy Bohmer * Copyright (C) 2003 David Brownell 323cd1385SRemy Bohmer * 4eee479cfSWolfgang Denk * SPDX-License-Identifier: LGPL-2.1+ 523cd1385SRemy Bohmer * 623cd1385SRemy Bohmer * Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and 723cd1385SRemy Bohmer * Remy Bohmer <linux@bohmer.net> 823cd1385SRemy Bohmer */ 923cd1385SRemy Bohmer 1023cd1385SRemy Bohmer #include <common.h> 1123cd1385SRemy Bohmer #include <asm/errno.h> 1223cd1385SRemy Bohmer #include <linux/usb/ch9.h> 1323cd1385SRemy Bohmer #include <linux/usb/gadget.h> 1423cd1385SRemy Bohmer 1523cd1385SRemy Bohmer #include <asm/unaligned.h> 1623cd1385SRemy Bohmer 1723cd1385SRemy Bohmer 1823cd1385SRemy Bohmer static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) 1923cd1385SRemy Bohmer { 2023cd1385SRemy Bohmer int count = 0; 2123cd1385SRemy Bohmer u8 c; 2223cd1385SRemy Bohmer u16 uchar; 2323cd1385SRemy Bohmer 246142e0aeSVitaly Kuzmichev /* 256142e0aeSVitaly Kuzmichev * this insists on correct encodings, though not minimal ones. 2623cd1385SRemy Bohmer * BUT it currently rejects legit 4-byte UTF-8 code points, 2723cd1385SRemy Bohmer * which need surrogate pairs. (Unicode 3.1 can use them.) 2823cd1385SRemy Bohmer */ 2923cd1385SRemy Bohmer while (len != 0 && (c = (u8) *s++) != 0) { 3023cd1385SRemy Bohmer if ((c & 0x80)) { 316142e0aeSVitaly Kuzmichev /* 326142e0aeSVitaly Kuzmichev * 2-byte sequence: 336142e0aeSVitaly Kuzmichev * 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx 346142e0aeSVitaly Kuzmichev */ 3523cd1385SRemy Bohmer if ((c & 0xe0) == 0xc0) { 3623cd1385SRemy Bohmer uchar = (c & 0x1f) << 6; 3723cd1385SRemy Bohmer 3823cd1385SRemy Bohmer c = (u8) *s++; 3923cd1385SRemy Bohmer if ((c & 0xc0) != 0x80) 4023cd1385SRemy Bohmer goto fail; 4123cd1385SRemy Bohmer c &= 0x3f; 4223cd1385SRemy Bohmer uchar |= c; 4323cd1385SRemy Bohmer 446142e0aeSVitaly Kuzmichev /* 456142e0aeSVitaly Kuzmichev * 3-byte sequence (most CJKV characters): 466142e0aeSVitaly Kuzmichev * zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx 476142e0aeSVitaly Kuzmichev */ 4823cd1385SRemy Bohmer } else if ((c & 0xf0) == 0xe0) { 4923cd1385SRemy Bohmer uchar = (c & 0x0f) << 12; 5023cd1385SRemy Bohmer 5123cd1385SRemy Bohmer c = (u8) *s++; 5223cd1385SRemy Bohmer if ((c & 0xc0) != 0x80) 5323cd1385SRemy Bohmer goto fail; 5423cd1385SRemy Bohmer c &= 0x3f; 5523cd1385SRemy Bohmer uchar |= c << 6; 5623cd1385SRemy Bohmer 5723cd1385SRemy Bohmer c = (u8) *s++; 5823cd1385SRemy Bohmer if ((c & 0xc0) != 0x80) 5923cd1385SRemy Bohmer goto fail; 6023cd1385SRemy Bohmer c &= 0x3f; 6123cd1385SRemy Bohmer uchar |= c; 6223cd1385SRemy Bohmer 6323cd1385SRemy Bohmer /* no bogus surrogates */ 6423cd1385SRemy Bohmer if (0xd800 <= uchar && uchar <= 0xdfff) 6523cd1385SRemy Bohmer goto fail; 6623cd1385SRemy Bohmer 676142e0aeSVitaly Kuzmichev /* 686142e0aeSVitaly Kuzmichev * 4-byte sequence (surrogate pairs, currently rare): 696142e0aeSVitaly Kuzmichev * 11101110wwwwzzzzyy + 110111yyyyxxxxxx 706142e0aeSVitaly Kuzmichev * = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 716142e0aeSVitaly Kuzmichev * (uuuuu = wwww + 1) 726142e0aeSVitaly Kuzmichev * FIXME accept the surrogate code points (only) 736142e0aeSVitaly Kuzmichev */ 7423cd1385SRemy Bohmer } else 7523cd1385SRemy Bohmer goto fail; 7623cd1385SRemy Bohmer } else 7723cd1385SRemy Bohmer uchar = c; 7823cd1385SRemy Bohmer put_unaligned_le16(uchar, cp++); 7923cd1385SRemy Bohmer count++; 8023cd1385SRemy Bohmer len--; 8123cd1385SRemy Bohmer } 8223cd1385SRemy Bohmer return count; 8323cd1385SRemy Bohmer fail: 8423cd1385SRemy Bohmer return -1; 8523cd1385SRemy Bohmer } 8623cd1385SRemy Bohmer 8723cd1385SRemy Bohmer 8823cd1385SRemy Bohmer /** 8923cd1385SRemy Bohmer * usb_gadget_get_string - fill out a string descriptor 9023cd1385SRemy Bohmer * @table: of c strings encoded using UTF-8 9123cd1385SRemy Bohmer * @id: string id, from low byte of wValue in get string descriptor 9223cd1385SRemy Bohmer * @buf: at least 256 bytes 9323cd1385SRemy Bohmer * 9423cd1385SRemy Bohmer * Finds the UTF-8 string matching the ID, and converts it into a 9523cd1385SRemy Bohmer * string descriptor in utf16-le. 9623cd1385SRemy Bohmer * Returns length of descriptor (always even) or negative errno 9723cd1385SRemy Bohmer * 9823cd1385SRemy Bohmer * If your driver needs stings in multiple languages, you'll probably 9923cd1385SRemy Bohmer * "switch (wIndex) { ... }" in your ep0 string descriptor logic, 10023cd1385SRemy Bohmer * using this routine after choosing which set of UTF-8 strings to use. 10123cd1385SRemy Bohmer * Note that US-ASCII is a strict subset of UTF-8; any string bytes with 10223cd1385SRemy Bohmer * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 10323cd1385SRemy Bohmer * characters (which are also widely used in C strings). 10423cd1385SRemy Bohmer */ 10523cd1385SRemy Bohmer int 10623cd1385SRemy Bohmer usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf) 10723cd1385SRemy Bohmer { 10823cd1385SRemy Bohmer struct usb_string *s; 10923cd1385SRemy Bohmer int len; 11023cd1385SRemy Bohmer 111*52d45012SRob Herring if (!table) 112*52d45012SRob Herring return -EINVAL; 113*52d45012SRob Herring 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 } 140