-#include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
-#define DEBUG
-#endif
-
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <asm/byteorder.h>
-
+#include "usb.h"
+#include "hcd.h"
#define USB_MAXALTSETTING 128 /* Hard limit */
#define USB_MAXENDPOINTS 30 /* Hard limit */
#define USB_MAXCONFIG 8 /* Arbitrary limit */
+static inline const char *plural(int n)
+{
+ return (n == 1 ? "" : "s");
+}
+
static int find_next_descriptor(unsigned char *buffer, int size,
int dt1, int dt2, int *num_skipped)
{
unsigned char *buffer0 = buffer;
/* Find the next descriptor of type dt1 or dt2 */
- while (size >= sizeof(struct usb_descriptor_header)) {
+ while (size > 0) {
h = (struct usb_descriptor_header *) buffer;
if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
break;
}
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
- int asnum, struct usb_host_endpoint *endpoint,
+ int asnum, struct usb_host_interface *ifp, int num_ep,
unsigned char *buffer, int size)
{
unsigned char *buffer0 = buffer;
- struct usb_descriptor_header *header;
+ struct usb_endpoint_descriptor *d;
+ struct usb_host_endpoint *endpoint;
int n, i;
- header = (struct usb_descriptor_header *)buffer;
- if (header->bDescriptorType != USB_DT_ENDPOINT) {
- dev_err(ddev, "config %d interface %d altsetting %d has an "
- "unexpected descriptor of type 0x%X, "
- "expecting endpoint type 0x%X\n",
- cfgno, inum, asnum,
- header->bDescriptorType, USB_DT_ENDPOINT);
- return -EINVAL;
- }
+ d = (struct usb_endpoint_descriptor *) buffer;
+ buffer += d->bLength;
+ size -= d->bLength;
- if (header->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
- memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_AUDIO_SIZE);
- else if (header->bLength >= USB_DT_ENDPOINT_SIZE)
- memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_SIZE);
+ if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
+ n = USB_DT_ENDPOINT_AUDIO_SIZE;
+ else if (d->bLength >= USB_DT_ENDPOINT_SIZE)
+ n = USB_DT_ENDPOINT_SIZE;
else {
- dev_err(ddev, "config %d interface %d altsetting %d has an "
- "invalid endpoint descriptor of length %d\n",
- cfgno, inum, asnum, header->bLength);
- return -EINVAL;
+ dev_warn(ddev, "config %d interface %d altsetting %d has an "
+ "invalid endpoint descriptor of length %d, skipping\n",
+ cfgno, inum, asnum, d->bLength);
+ goto skip_to_next_endpoint_or_interface_descriptor;
}
- i = endpoint->desc.bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;
+ i = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;
if (i >= 16 || i == 0) {
- dev_err(ddev, "config %d interface %d altsetting %d has an "
- "invalid endpoint with address 0x%X\n",
- cfgno, inum, asnum, endpoint->desc.bEndpointAddress);
- return -EINVAL;
+ dev_warn(ddev, "config %d interface %d altsetting %d has an "
+ "invalid endpoint with address 0x%X, skipping\n",
+ cfgno, inum, asnum, d->bEndpointAddress);
+ goto skip_to_next_endpoint_or_interface_descriptor;
}
- le16_to_cpus(&endpoint->desc.wMaxPacketSize);
+ /* Only store as many endpoints as we have room for */
+ if (ifp->desc.bNumEndpoints >= num_ep)
+ goto skip_to_next_endpoint_or_interface_descriptor;
+
+ endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
+ ++ifp->desc.bNumEndpoints;
- buffer += header->bLength;
- size -= header->bLength;
+ memcpy(&endpoint->desc, d, n);
+ INIT_LIST_HEAD(&endpoint->urb_list);
/* Skip over any Class Specific or Vendor Specific descriptors;
* find the next endpoint or interface descriptor */
USB_DT_INTERFACE, &n);
endpoint->extralen = i;
if (n > 0)
- dev_dbg(ddev, "skipped %d class/vendor specific endpoint "
- "descriptors\n", n);
+ dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
+ n, plural(n), "endpoint");
+ return buffer - buffer0 + i;
+
+skip_to_next_endpoint_or_interface_descriptor:
+ i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
+ USB_DT_INTERFACE, NULL);
return buffer - buffer0 + i;
}
-static void usb_free_intf(struct usb_interface *intf)
+void usb_release_interface_cache(struct kref *ref)
{
+ struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref);
int j;
- if (intf->altsetting) {
- for (j = 0; j < intf->num_altsetting; j++) {
- struct usb_host_interface *alt = &intf->altsetting[j];
+ for (j = 0; j < intfc->num_altsetting; j++) {
+ struct usb_host_interface *alt = &intfc->altsetting[j];
- kfree(alt->endpoint);
- }
- kfree(intf->altsetting);
+ kfree(alt->endpoint);
+ kfree(alt->string);
}
- kfree(intf);
+ kfree(intfc);
}
static int usb_parse_interface(struct device *ddev, int cfgno,
- struct usb_host_config *config, unsigned char *buffer, int size)
+ struct usb_host_config *config, unsigned char *buffer, int size,
+ u8 inums[], u8 nalts[])
{
unsigned char *buffer0 = buffer;
struct usb_interface_descriptor *d;
int inum, asnum;
- struct usb_interface *interface;
+ struct usb_interface_cache *intfc;
struct usb_host_interface *alt;
int i, n;
int len, retval;
+ int num_ep, num_ep_orig;
d = (struct usb_interface_descriptor *) buffer;
buffer += d->bLength;
size -= d->bLength;
- if (d->bDescriptorType != USB_DT_INTERFACE) {
- dev_err(ddev, "config %d has an unexpected descriptor of type "
- "0x%X, expecting interface type 0x%X\n",
- cfgno, d->bDescriptorType, USB_DT_INTERFACE);
- return -EINVAL;
- }
+ if (d->bLength < USB_DT_INTERFACE_SIZE)
+ goto skip_to_next_interface_descriptor;
+ /* Which interface entry is this? */
+ intfc = NULL;
inum = d->bInterfaceNumber;
- if (inum >= config->desc.bNumInterfaces)
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ if (inums[i] == inum) {
+ intfc = config->intf_cache[i];
+ break;
+ }
+ }
+ if (!intfc || intfc->num_altsetting >= nalts[i])
goto skip_to_next_interface_descriptor;
- interface = config->interface[inum];
+ /* Check for duplicate altsetting entries */
asnum = d->bAlternateSetting;
- if (asnum >= interface->num_altsetting) {
- dev_err(ddev, "config %d interface %d has an invalid "
- "alternate setting number: %d but max is %d\n",
- cfgno, inum, asnum, interface->num_altsetting - 1);
- return -EINVAL;
+ for ((i = 0, alt = &intfc->altsetting[0]);
+ i < intfc->num_altsetting;
+ (++i, ++alt)) {
+ if (alt->desc.bAlternateSetting == asnum) {
+ dev_warn(ddev, "Duplicate descriptor for config %d "
+ "interface %d altsetting %d, skipping\n",
+ cfgno, inum, asnum);
+ goto skip_to_next_interface_descriptor;
+ }
}
- alt = &interface->altsetting[asnum];
- if (alt->desc.bLength) {
- dev_err(ddev, "Duplicate descriptor for config %d "
- "interface %d altsetting %d\n", cfgno, inum, asnum);
- return -EINVAL;
- }
+ ++intfc->num_altsetting;
memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE);
/* Skip over any Class Specific or Vendor Specific descriptors;
USB_DT_INTERFACE, &n);
alt->extralen = i;
if (n > 0)
- dev_dbg(ddev, "skipped %d class/vendor specific "
- "interface descriptors\n", n);
+ dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
+ n, plural(n), "interface");
buffer += i;
size -= i;
- if (alt->desc.bNumEndpoints > USB_MAXENDPOINTS) {
- dev_err(ddev, "too many endpoints for config %d interface %d "
- "altsetting %d: %d, maximum allowed: %d\n",
- cfgno, inum, asnum, alt->desc.bNumEndpoints,
- USB_MAXENDPOINTS);
- return -EINVAL;
+ /* Allocate space for the right(?) number of endpoints */
+ num_ep = num_ep_orig = alt->desc.bNumEndpoints;
+ alt->desc.bNumEndpoints = 0; // Use as a counter
+ if (num_ep > USB_MAXENDPOINTS) {
+ dev_warn(ddev, "too many endpoints for config %d interface %d "
+ "altsetting %d: %d, using maximum allowed: %d\n",
+ cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS);
+ num_ep = USB_MAXENDPOINTS;
}
- len = alt->desc.bNumEndpoints * sizeof(struct usb_host_endpoint);
- alt->endpoint = kmalloc(len, GFP_KERNEL);
+ len = sizeof(struct usb_host_endpoint) * num_ep;
+ alt->endpoint = kzalloc(len, GFP_KERNEL);
if (!alt->endpoint)
return -ENOMEM;
- memset(alt->endpoint, 0, len);
- for (i = 0; i < alt->desc.bNumEndpoints; i++) {
- if (size < USB_DT_ENDPOINT_SIZE) {
- dev_err(ddev, "too few endpoint descriptors for "
- "config %d interface %d altsetting %d\n",
- cfgno, inum, asnum);
- return -EINVAL;
- }
-
- retval = usb_parse_endpoint(ddev, cfgno, inum, asnum,
- alt->endpoint + i, buffer, size);
+ /* Parse all the endpoint descriptors */
+ n = 0;
+ while (size > 0) {
+ if (((struct usb_descriptor_header *) buffer)->bDescriptorType
+ == USB_DT_INTERFACE)
+ break;
+ retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt,
+ num_ep, buffer, size);
if (retval < 0)
return retval;
+ ++n;
buffer += retval;
size -= retval;
}
+
+ if (n != num_ep_orig)
+ dev_warn(ddev, "config %d interface %d altsetting %d has %d "
+ "endpoint descriptor%s, different from the interface "
+ "descriptor's value: %d\n",
+ cfgno, inum, asnum, n, plural(n), num_ep_orig);
return buffer - buffer0;
skip_to_next_interface_descriptor:
return buffer - buffer0 + i;
}
-int usb_parse_configuration(struct device *ddev, int cfgidx,
+static int usb_parse_configuration(struct device *ddev, int cfgidx,
struct usb_host_config *config, unsigned char *buffer, int size)
{
+ unsigned char *buffer0 = buffer;
int cfgno;
int nintf, nintf_orig;
int i, j, n;
- struct usb_interface *interface;
+ struct usb_interface_cache *intfc;
unsigned char *buffer2;
int size2;
struct usb_descriptor_header *header;
int len, retval;
+ u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
config->desc.bDescriptorType, config->desc.bLength);
return -EINVAL;
}
- config->desc.wTotalLength = size;
cfgno = config->desc.bConfigurationValue;
buffer += config->desc.bLength;
dev_warn(ddev, "config %d has too many interfaces: %d, "
"using maximum allowed: %d\n",
cfgno, 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);
- if (!interface)
- return -ENOMEM;
- memset(interface, 0, sizeof(struct usb_interface));
+ nintf = USB_MAXINTERFACES;
}
/* Go through the descriptors, checking their length and counting the
* number of altsettings for each interface */
+ n = 0;
for ((buffer2 = buffer, size2 = size);
- size2 >= sizeof(struct usb_descriptor_header);
+ size2 > 0;
(buffer2 += header->bLength, size2 -= header->bLength)) {
+ if (size2 < sizeof(struct usb_descriptor_header)) {
+ dev_warn(ddev, "config %d descriptor has %d excess "
+ "byte%s, ignoring\n",
+ cfgno, size2, plural(size2));
+ break;
+ }
+
header = (struct usb_descriptor_header *) buffer2;
if ((header->bLength > size2) || (header->bLength < 2)) {
- dev_err(ddev, "config %d has an invalid descriptor "
- "of length %d\n", cfgno, header->bLength);
- return -EINVAL;
+ dev_warn(ddev, "config %d has an invalid descriptor "
+ "of length %d, skipping remainder of the config\n",
+ cfgno, header->bLength);
+ break;
}
if (header->bDescriptorType == USB_DT_INTERFACE) {
struct usb_interface_descriptor *d;
+ int inum;
d = (struct usb_interface_descriptor *) header;
if (d->bLength < USB_DT_INTERFACE_SIZE) {
- dev_err(ddev, "config %d has an invalid "
- "interface descriptor of length %d\n",
- cfgno, d->bLength);
- return -EINVAL;
+ dev_warn(ddev, "config %d has an invalid "
+ "interface descriptor of length %d, "
+ "skipping\n", cfgno, d->bLength);
+ continue;
}
- i = d->bInterfaceNumber;
- if (i >= nintf_orig) {
- dev_err(ddev, "config %d has an invalid "
+ inum = d->bInterfaceNumber;
+ if (inum >= nintf_orig)
+ dev_warn(ddev, "config %d has an invalid "
"interface number: %d but max is %d\n",
- cfgno, i, nintf_orig - 1);
- return -EINVAL;
+ cfgno, inum, nintf_orig - 1);
+
+ /* Have we already encountered this interface?
+ * Count its altsettings */
+ for (i = 0; i < n; ++i) {
+ if (inums[i] == inum)
+ break;
+ }
+ if (i < n) {
+ if (nalts[i] < 255)
+ ++nalts[i];
+ } else if (n < USB_MAXINTERFACES) {
+ 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) {
- dev_err(ddev, "config %d contains an unexpected "
- "descriptor of type 0x%X\n",
+ header->bDescriptorType == USB_DT_CONFIG)
+ dev_warn(ddev, "config %d contains an unexpected "
+ "descriptor of type 0x%X, skipping\n",
cfgno, header->bDescriptorType);
- return -EINVAL;
- }
} /* for ((buffer2 = buffer, size2 = size); ...) */
-
- /* Allocate the altsetting arrays */
+ size = buffer2 - buffer;
+ config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);
+
+ if (n != nintf)
+ dev_warn(ddev, "config %d has %d interface%s, different from "
+ "the descriptor's value: %d\n",
+ cfgno, n, plural(n), nintf_orig);
+ else if (n == 0)
+ dev_warn(ddev, "config %d has no interfaces?\n", cfgno);
+ config->desc.bNumInterfaces = nintf = n;
+
+ /* Check for missing interface numbers */
for (i = 0; i < nintf; ++i) {
- interface = config->interface[i];
- if (interface->num_altsetting > USB_MAXALTSETTING) {
- dev_err(ddev, "too many alternate settings for "
- "config %d interface %d: %d, "
- "maximum allowed: %d\n",
- cfgno, i, interface->num_altsetting,
- USB_MAXALTSETTING);
- return -EINVAL;
+ for (j = 0; j < nintf; ++j) {
+ if (inums[j] == i)
+ break;
}
- if (interface->num_altsetting == 0) {
- dev_err(ddev, "config %d has no interface number "
+ if (j >= nintf)
+ dev_warn(ddev, "config %d has no interface number "
"%d\n", cfgno, i);
- return -EINVAL;
+ }
+
+ /* Allocate the usb_interface_caches and altsetting arrays */
+ for (i = 0; i < nintf; ++i) {
+ j = nalts[i];
+ if (j > USB_MAXALTSETTING) {
+ dev_warn(ddev, "too many alternate settings for "
+ "config %d interface %d: %d, "
+ "using maximum allowed: %d\n",
+ cfgno, inums[i], j, USB_MAXALTSETTING);
+ nalts[i] = j = USB_MAXALTSETTING;
}
- len = sizeof(*interface->altsetting) *
- interface->num_altsetting;
- interface->altsetting = kmalloc(len, GFP_KERNEL);
- if (!interface->altsetting)
+ len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
+ config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL);
+ if (!intfc)
return -ENOMEM;
- memset(interface->altsetting, 0, len);
+ kref_init(&intfc->ref);
}
/* Skip over any Class Specific or Vendor Specific descriptors;
USB_DT_INTERFACE, &n);
config->extralen = i;
if (n > 0)
- dev_dbg(ddev, "skipped %d class/vendor specific "
- "configuration descriptors\n", n);
+ dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
+ n, plural(n), "configuration");
buffer += i;
size -= i;
/* Parse all the interface/altsetting descriptors */
- while (size >= sizeof(struct usb_descriptor_header)) {
+ while (size > 0) {
retval = usb_parse_interface(ddev, cfgno, config,
- buffer, size);
+ buffer, size, inums, nalts);
if (retval < 0)
return retval;
/* Check for missing altsettings */
for (i = 0; i < nintf; ++i) {
- interface = config->interface[i];
- for (j = 0; j < interface->num_altsetting; ++j) {
- if (!interface->altsetting[j].desc.bLength) {
- dev_err(ddev, "config %d interface %d has no "
- "altsetting %d\n", cfgno, i, j);
- return -EINVAL;
+ intfc = config->intf_cache[i];
+ for (j = 0; j < intfc->num_altsetting; ++j) {
+ for (n = 0; n < intfc->num_altsetting; ++n) {
+ if (intfc->altsetting[n].desc.
+ bAlternateSetting == j)
+ break;
}
+ if (n >= intfc->num_altsetting)
+ dev_warn(ddev, "config %d interface %d has no "
+ "altsetting %d\n", cfgno, inums[i], j);
}
}
- return size;
+ return 0;
}
// hub-only!! ... and only exported for reset/reinit path.
kfree(dev->rawdescriptors[i]);
kfree(dev->rawdescriptors);
- dev->rawdescriptors = 0;
+ dev->rawdescriptors = NULL;
}
for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
struct usb_host_config *cf = &dev->config[c];
+ kfree(cf->string);
for (i = 0; i < cf->desc.bNumInterfaces; i++) {
- struct usb_interface *ifp = cf->interface[i];
-
- if (ifp)
- usb_free_intf(ifp);
+ if (cf->intf_cache[i])
+ kref_put(&cf->intf_cache[i]->ref,
+ usb_release_interface_cache);
}
}
kfree(dev->config);
- dev->config = 0;
+ dev->config = NULL;
}
}
length = ncfg * sizeof(struct usb_host_config);
- dev->config = kmalloc(length, GFP_KERNEL);
+ dev->config = kzalloc(length, GFP_KERNEL);
if (!dev->config)
goto err2;
- memset(dev->config, 0, length);
length = ncfg * sizeof(char *);
- dev->rawdescriptors = kmalloc(length, GFP_KERNEL);
+ dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
if (!dev->rawdescriptors)
goto err2;
- memset(dev->rawdescriptors, 0, length);
- buffer = kmalloc(8, GFP_KERNEL);
+ buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
if (!buffer)
goto err2;
desc = (struct usb_config_descriptor *)buffer;
for (cfgno = 0; cfgno < ncfg; cfgno++) {
- /* We grab the first 8 bytes so we know how long the whole */
- /* configuration is */
+ /* We grab just the first descriptor so we know how long
+ * the whole configuration is */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
- buffer, 8);
+ buffer, USB_DT_CONFIG_SIZE);
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
- "descriptor\n", cfgno);
- goto err;
- } else if (result < 8) {
+ "descriptor/%s\n", cfgno, "start");
+ dev_err(ddev, "chopping to %d config(s)\n", cfgno);
+ dev->descriptor.bNumConfigurations = cfgno;
+ break;
+ } else if (result < 4) {
dev_err(ddev, "config index %d descriptor too short "
- "(expected %i, got %i)\n", cfgno, 8, result);
+ "(expected %i, got %i)\n", cfgno,
+ USB_DT_CONFIG_SIZE, result);
result = -EINVAL;
goto err;
}
bigbuffer, length);
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
- "descriptor\n", cfgno);
+ "descriptor/%s\n", cfgno, "all");
kfree(bigbuffer);
goto err;
}
if (result < length) {
- dev_err(ddev, "config index %d descriptor too short "
+ dev_warn(ddev, "config index %d descriptor too short "
"(expected %i, got %i)\n", cfgno, length, result);
- result = -EINVAL;
- kfree(bigbuffer);
- goto err;
+ length = result;
}
dev->rawdescriptors[cfgno] = bigbuffer;
result = usb_parse_configuration(&dev->dev, cfgno,
&dev->config[cfgno], bigbuffer, length);
- if (result > 0)
- dev_dbg(ddev, "config index %d descriptor has %d "
- "excess byte(s)\n", cfgno, result);
- else if (result < 0) {
+ if (result < 0) {
++cfgno;
goto err;
}