summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/config.c89
-rw-r--r--include/linux/usb.h2
2 files changed, 54 insertions, 37 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 89112f2bde2f..4ce45e0091d7 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -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 @@ static int usb_parse_interface(struct usb_interface *interface, unsigned char *b
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;
+ d = (struct usb_interface_descriptor *) buffer;
+ if (d->bAlternateSetting >= interface->num_altsetting) {
- 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;
- }
+ /* 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;
- ptr = kmalloc(sizeof(*ptr) * interface->max_altsetting, GFP_KERNEL);
- if (ptr == NULL) {
- err("couldn't kmalloc interface->altsetting");
- return -ENOMEM;
+ 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 @@ static int usb_parse_interface(struct usb_interface *interface, unsigned char *b
|| d->bDescriptorType != USB_DT_INTERFACE
|| !d->bAlternateSetting)
break;
+
+ ++ifp;
}
return buffer - buffer0;
@@ -223,7 +208,7 @@ static int usb_parse_interface(struct usb_interface *interface, unsigned char *b
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 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer)
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 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer)
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 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer)
}
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 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer)
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 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer)
}
/* 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 --git a/include/linux/usb.h b/include/linux/usb.h
index a20b33d953aa..5575ebfb276d 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -80,7 +80,6 @@ struct usb_host_interface {
* @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 @@ struct usb_interface {
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 */