xref: /openbmc/u-boot/drivers/usb/gadget/usbstring.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
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