xref: /rk3399_rockchip-uboot/drivers/usb/gadget/usbstring.c (revision 6142e0ae0fcf8bf5a7a8d785061197ace8955cb6)
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