USB: Fix up bogus bInterval values in endpoint descriptors
authorAlan Stern <stern@rowland.harvard.edu>
Fri, 8 Jun 2007 19:23:27 +0000 (15:23 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 8 Jun 2007 23:24:31 +0000 (16:24 -0700)
This patch (as904) adds code to check for endpoint descriptor bInterval
values outside the legal limits.  Illegal values are set to 32 ms, which
seems like a reasonable default.

This fixes Bugzilla #8432.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/config.c

index 2d4fd53..dd34823 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/usb.h>
+#include <linux/usb/ch9.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -49,7 +50,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
        unsigned char *buffer0 = buffer;
        struct usb_endpoint_descriptor *d;
        struct usb_host_endpoint *endpoint;
-       int n, i;
+       int n, i, j;
 
        d = (struct usb_endpoint_descriptor *) buffer;
        buffer += d->bLength;
@@ -84,6 +85,45 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
        memcpy(&endpoint->desc, d, n);
        INIT_LIST_HEAD(&endpoint->urb_list);
 
+       /* If the bInterval value is outside the legal range,
+        * set it to a default value: 32 ms */
+       i = 0;          /* i = min, j = max, n = default */
+       j = 255;
+       if (usb_endpoint_xfer_int(d)) {
+               i = 1;
+               switch (to_usb_device(ddev)->speed) {
+               case USB_SPEED_HIGH:
+                       n = 9;          /* 32 ms = 2^(9-1) uframes */
+                       j = 16;
+                       break;
+               default:                /* USB_SPEED_FULL or _LOW */
+                       /* For low-speed, 10 ms is the official minimum.
+                        * But some "overclocked" devices might want faster
+                        * polling so we'll allow it. */
+                       n = 32;
+                       break;
+               }
+       } else if (usb_endpoint_xfer_isoc(d)) {
+               i = 1;
+               j = 16;
+               switch (to_usb_device(ddev)->speed) {
+               case USB_SPEED_HIGH:
+                       n = 9;          /* 32 ms = 2^(9-1) uframes */
+                       break;
+               default:                /* USB_SPEED_FULL */
+                       n = 6;          /* 32 ms = 2^(6-1) frames */
+                       break;
+               }
+       }
+       if (d->bInterval < i || d->bInterval > j) {
+               dev_warn(ddev, "config %d interface %d altsetting %d "
+                   "endpoint 0x%X has an invalid bInterval %d, "
+                   "changing to %d\n",
+                   cfgno, inum, asnum,
+                   d->bEndpointAddress, d->bInterval, n);
+               endpoint->desc.bInterval = n;
+       }
+
        /* Skip over any Class Specific or Vendor Specific descriptors;
         * find the next endpoint or interface descriptor */
        endpoint->extra = buffer;