ChangeSet 1.1315.8.25, 2003/09/17 17:59:57-07:00, stern@rowland.harvard.edu [PATCH] USB: Changes to core/config.c (6 of 9) This patch removes the home-brewed resizeable arrays used to store altsetting structures, along with the now-unneeded max_altsetting field. Since we are already making a preliminary pass through all the descriptors to check their lengths, we take the opportunity to also count the number of altsetting descriptors for each interface. Then exactly the right number can be allocated all at once. This also moves the code that allocates the altsettings outside the usb_parse_interface() routine. Though not important now, this change will come in handy in the next patch. drivers/usb/core/config.c | 91 +++++++++++++++++++++++++++------------------- include/linux/usb.h | 2 - 2 files changed, 55 insertions(+), 38 deletions(-) diff -Nru a/drivers/usb/core/config.c b/drivers/usb/core/config.c --- a/drivers/usb/core/config.c Fri Sep 19 17:09:30 2003 +++ b/drivers/usb/core/config.c Fri Sep 19 17:09:30 2003 @@ -10,7 +10,6 @@ /* these maximums are arbitrary */ #define USB_MAXCONFIG 8 -#define USB_ALTSETTINGALLOC 4 #define USB_MAXINTERFACES 32 static int usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char *buffer, int size) @@ -108,43 +107,27 @@ struct usb_host_interface *ifp; unsigned char *begin; - interface->max_altsetting = USB_ALTSETTINGALLOC; - interface->altsetting = kmalloc(sizeof(*interface->altsetting) * interface->max_altsetting, - GFP_KERNEL); - if (!interface->altsetting) { - err("couldn't kmalloc interface->altsetting"); - return -ENOMEM; - } - - while (size > 0) { + ifp = interface->altsetting; + while (size >= sizeof(struct usb_descriptor_header)) { struct usb_interface_descriptor *d; - if (interface->num_altsetting >= interface->max_altsetting) { - struct usb_host_interface *ptr; - int oldmas; - - oldmas = interface->max_altsetting; - interface->max_altsetting += USB_ALTSETTINGALLOC; - if (interface->max_altsetting > USB_MAXALTSETTING) { - warn("too many alternate settings (incr %d max %d)\n", - USB_ALTSETTINGALLOC, USB_MAXALTSETTING); - return -EINVAL; - } + d = (struct usb_interface_descriptor *) buffer; + if (d->bAlternateSetting >= interface->num_altsetting) { - ptr = kmalloc(sizeof(*ptr) * interface->max_altsetting, GFP_KERNEL); - if (ptr == NULL) { - err("couldn't kmalloc interface->altsetting"); - return -ENOMEM; + /* Skip to the next interface descriptor */ + buffer += d->bLength; + size -= d->bLength; + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *) buffer; + + if (header->bDescriptorType == USB_DT_INTERFACE) + break; + buffer += header->bLength; + size -= header->bLength; } - memcpy(ptr, interface->altsetting, sizeof(*interface->altsetting) * oldmas); - kfree(interface->altsetting); - interface->altsetting = ptr; + continue; } - ifp = interface->altsetting + interface->num_altsetting; - memset(ifp, 0, sizeof(*ifp)); - interface->num_altsetting++; - memcpy(&ifp->desc, buffer, USB_DT_INTERFACE_SIZE); buffer += ifp->desc.bLength; @@ -216,6 +199,8 @@ || d->bDescriptorType != USB_DT_INTERFACE || !d->bAlternateSetting) break; + + ++ifp; } return buffer - buffer0; @@ -223,7 +208,7 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer) { - int nintf; + int nintf, nintf_orig; int i, j, size; struct usb_interface *interface; char *buffer2; @@ -237,7 +222,7 @@ le16_to_cpus(&config->desc.wTotalLength); size = config->desc.wTotalLength; - nintf = config->desc.bNumInterfaces; + nintf = nintf_orig = config->desc.bNumInterfaces; if (nintf > USB_MAXINTERFACES) { warn("too many interfaces (%d max %d)", nintf, USB_MAXINTERFACES); @@ -260,7 +245,8 @@ get_device(&interface->dev); } - /* Go through the descriptors, checking their length */ + /* Go through the descriptors, checking their length and counting the + * number of altsettings for each interface */ buffer2 = buffer; size2 = size; j = 0; @@ -272,10 +258,21 @@ } if (header->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *d; + 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) { + warn("invalid interface number (%d/%d)", + i, nintf_orig); + return -EINVAL; + } + if (i < nintf) + ++config->interface[i]->num_altsetting; } else if ((header->bDescriptorType == USB_DT_DEVICE || header->bDescriptorType == USB_DT_CONFIG) && j) { @@ -288,6 +285,28 @@ size2 -= header->bLength; } + /* Allocate the altsetting arrays */ + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + interface = config->interface[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); + return -EINVAL; + } + + len = sizeof(*interface->altsetting) * interface->num_altsetting; + interface->altsetting = kmalloc(len, GFP_KERNEL); + if (!interface->altsetting) { + err("couldn't kmalloc interface->altsetting"); + return -ENOMEM; + } + memset(interface->altsetting, 0, len); + } + buffer += config->desc.bLength; size -= config->desc.bLength; @@ -325,7 +344,7 @@ } /* Parse all the interface/altsetting descriptors */ - for (i = 0; i < nintf; i++) { + for (i = 0; i < nintf_orig; i++) { retval = usb_parse_interface(config->interface[i], buffer, size); if (retval < 0) return retval; diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Fri Sep 19 17:09:30 2003 +++ b/include/linux/usb.h Fri Sep 19 17:09:30 2003 @@ -80,7 +80,6 @@ * @act_altsetting: index of current altsetting. this number is always * less than num_altsetting. after the device is configured, each * interface uses its default setting of zero. - * @max_altsetting: the max number of altsettings for this interface. * @driver: the USB driver that is bound to this interface. * @minor: the minor number assigned to this interface, if this * interface is bound to a driver that uses the USB major number. @@ -118,7 +117,6 @@ unsigned act_altsetting; /* active alternate setting */ unsigned num_altsetting; /* number of alternate settings */ - unsigned max_altsetting; /* total memory allocated */ struct usb_driver *driver; /* driver */ int minor; /* minor number this interface is bound to */