1*83d290c5STom Rini // SPDX-License-Identifier: LGPL-2.1+
223cd1385SRemy Bohmer /*
323cd1385SRemy Bohmer * Copyright (C) 2003 David Brownell
423cd1385SRemy Bohmer *
5a187559eSBin Meng * Ported to U-Boot by: Thomas Smits <ts.smits@gmail.com> and
623cd1385SRemy Bohmer * Remy Bohmer <linux@bohmer.net>
723cd1385SRemy Bohmer */
823cd1385SRemy Bohmer
923cd1385SRemy Bohmer #include <common.h>
101221ce45SMasahiro Yamada #include <linux/errno.h>
1123cd1385SRemy Bohmer #include <linux/usb/ch9.h>
1223cd1385SRemy Bohmer #include <linux/usb/gadget.h>
1323cd1385SRemy Bohmer
1423cd1385SRemy Bohmer #include <asm/unaligned.h>
1523cd1385SRemy Bohmer
1623cd1385SRemy Bohmer
utf8_to_utf16le(const char * s,__le16 * cp,unsigned len)1723cd1385SRemy Bohmer static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
1823cd1385SRemy Bohmer {
1923cd1385SRemy Bohmer int count = 0;
2023cd1385SRemy Bohmer u8 c;
2123cd1385SRemy Bohmer u16 uchar;
2223cd1385SRemy Bohmer
236142e0aeSVitaly Kuzmichev /*
246142e0aeSVitaly Kuzmichev * this insists on correct encodings, though not minimal ones.
2523cd1385SRemy Bohmer * BUT it currently rejects legit 4-byte UTF-8 code points,
2623cd1385SRemy Bohmer * which need surrogate pairs. (Unicode 3.1 can use them.)
2723cd1385SRemy Bohmer */
2823cd1385SRemy Bohmer while (len != 0 && (c = (u8) *s++) != 0) {
2923cd1385SRemy Bohmer if ((c & 0x80)) {
306142e0aeSVitaly Kuzmichev /*
316142e0aeSVitaly Kuzmichev * 2-byte sequence:
326142e0aeSVitaly Kuzmichev * 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
336142e0aeSVitaly Kuzmichev */
3423cd1385SRemy Bohmer if ((c & 0xe0) == 0xc0) {
3523cd1385SRemy Bohmer uchar = (c & 0x1f) << 6;
3623cd1385SRemy Bohmer
3723cd1385SRemy Bohmer c = (u8) *s++;
3823cd1385SRemy Bohmer if ((c & 0xc0) != 0x80)
3923cd1385SRemy Bohmer goto fail;
4023cd1385SRemy Bohmer c &= 0x3f;
4123cd1385SRemy Bohmer uchar |= c;
4223cd1385SRemy Bohmer
436142e0aeSVitaly Kuzmichev /*
446142e0aeSVitaly Kuzmichev * 3-byte sequence (most CJKV characters):
456142e0aeSVitaly Kuzmichev * zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
466142e0aeSVitaly Kuzmichev */
4723cd1385SRemy Bohmer } else if ((c & 0xf0) == 0xe0) {
4823cd1385SRemy Bohmer uchar = (c & 0x0f) << 12;
4923cd1385SRemy Bohmer
5023cd1385SRemy Bohmer c = (u8) *s++;
5123cd1385SRemy Bohmer if ((c & 0xc0) != 0x80)
5223cd1385SRemy Bohmer goto fail;
5323cd1385SRemy Bohmer c &= 0x3f;
5423cd1385SRemy Bohmer uchar |= c << 6;
5523cd1385SRemy Bohmer
5623cd1385SRemy Bohmer c = (u8) *s++;
5723cd1385SRemy Bohmer if ((c & 0xc0) != 0x80)
5823cd1385SRemy Bohmer goto fail;
5923cd1385SRemy Bohmer c &= 0x3f;
6023cd1385SRemy Bohmer uchar |= c;
6123cd1385SRemy Bohmer
6223cd1385SRemy Bohmer /* no bogus surrogates */
6323cd1385SRemy Bohmer if (0xd800 <= uchar && uchar <= 0xdfff)
6423cd1385SRemy Bohmer goto fail;
6523cd1385SRemy Bohmer
666142e0aeSVitaly Kuzmichev /*
676142e0aeSVitaly Kuzmichev * 4-byte sequence (surrogate pairs, currently rare):
686142e0aeSVitaly Kuzmichev * 11101110wwwwzzzzyy + 110111yyyyxxxxxx
696142e0aeSVitaly Kuzmichev * = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
706142e0aeSVitaly Kuzmichev * (uuuuu = wwww + 1)
716142e0aeSVitaly Kuzmichev * FIXME accept the surrogate code points (only)
726142e0aeSVitaly Kuzmichev */
7323cd1385SRemy Bohmer } else
7423cd1385SRemy Bohmer goto fail;
7523cd1385SRemy Bohmer } else
7623cd1385SRemy Bohmer uchar = c;
7723cd1385SRemy Bohmer put_unaligned_le16(uchar, cp++);
7823cd1385SRemy Bohmer count++;
7923cd1385SRemy Bohmer len--;
8023cd1385SRemy Bohmer }
8123cd1385SRemy Bohmer return count;
8223cd1385SRemy Bohmer fail:
8323cd1385SRemy Bohmer return -1;
8423cd1385SRemy Bohmer }
8523cd1385SRemy Bohmer
8623cd1385SRemy Bohmer
8723cd1385SRemy Bohmer /**
8823cd1385SRemy Bohmer * usb_gadget_get_string - fill out a string descriptor
8923cd1385SRemy Bohmer * @table: of c strings encoded using UTF-8
9023cd1385SRemy Bohmer * @id: string id, from low byte of wValue in get string descriptor
9123cd1385SRemy Bohmer * @buf: at least 256 bytes
9223cd1385SRemy Bohmer *
9323cd1385SRemy Bohmer * Finds the UTF-8 string matching the ID, and converts it into a
9423cd1385SRemy Bohmer * string descriptor in utf16-le.
9523cd1385SRemy Bohmer * Returns length of descriptor (always even) or negative errno
9623cd1385SRemy Bohmer *
9723cd1385SRemy Bohmer * If your driver needs stings in multiple languages, you'll probably
9823cd1385SRemy Bohmer * "switch (wIndex) { ... }" in your ep0 string descriptor logic,
9923cd1385SRemy Bohmer * using this routine after choosing which set of UTF-8 strings to use.
10023cd1385SRemy Bohmer * Note that US-ASCII is a strict subset of UTF-8; any string bytes with
10123cd1385SRemy Bohmer * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
10223cd1385SRemy Bohmer * characters (which are also widely used in C strings).
10323cd1385SRemy Bohmer */
10423cd1385SRemy Bohmer int
usb_gadget_get_string(struct usb_gadget_strings * table,int id,u8 * buf)10523cd1385SRemy Bohmer usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf)
10623cd1385SRemy Bohmer {
10723cd1385SRemy Bohmer struct usb_string *s;
10823cd1385SRemy Bohmer int len;
10923cd1385SRemy Bohmer
11052d45012SRob Herring if (!table)
11152d45012SRob Herring return -EINVAL;
11252d45012SRob Herring
11323cd1385SRemy Bohmer /* descriptor 0 has the language id */
11423cd1385SRemy Bohmer if (id == 0) {
11523cd1385SRemy Bohmer buf[0] = 4;
11623cd1385SRemy Bohmer buf[1] = USB_DT_STRING;
11723cd1385SRemy Bohmer buf[2] = (u8) table->language;
11823cd1385SRemy Bohmer buf[3] = (u8) (table->language >> 8);
11923cd1385SRemy Bohmer return 4;
12023cd1385SRemy Bohmer }
12123cd1385SRemy Bohmer for (s = table->strings; s && s->s; s++)
12223cd1385SRemy Bohmer if (s->id == id)
12323cd1385SRemy Bohmer break;
12423cd1385SRemy Bohmer
12523cd1385SRemy Bohmer /* unrecognized: stall. */
12623cd1385SRemy Bohmer if (!s || !s->s)
12723cd1385SRemy Bohmer return -EINVAL;
12823cd1385SRemy Bohmer
12923cd1385SRemy Bohmer /* string descriptors have length, tag, then UTF16-LE text */
13023cd1385SRemy Bohmer len = min((size_t) 126, strlen(s->s));
13123cd1385SRemy Bohmer memset(buf + 2, 0, 2 * len); /* zero all the bytes */
13223cd1385SRemy Bohmer len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len);
13323cd1385SRemy Bohmer if (len < 0)
13423cd1385SRemy Bohmer return -EINVAL;
13523cd1385SRemy Bohmer buf[0] = (len + 1) * 2;
13623cd1385SRemy Bohmer buf[1] = USB_DT_STRING;
13723cd1385SRemy Bohmer return buf[0];
13823cd1385SRemy Bohmer }
139