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