Commit 823bd343 authored by Oliver Neukum's avatar Oliver Neukum Committed by David S. Miller

cdc-ether: switch to common CDC parser

This patch uses the common parser to parse extra CDC
headers in order to reduce code duplication.
Signed-off-by: default avatarOliver Neukum <oneukum@suse.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 77b0a099
...@@ -112,8 +112,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) ...@@ -112,8 +112,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
int rndis; int rndis;
bool android_rndis_quirk = false; bool android_rndis_quirk = false;
struct usb_driver *driver = driver_of(intf); struct usb_driver *driver = driver_of(intf);
struct usb_cdc_mdlm_desc *desc = NULL; struct usb_cdc_parsed_header header;
struct usb_cdc_mdlm_detail_desc *detail = NULL;
if (sizeof(dev->data) < sizeof(*info)) if (sizeof(dev->data) < sizeof(*info))
return -EDOM; return -EDOM;
...@@ -155,156 +154,89 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) ...@@ -155,156 +154,89 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
info->control = intf; info->control = intf;
while (len > 3) {
if (buf[1] != USB_DT_CS_INTERFACE) cdc_parse_cdc_header(&header, intf, buf, len);
goto next_desc;
info->u = header.usb_cdc_union_desc;
/* use bDescriptorSubType to identify the CDC descriptors. info->header = header.usb_cdc_header_desc;
* We expect devices with CDC header and union descriptors. info->ether = header.usb_cdc_ether_desc;
* For CDC Ethernet we need the ethernet descriptor. /* we need a master/control interface (what we're
* For RNDIS, ignore two (pointless) CDC modem descriptors * probed with) and a slave/data interface; union
* in favor of a complicated OID-based RPC scheme doing what * descriptors sort this all out.
* CDC Ethernet achieves with a simple descriptor. */
*/ info->control = usb_ifnum_to_if(dev->udev,
switch (buf[2]) { info->u->bMasterInterface0);
case USB_CDC_HEADER_TYPE: info->data = usb_ifnum_to_if(dev->udev,
if (info->header) { info->u->bSlaveInterface0);
dev_dbg(&intf->dev, "extra CDC header\n"); if (!info->control || !info->data) {
goto bad_desc; dev_dbg(&intf->dev,
} "master #%u/%p slave #%u/%p\n",
info->header = (void *) buf; info->u->bMasterInterface0,
if (info->header->bLength != sizeof(*info->header)) { info->control,
dev_dbg(&intf->dev, "CDC header len %u\n", info->u->bSlaveInterface0,
info->header->bLength); info->data);
goto bad_desc; /* fall back to hard-wiring for RNDIS */
} if (rndis) {
break; android_rndis_quirk = true;
case USB_CDC_ACM_TYPE: goto skip;
/* paranoia: disambiguate a "real" vendor-specific
* modem interface from an RNDIS non-modem.
*/
if (rndis) {
struct usb_cdc_acm_descriptor *acm;
acm = (void *) buf;
if (acm->bmCapabilities) {
dev_dbg(&intf->dev,
"ACM capabilities %02x, "
"not really RNDIS?\n",
acm->bmCapabilities);
goto bad_desc;
}
}
break;
case USB_CDC_UNION_TYPE:
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)) {
dev_dbg(&intf->dev, "CDC union len %u\n",
info->u->bLength);
goto bad_desc;
}
/* 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);
/* fall back to hard-wiring for RNDIS */
if (rndis) {
android_rndis_quirk = true;
goto next_desc;
}
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;
}
/* some devices merge these - skip class check */
if (info->control == info->data)
goto next_desc;
/* a data interface altsetting does the real i/o */
d = &info->data->cur_altsetting->desc;
if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
dev_dbg(&intf->dev, "slave class %u\n",
d->bInterfaceClass);
goto bad_desc;
}
break;
case USB_CDC_ETHERNET_TYPE:
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)) {
dev_dbg(&intf->dev, "CDC ether len %u\n",
info->ether->bLength);
goto bad_desc;
}
dev->hard_mtu = le16_to_cpu(
info->ether->wMaxSegmentSize);
/* because of Zaurus, we may be ignoring the host
* side link address we were given.
*/
break;
case USB_CDC_MDLM_TYPE:
if (desc) {
dev_dbg(&intf->dev, "extra MDLM descriptor\n");
goto bad_desc;
}
desc = (void *)buf;
if (desc->bLength != sizeof(*desc))
goto bad_desc;
if (memcmp(&desc->bGUID, mbm_guid, 16))
goto bad_desc;
break;
case USB_CDC_MDLM_DETAIL_TYPE:
if (detail) {
dev_dbg(&intf->dev, "extra MDLM detail descriptor\n");
goto bad_desc;
}
detail = (void *)buf;
if (detail->bGuidDescriptorType == 0) {
if (detail->bLength < (sizeof(*detail) + 1))
goto bad_desc;
} else
goto bad_desc;
break;
} }
next_desc: goto bad_desc;
len -= buf[0]; /* bLength */ }
buf += buf[0]; 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;
}
/* some devices merge these - skip class check */
if (info->control == info->data)
goto skip;
/* a data interface altsetting does the real i/o */
d = &info->data->cur_altsetting->desc;
if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
dev_dbg(&intf->dev, "slave class %u\n",
d->bInterfaceClass);
goto bad_desc;
}
skip:
if ( rndis &&
header.usb_cdc_acm_descriptor &&
header.usb_cdc_acm_descriptor->bmCapabilities) {
dev_dbg(&intf->dev,
"ACM capabilities %02x, not really RNDIS?\n",
header.usb_cdc_acm_descriptor->bmCapabilities);
goto bad_desc;
} }
if (header.usb_cdc_ether_desc) {
dev->hard_mtu = le16_to_cpu(info->ether->wMaxSegmentSize);
/* because of Zaurus, we may be ignoring the host
* side link address we were given.
*/
}
if (header.usb_cdc_mdlm_desc &&
memcmp(header.usb_cdc_mdlm_desc->bGUID, mbm_guid, 16)) {
dev_dbg(&intf->dev, "GUID doesn't match\n");
goto bad_desc;
}
if (header.usb_cdc_mdlm_detail_desc &&
header.usb_cdc_mdlm_detail_desc->bLength <
(sizeof(struct usb_cdc_mdlm_detail_desc) + 1)) {
dev_dbg(&intf->dev, "Descriptor too short\n");
goto bad_desc;
}
/* Microsoft ActiveSync based and some regular RNDIS devices lack the /* Microsoft ActiveSync based and some regular RNDIS devices lack the
* CDC descriptors, so we'll hard-wire the interfaces and not check * CDC descriptors, so we'll hard-wire the interfaces and not check
* for descriptors. * for descriptors.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment