diff options
author | George Spelvin <linux@horizon.com> | 2009-08-24 22:06:41 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 06:46:37 -0700 |
commit | 392ca68b401e0797115a08836642faad5778fdb2 (patch) | |
tree | 8215eb8d605a85d28581fd0ef29f978617a8468d /drivers/usb | |
parent | 48d316770bd4dcf3d21b53cfa91e358280c31d69 (diff) |
USB: Clean up root hub string descriptors
The previous code had a bug that would add a trailing null byte to
the returned descriptor.
Signed-off-by: George Spelvin <linux@horizon.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hcd.c | 111 |
1 files changed, 64 insertions, 47 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 95ccfa0b9fc..34de475f016 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -337,72 +337,89 @@ static const u8 ss_rh_config_descriptor[] = { /*-------------------------------------------------------------------------*/ -/* - * helper routine for returning string descriptors in UTF-16LE - * input can actually be ISO-8859-1; ASCII is its 7-bit subset +/** + * ascii2desc() - Helper routine for producing UTF-16LE string descriptors + * @s: Null-terminated ASCII (actually ISO-8859-1) string + * @buf: Buffer for USB string descriptor (header + UTF-16LE) + * @len: Length (in bytes; may be odd) of descriptor buffer. + * + * The return value is the number of bytes filled in: 2 + 2*strlen(s) or + * buflen, whichever is less. + * + * USB String descriptors can contain at most 126 characters; input + * strings longer than that are truncated. */ -static unsigned ascii2utf(char *s, u8 *utf, int utfmax) +static unsigned +ascii2desc(char const *s, u8 *buf, unsigned len) { - unsigned retval; + unsigned n, t = 2 + 2*strlen(s); - for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { - *utf++ = *s++; - *utf++ = 0; - } - if (utfmax > 0) { - *utf = *s; - ++retval; + if (t > 254) + t = 254; /* Longest possible UTF string descriptor */ + if (len > t) + len = t; + + t += USB_DT_STRING << 8; /* Now t is first 16 bits to store */ + + n = len; + while (n--) { + *buf++ = t; + if (!n--) + break; + *buf++ = t >> 8; + t = (unsigned char)*s++; } - return retval; + return len; } -/* - * rh_string - provides manufacturer, product and serial strings for root hub - * @id: the string ID number (1: serial number, 2: product, 3: vendor) +/** + * rh_string() - provides string descriptors for root hub + * @id: the string ID number (0: langids, 1: serial #, 2: product, 3: vendor) * @hcd: the host controller for this root hub - * @data: return packet in UTF-16 LE - * @len: length of the return packet + * @data: buffer for output packet + * @len: length of the provided buffer * * Produces either a manufacturer, product or serial number string for the * virtual root hub device. + * Returns the number of bytes filled in: the length of the descriptor or + * of the provided buffer, whichever is less. */ -static unsigned rh_string(int id, struct usb_hcd *hcd, u8 *data, unsigned len) +static unsigned +rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len) { - char buf [100]; + char buf[100]; + char const *s; + static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04}; // language ids - if (id == 0) { - buf[0] = 4; buf[1] = 3; /* 4 bytes string data */ - buf[2] = 0x09; buf[3] = 0x04; /* MSFT-speak for "en-us" */ - len = min_t(unsigned, len, 4); - memcpy (data, buf, len); + switch (id) { + case 0: + /* Array of LANGID codes (0x0409 is MSFT-speak for "en-us") */ + /* See http://www.usb.org/developers/docs/USB_LANGIDs.pdf */ + if (len > 4) + len = 4; + memcpy(data, langids, len); return len; - - // serial number - } else if (id == 1) { - strlcpy (buf, hcd->self.bus_name, sizeof buf); - - // product description - } else if (id == 2) { - strlcpy (buf, hcd->product_desc, sizeof buf); - - // id 3 == vendor description - } else if (id == 3) { + case 1: + /* Serial number */ + s = hcd->self.bus_name; + break; + case 2: + /* Product name */ + s = hcd->product_desc; + break; + case 3: + /* Manufacturer */ snprintf (buf, sizeof buf, "%s %s %s", init_utsname()->sysname, init_utsname()->release, hcd->driver->description); - } - - switch (len) { /* All cases fall through */ + s = buf; + break; default: - len = 2 + ascii2utf (buf, data + 2, len - 2); - case 2: - data [1] = 3; /* type == string */ - case 1: - data [0] = 2 * (strlen (buf) + 1); - case 0: - ; /* Compiler wants a statement here */ + /* Can't happen; caller guarantees it */ + return 0; } - return len; + + return ascii2desc(s, data, len); } |