This patch makes config.c more liberal about the sorts of interface 
numbering it will accept when reading interface descriptors.  The current 
code only allows interfaces numbered 0..(bNumInterfaces-1), but a number 
of devices erroneously number their interfaces starting at 1.

With this patch, the code will accept a total of bNumInterfaces with 
numbers in the interval 0..bNumInterfaces.  That will accomodate origin-1 
indexing (and a few other things beside).  Also included is a comment in 
include/linux/usb.h warning people that the interface numbers might not 
correspond to the locations in the usb_host_config->interface[] array and 
telling them they should use usb_ifnum_to_if().

Lauri, can you try this out and see if it helps with your prism2_usb?

Alan Stern


===== drivers/usb/core/config.c 1.28 vs edited =====
--- 1.28/drivers/usb/core/config.c      Fri Sep 26 12:37:44 2003
+++ edited/drivers/usb/core/config.c    Mon Oct 27 09:52:20 2003
@@ -8,9 +8,7 @@
 #define USB_MAXALTSETTING              128     /* Hard limit */
 #define USB_MAXENDPOINTS               30      /* Hard limit */
 
-/* these maximums are arbitrary */
-#define USB_MAXCONFIG                  8
-#define USB_MAXINTERFACES              32
+#define USB_MAXCONFIG                  8       /* Arbitrary limit */
 
 static int usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char 
*buffer, int size)
 {
@@ -90,7 +88,8 @@
        kfree(intf);
 }
 
-static int usb_parse_interface(struct usb_host_config *config, unsigned char *buffer, 
int size)
+static int usb_parse_interface(struct usb_host_config *config,
+               unsigned char *buffer, int size, u8 inums[])
 {
        unsigned char *buffer0 = buffer;
        struct usb_interface_descriptor *d;
@@ -109,8 +108,15 @@
                return -EINVAL;
        }
 
+       interface = NULL;
        inum = d->bInterfaceNumber;
-       if (inum >= config->desc.bNumInterfaces) {
+       for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+               if (inums[i] == inum) {
+                       interface = config->interface[i];
+                       break;
+               }
+       }
+       if (!interface) {
 
                /* Skip to the next interface descriptor */
                buffer += d->bLength;
@@ -126,7 +132,6 @@
                return buffer - buffer0;
        }
 
-       interface = config->interface[inum];
        asnum = d->bAlternateSetting;
        if (asnum >= interface->num_altsetting) {
                warn("invalid alternate setting %d for interface %d",
@@ -210,6 +215,8 @@
        int numskipped, len;
        char *begin;
        int retval;
+       int n;
+       u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
 
        memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
        if (config->desc.bDescriptorType != USB_DT_CONFIG ||
@@ -225,25 +232,14 @@
                    nintf, USB_MAXINTERFACES);
                config->desc.bNumInterfaces = nintf = USB_MAXINTERFACES;
        }
-
-       for (i = 0; i < nintf; ++i) {
-               interface = config->interface[i] =
-                   kmalloc(sizeof(struct usb_interface), GFP_KERNEL);
-               dbg("kmalloc IF %p, numif %i", interface, i);
-               if (!interface) {
-                       err("out of memory");
-                       return -ENOMEM;
-               }
-               memset(interface, 0, sizeof(struct usb_interface));
-               interface->dev.release = usb_release_intf;
-               device_initialize(&interface->dev);
-       }
+       if (nintf == 0)
+               warn("no interfaces?");
 
        /* Go through the descriptors, checking their length and counting the
         * number of altsettings for each interface */
+       n = 0;
        buffer2 = buffer;
        size2 = size;
-       j = 0;
        while (size2 >= sizeof(struct usb_descriptor_header)) {
                header = (struct usb_descriptor_header *) buffer2;
                if ((header->bLength > size2) || (header->bLength < 2)) {
@@ -253,42 +249,72 @@
 
                if (header->bDescriptorType == USB_DT_INTERFACE) {
                        struct usb_interface_descriptor *d;
+                       int inum;
 
                        if (header->bLength < USB_DT_INTERFACE_SIZE) {
                                warn("invalid interface descriptor");
                                return -EINVAL;
                        }
                        d = (struct usb_interface_descriptor *) header;
-                       i = d->bInterfaceNumber;
-                       if (i >= nintf_orig) {
+                       inum = d->bInterfaceNumber;
+                       if (inum > nintf_orig) {
                                warn("invalid interface number (%d/%d)",
-                                   i, nintf_orig);
+                                   inum, nintf_orig);
+                               return -EINVAL;
+                       }
+
+                       /* Insertion sort is slow but it works */
+                       for (i = n - 1; i >= 0; --i) {
+                               if (inums[i] < inum)
+                                       break;
+                       }
+                       if (++i < n && inums[i] == inum)
+                               ++nalts[i];
+                       else if (n >= nintf_orig) {
+                               warn("too many interfaces (> %d)", nintf_orig);
                                return -EINVAL;
+                       } else if (i < nintf) {
+                               if (n < nintf)
+                                       ++n;
+                               for (j = n-1; j > i; --j) {
+                                       inums[j] = inums[j-1];
+                                       nalts[j] = nalts[j-1];
+                               }
+                               inums[i] = inum;
+                               nalts[i] = 1;
                        }
-                       if (i < nintf)
-                               ++config->interface[i]->num_altsetting;
 
                } else if ((header->bDescriptorType == USB_DT_DEVICE ||
-                   header->bDescriptorType == USB_DT_CONFIG) && j) {
+                   header->bDescriptorType == USB_DT_CONFIG) && buffer2 > buffer) {
                        warn("unexpected descriptor type 0x%X", 
header->bDescriptorType);
                        return -EINVAL;
                }
 
-               j = 1;
                buffer2 += header->bLength;
                size2 -= header->bLength;
        }
+       if (n < nintf) {
+               warn("not enough interfaces (%d/%d)", n, nintf);
+               return -EINVAL;
+       }
 
-       /* Allocate the altsetting arrays */
-       for (i = 0; i < config->desc.bNumInterfaces; ++i) {
-               interface = config->interface[i];
+       /* Allocate the interfaces and altsetting arrays */
+       for (i = 0; i < nintf; ++i) {
+               interface = config->interface[i] =
+                   kmalloc(sizeof(struct usb_interface), GFP_KERNEL);
+               dbg("kmalloc IF %p, numif %i", interface, i);
+               if (!interface) {
+                       err("out of memory");
+                       return -ENOMEM;
+               }
+               memset(interface, 0, sizeof(struct usb_interface));
+               interface->dev.release = usb_release_intf;
+               device_initialize(&interface->dev);
+
+               interface->num_altsetting = nalts[i];
                if (interface->num_altsetting > USB_MAXALTSETTING) {
                        warn("too many alternate settings for interface %d (%d max 
%d)\n",
-                           i, interface->num_altsetting, USB_MAXALTSETTING);
-                       return -EINVAL;
-               }
-               if (interface->num_altsetting == 0) {
-                       warn("no alternate settings for interface %d", i);
+                           inums[i], interface->num_altsetting, USB_MAXALTSETTING);
                        return -EINVAL;
                }
 
@@ -329,7 +355,7 @@
 
        /* Parse all the interface/altsetting descriptors */
        while (size >= sizeof(struct usb_descriptor_header)) {
-               retval = usb_parse_interface(config, buffer, size);
+               retval = usb_parse_interface(config, buffer, size, inums);
                if (retval < 0)
                        return retval;
 
===== include/linux/usb.h 1.164 vs edited =====
--- 1.164/include/linux/usb.h   Mon Oct  6 13:46:13 2003
+++ edited/include/linux/usb.h  Mon Oct 27 10:02:27 2003
@@ -75,7 +75,7 @@
  * @altsetting: array of interface descriptors, one for each alternate
  *     setting that may be selected.  Each one includes a set of
  *     endpoint configurations and will be in numberic order,
- *     0..num_altsetting.
+ *     0..num_altsetting-1.
  * @num_altsetting: number of altsettings defined.
  * @act_altsetting: index of current altsetting.  this number is always
  *     less than num_altsetting.  after the device is configured, each
@@ -111,7 +111,7 @@
  */
 struct usb_interface {
        /* array of alternate settings for this interface.
-        * these will be in numeric order, 0..num_altsettting
+        * these will be in numeric order, 0..num_altsetting-1
         */
        struct usb_host_interface *altsetting;
 
@@ -150,8 +150,12 @@
 struct usb_host_config {
        struct usb_config_descriptor    desc;
 
-       /* the interfaces associated with this configuration
-        * these will be in numeric order, 0..desc.bNumInterfaces
+       /* The interfaces associated with this configuration.
+        * There are desc.bNumInterfaces of them, and they are
+        * in numeric order.  But some non-compliant devices
+        * number the interfaces starting with 1, not 0.  To be
+        * safe don't index this array directly; instead use
+        * usb_ifnum_to_if().
         */
        struct usb_interface *interface[USB_MAXINTERFACES];
 



-------------------------------------------------------
This SF.net email is sponsored by: The SF.net Donation Program.
Do you like what SourceForge.net is doing for the Open
Source Community?  Make a contribution, and help us add new
features and functionality. Click here: http://sourceforge.net/donate/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to