]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/core/config.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[linux-2.6-omap-h63xx.git] / drivers / usb / core / config.c
index dd3482328ad2603e7726e10d07dd4f2dc09ffc58..cb69aa1e02e8762913e1d68901fb64495b68d8d0 100644 (file)
@@ -85,15 +85,21 @@ 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 */
+       /* Fix up bInterval values outside the legal range. Use 32 ms if no
+        * proper value can be guessed. */
        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 */
+                       /* Many device manufacturers are using full-speed
+                        * bInterval values in high-speed interrupt endpoint
+                        * descriptors. Try to fix those and fall back to a
+                        * 32 ms default value otherwise. */
+                       n = fls(d->bInterval*8);
+                       if (n == 0)
+                               n = 9;  /* 32 ms = 2^(9-1) uframes */
                        j = 16;
                        break;
                default:                /* USB_SPEED_FULL or _LOW */
@@ -124,6 +130,21 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
                endpoint->desc.bInterval = n;
        }
 
+       /* Some buggy low-speed devices have Bulk endpoints, which is
+        * explicitly forbidden by the USB spec.  In an attempt to make
+        * them usable, we will try treating them as Interrupt endpoints.
+        */
+       if (to_usb_device(ddev)->speed == USB_SPEED_LOW &&
+                       usb_endpoint_xfer_bulk(d)) {
+               dev_warn(ddev, "config %d interface %d altsetting %d "
+                   "endpoint 0x%X is Bulk; changing to Interrupt\n",
+                   cfgno, inum, asnum, d->bEndpointAddress);
+               endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;
+               endpoint->desc.bInterval = 1;
+               if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8)
+                       endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
+       }
+
        /* Skip over any Class Specific or Vendor Specific descriptors;
         * find the next endpoint or interface descriptor */
        endpoint->extra = buffer;
@@ -274,6 +295,7 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
        struct usb_descriptor_header *header;
        int len, retval;
        u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
+       unsigned iad_num = 0;
 
        memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
        if (config->desc.bDescriptorType != USB_DT_CONFIG ||
@@ -351,6 +373,20 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,
                                ++n;
                        }
 
+               } else if (header->bDescriptorType ==
+                               USB_DT_INTERFACE_ASSOCIATION) {
+                       if (iad_num == USB_MAXIADS) {
+                               dev_warn(ddev, "found more Interface "
+                                              "Association Descriptors "
+                                              "than allocated for in "
+                                              "configuration %d\n", cfgno);
+                       } else {
+                               config->intf_assoc[iad_num] =
+                                       (struct usb_interface_assoc_descriptor
+                                       *)header;
+                               iad_num++;
+                       }
+
                } else if (header->bDescriptorType == USB_DT_DEVICE ||
                            header->bDescriptorType == USB_DT_CONFIG)
                        dev_warn(ddev, "config %d contains an unexpected "