Skip to content

Commit

Permalink
cdc-ether: switch to common CDC parser
Browse files Browse the repository at this point in the history
This patch uses the common parser to parse extra CDC
headers in order to reduce code duplication.

Signed-off-by: Oliver Neukum <oneukum@suse.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Oliver Neukum authored and David S. Miller committed Sep 15, 2015
1 parent 77b0a09 commit 823bd34
Showing 1 changed file with 81 additions and 149 deletions.
230 changes: 81 additions & 149 deletions drivers/net/usb/cdc_ether.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
int rndis;
bool android_rndis_quirk = false;
struct usb_driver *driver = driver_of(intf);
struct usb_cdc_mdlm_desc *desc = NULL;
struct usb_cdc_mdlm_detail_desc *detail = NULL;
struct usb_cdc_parsed_header header;

if (sizeof(dev->data) < sizeof(*info))
return -EDOM;
Expand Down Expand Up @@ -155,156 +154,89 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)

memset(info, 0, sizeof(*info));
info->control = intf;
while (len > 3) {
if (buf[1] != USB_DT_CS_INTERFACE)
goto next_desc;

/* use bDescriptorSubType to identify the CDC descriptors.
* We expect devices with CDC header and union descriptors.
* For CDC Ethernet we need the ethernet descriptor.
* For RNDIS, ignore two (pointless) CDC modem descriptors
* in favor of a complicated OID-based RPC scheme doing what
* CDC Ethernet achieves with a simple descriptor.
*/
switch (buf[2]) {
case USB_CDC_HEADER_TYPE:
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)) {
dev_dbg(&intf->dev, "CDC header len %u\n",
info->header->bLength);
goto bad_desc;
}
break;
case USB_CDC_ACM_TYPE:
/* 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;

cdc_parse_cdc_header(&header, intf, buf, len);

info->u = header.usb_cdc_union_desc;
info->header = header.usb_cdc_header_desc;
info->ether = header.usb_cdc_ether_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 skip;
}
next_desc:
len -= buf[0]; /* bLength */
buf += buf[0];
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 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
* CDC descriptors, so we'll hard-wire the interfaces and not check
* for descriptors.
Expand Down

0 comments on commit 823bd34

Please sign in to comment.