1/* 2 * Copyright (C) 2003 David Brownell 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License as published 6 * by the Free Software Foundation; either version 2.1 of the License, or 7 * (at your option) any later version. 8 */ 9 10#include <linux/errno.h> 11#include <linux/kernel.h> 12#include <linux/list.h> 13#include <linux/string.h> 14#include <linux/device.h> 15#include <linux/init.h> 16 17#include <linux/usb/ch9.h> 18#include <linux/usb/gadget.h> 19 20#include <asm/unaligned.h> 21 22 23static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len) 24{ 25 int count = 0; 26 u8 c; 27 u16 uchar; 28 29 /* this insists on correct encodings, though not minimal ones. 30 * BUT it currently rejects legit 4-byte UTF-8 code points, 31 * which need surrogate pairs. (Unicode 3.1 can use them.) 32 */ 33 while (len != 0 && (c = (u8) *s++) != 0) { 34 if (unlikely(c & 0x80)) { 35 // 2-byte sequence: 36 // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx 37 if ((c & 0xe0) == 0xc0) { 38 uchar = (c & 0x1f) << 6; 39 40 c = (u8) *s++; 41 if ((c & 0xc0) != 0x80) 42 goto fail; 43 c &= 0x3f; 44 uchar |= c; 45 46 // 3-byte sequence (most CJKV characters): 47 // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx 48 } else if ((c & 0xf0) == 0xe0) { 49 uchar = (c & 0x0f) << 12; 50 51 c = (u8) *s++; 52 if ((c & 0xc0) != 0x80) 53 goto fail; 54 c &= 0x3f; 55 uchar |= c << 6; 56 57 c = (u8) *s++; 58 if ((c & 0xc0) != 0x80) 59 goto fail; 60 c &= 0x3f; 61 uchar |= c; 62 63 /* no bogus surrogates */ 64 if (0xd800 <= uchar && uchar <= 0xdfff) 65 goto fail; 66 67 // 4-byte sequence (surrogate pairs, currently rare): 68 // 11101110wwwwzzzzyy + 110111yyyyxxxxxx 69 // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 70 // (uuuuu = wwww + 1) 71 72 } else 73 goto fail; 74 } else 75 uchar = c; 76 put_unaligned_le16(uchar, cp++); 77 count++; 78 len--; 79 } 80 return count; 81fail: 82 return -1; 83} 84 85 86/** 87 * usb_gadget_get_string - fill out a string descriptor 88 * @table: of c strings encoded using UTF-8 89 * @id: string id, from low byte of wValue in get string descriptor 90 * @buf: at least 256 bytes 91 * 92 * Finds the UTF-8 string matching the ID, and converts it into a 93 * string descriptor in utf16-le. 94 * Returns length of descriptor (always even) or negative errno 95 * 96 * If your driver needs stings in multiple languages, you'll probably 97 * "switch (wIndex) { ... }" in your ep0 string descriptor logic, 98 * using this routine after choosing which set of UTF-8 strings to use. 99 * Note that US-ASCII is a strict subset of UTF-8; any string bytes with 100 * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 101 * characters (which are also widely used in C strings). 102 */ 103int 104usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) 105{ 106 struct usb_string *s; 107 int len; 108 109 /* descriptor 0 has the language id */ 110 if (id == 0) { 111 buf [0] = 4; 112 buf [1] = USB_DT_STRING; 113 buf [2] = (u8) table->language; 114 buf [3] = (u8) (table->language >> 8); 115 return 4; 116 } 117 for (s = table->strings; s && s->s; s++) 118 if (s->id == id) 119 break; 120 121 /* unrecognized: stall. */ 122 if (!s || !s->s) 123 return -EINVAL; 124 125 /* string descriptors have length, tag, then UTF16-LE text */ 126 len = min ((size_t) 126, strlen (s->s)); 127 memset (buf + 2, 0, 2 * len); /* zero all the bytes */ 128 len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len); 129 if (len < 0) 130 return -EINVAL; 131 buf [0] = (len + 1) * 2; 132 buf [1] = USB_DT_STRING; 133 return buf [0]; 134} 135