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