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: Oliver Neukum <oneukum@suse.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
77b0a09967
commit
823bd34334
|
@ -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;
|
||||
|
@ -155,155 +154,88 @@ 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;
|
||||
cdc_parse_cdc_header(&header, intf, buf, len);
|
||||
|
||||
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;
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue