- Uses the active config, not just the default one, if it's coping "descriptors in wrong place" quirk. (bugfix)
- Uses usb_ifnum_to_if() to get interfaces. (bugfix)
- AMBIT USB cable modems have bogus CDC Union descriptors; workaround by switching master and slave. (add quirk)
- To make it easier the next time we run into firmware that violates the class spec, add debug messages saying exactly why it's giving up on a given CDC device.
Net result, this code now handles at least one more cable modem design.
Please merge.
- Dave
--- 1.69/drivers/usb/net/usbnet.c Fri Aug 15 18:55:10 2003
+++ edited/drivers/usb/net/usbnet.c Mon Aug 25 08:17:27 2003
@@ -804,12 +773,15 @@
/* expect strict spec conformance for the descriptors, but
* cope with firmware which stores them in the wrong place
*/
- if (len == 0 && dev->udev->config->extralen) {
- /* Motorola SB4100 (and maybe others) put
- * CDC descriptors here
+ if (len == 0 && dev->udev->actconfig->extralen) {
+ /* Motorola SB4100 (and others: Brad Hards says it's
+ * from a Broadcom design) put CDC descriptors here
*/
- buf = dev->udev->config->extra;
- len = dev->udev->config->extralen;
+ buf = dev->udev->actconfig->extra;
+ len = dev->udev->actconfig->extralen;
+ if (len)
+ dev_dbg (&intf->dev,
+ "CDC descriptors on config\n");
}
memset (info, 0, sizeof *info);
@@ -824,48 +796,92 @@
*/
switch (buf [2]) {
case 0x00: /* Header, mostly useless */
- if (info->header)
+ if (info->header) {
+ dev_dbg (&intf->dev, "extra CDC header\n");
goto bad_desc;
+ }
info->header = (void *) buf;
- if (info->header->bLength != sizeof *info->header)
+ if (info->header->bLength != sizeof *info->header) {
+ dev_dbg (&intf->dev, "CDC header len %u\n",
+ info->header->bLength);
goto bad_desc;
+ }
break;
case 0x06: /* Union (groups interfaces) */
- if (info->u)
+ if (info->u) {
+ dev_dbg (&intf->dev, "extra CDC union\n");
goto bad_desc;
+ }
info->u = (void *) buf;
- if (info->u->bLength != sizeof *info->u)
+ if (info->u->bLength != sizeof *info->u) {
+ dev_dbg (&intf->dev, "CDC union len %u\n",
+ info->u->bLength);
goto bad_desc;
- d = &intf->altsetting->desc;
- if (info->u->bMasterInterface0 != d->bInterfaceNumber)
- goto bad_desc;
- info->data = dev->udev->actconfig->interface[0];
- if (intf != (info->data + info->u->bMasterInterface0))
+ }
+
+ /* we need a master/control interface (what we're
+ * probed with) and a slave/data interface; union
+ * descriptors sort this all out.
+ */
+ info->control = usb_ifnum_to_if(dev->udev,
+ info->u->bMasterInterface0);
+ info->data = usb_ifnum_to_if(dev->udev,
+ info->u->bSlaveInterface0);
+ if (!info->control || !info->data) {
+ dev_dbg (&intf->dev,
+ "master #%u/%p slave #%u/%p\n",
+ info->u->bMasterInterface0
+ info->control,
+ info->u->bSlaveInterface0,
+ info->data);
goto bad_desc;
+ }
+ if (info->control != intf) {
+ dev_dbg (&intf->dev, "bogus CDC Union\n");
+ /* Ambit USB Cable Modem (and maybe others)
+ * interchanges master and slave interface.
+ */
+ if (info->data == intf) {
+ info->data = info->control;
+ info->control = intf;
+ } else
+ goto bad_desc;
+ }
/* a data interface altsetting does the real i/o */
- info->data += info->u->bSlaveInterface0;
d = &info->data->altsetting->desc;
- if (info->u->bSlaveInterface0 != d->bInterfaceNumber
- || d->bInterfaceClass != USB_CLASS_CDC_DATA)
+ if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
+ dev_dbg (&intf->dev, "slave class %u\n",
+ d->bInterfaceClass);
goto bad_desc;
+ }
if (usb_interface_claimed (info->data))
return -EBUSY;
break;
case 0x0F: /* Ethernet Networking */
- if (info->ether)
+ if (info->ether) {
+ dev_dbg (&intf->dev, "extra CDC ether\n");
goto bad_desc;
+ }
info->ether = (void *) buf;
- if (info->ether->bLength != sizeof *info->ether)
+ if (info->ether->bLength != sizeof *info->ether) {
+ dev_dbg (&intf->dev, "CDC ether len %u\n",
+ info->u->bLength);
goto bad_desc;
+ }
break;
}
next_desc:
len -= buf [0]; /* bLength */
buf += buf [0];
}
- if (!info->header || !info ->u || !info->ether)
+ if (!info->header || !info ->u || !info->ether) {
+ dev_dbg (&intf->dev, "missing cdc %s%s%sdescriptor\n",
+ info->header ? "" : "header ",
+ info->u ? "" : "union ",
+ info->ether ? "" : "ether ");
goto bad_desc;
+ }
#ifdef CONFIG_USB_ZAURUS
/* Zaurus ethernet addresses aren't unique ... */
