Re: Linux host controller string descriptors are odd-length

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Subject: USB: Clean up root hub string descriptors

The previous code had a bug in that it would add a trailing null byte to
the returned descriptor.
---

> Please feel free to submit a patch.

To who?  Here it is in patch form...
It compiles cleanly, but I haven't rebooted with it yet.

diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 95ccfa0..31081d0 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -337,72 +337,93 @@ 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
+ * @utf: Buffer for USB string descritpro (header + UTF-16LE)
+ * @utfmax: Length (in bytes; may be odd) of utf buffer.
+ *
+ * The return value is the number of bytes filled in: 2 + 2*strlen(s) or
+ * utfmax, whichever is less.
  */
-static unsigned ascii2utf(char *s, u8 *utf, int utfmax)
+static unsigned
+ascii2desc(char const *s, u8 *utf, unsigned utfmax)
 {
-	unsigned retval;
+	u8 * const utf0 = utf;
+	unsigned slen = strlen(s);
+
+	if (slen > 126)
+		slen = 126;     /* Limit so 2 + 2*slen <= 255 */
 
-	for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
+	if (!utfmax--)
+		goto end;
+	*utf++ = 2 + 2*slen;    /* Descriptor total length */
+
+	if (!utfmax--)
+		goto end;
+	*utf++ = 3;             /* Descriptor type 3 (string) */
+
+	if (utfmax > 2*slen)
+		utfmax = 2*slen;
+
+	while (utfmax--) {
 		*utf++ = *s++;
+		if (!utfmax--)
+			break;
 		*utf++ = 0;
 	}
-	if (utfmax > 0) {
-		*utf = *s;
-		++retval;
-	}
-	return retval;
+end:
+	return (unsigned)(utf - utf0);
 }
 
-/*
- * 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, 3, 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);
 }
 
 
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux