Joan and Sergio:

It looks like you're both suffering from the old "device numbers its 
interfaces starting with 1 instead of 0" problem.  It's a reasonably 
common error in device firmware.  See also

        http://bugme.osdl.org/show_bug.cgi?id=2107

The patch below ought to help.

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    Tue Dec 16 16:41:44 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,67 @@
 
                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;
+                       }
+
+                       /* Have we already encountered this interface? */
+                       for (i = n - 1; i >= 0; --i) {
+                               if (inums[i] == inum)
+                                       break;
+                       }
+                       if (i >= 0)
+                               ++nalts[i];
+                       else if (n >= nintf_orig) {
+                               warn("too many interfaces (> %d)", nintf_orig);
                                return -EINVAL;
+                       } else if (n < nintf) {
+                               inums[n] = inum;
+                               nalts[n] = 1;
+                               ++n;
                        }
-                       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 +350,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.165 vs edited =====
--- 1.165/include/linux/usb.h   Mon Dec  8 12:39:26 2003
+++ edited/include/linux/usb.h  Tue Dec 16 16:49:47 2003
@@ -74,8 +74,8 @@
  * struct usb_interface - what usb device drivers talk to
  * @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.
+ *     endpoint configurations, and they will be in numeric order:
+ *     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
@@ -110,10 +110,8 @@
  * will use them in non-default settings.
  */
 struct usb_interface {
-       /* array of alternate settings for this interface.
-        * these will be in numeric order, 0..num_altsettting
-        */
-       struct usb_host_interface *altsetting;
+       struct usb_host_interface *altsetting;  /* array of alternate */
+                       /* setting structures for this interface */
 
        unsigned act_altsetting;        /* active alternate setting */
        unsigned num_altsetting;        /* number of alternate settings */
@@ -150,8 +148,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
+        * *not* guaranteed to be in numeric order.  Even worse,
+        * 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];
 


-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED] 
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to