xref: /rk3399_rockchip-uboot/drivers/usb/gadget/usbstring.c (revision 1221ce459d04a428f8880f58581f671b736c3c27)
123cd1385SRemy Bohmer /*
223cd1385SRemy Bohmer  * Copyright (C) 2003 David Brownell
323cd1385SRemy Bohmer  *
4eee479cfSWolfgang Denk  * SPDX-License-Identifier:	LGPL-2.1+
523cd1385SRemy Bohmer  *
6a187559eSBin Meng  * 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>
11*1221ce45SMasahiro Yamada #include <linux/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 
utf8_to_utf16le(const char * s,__le16 * cp,unsigned len)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
usb_gadget_get_string(struct usb_gadget_strings * table,int id,u8 * buf)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 
11152d45012SRob Herring 	if (!table)
11252d45012SRob Herring 		return -EINVAL;
11352d45012SRob 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