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
1 changed files with 79 additions and 147 deletions
|
@ -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,155 +154,88 @@ 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)
|
|
||||||
goto next_desc;
|
|
||||||
|
|
||||||
/* use bDescriptorSubType to identify the CDC descriptors.
|
cdc_parse_cdc_header(&header, intf, buf, len);
|
||||||
* 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;
|
info->u = header.usb_cdc_union_desc;
|
||||||
if (acm->bmCapabilities) {
|
info->header = header.usb_cdc_header_desc;
|
||||||
dev_dbg(&intf->dev,
|
info->ether = header.usb_cdc_ether_desc;
|
||||||
"ACM capabilities %02x, "
|
/* we need a master/control interface (what we're
|
||||||
"not really RNDIS?\n",
|
* probed with) and a slave/data interface; union
|
||||||
acm->bmCapabilities);
|
* descriptors sort this all out.
|
||||||
goto bad_desc;
|
*/
|
||||||
}
|
info->control = usb_ifnum_to_if(dev->udev,
|
||||||
}
|
info->u->bMasterInterface0);
|
||||||
break;
|
info->data = usb_ifnum_to_if(dev->udev,
|
||||||
case USB_CDC_UNION_TYPE:
|
info->u->bSlaveInterface0);
|
||||||
if (info->u) {
|
if (!info->control || !info->data) {
|
||||||
dev_dbg(&intf->dev, "extra CDC union\n");
|
dev_dbg(&intf->dev,
|
||||||
goto bad_desc;
|
"master #%u/%p slave #%u/%p\n",
|
||||||
}
|
info->u->bMasterInterface0,
|
||||||
info->u = (void *) buf;
|
info->control,
|
||||||
if (info->u->bLength != sizeof(*info->u)) {
|
info->u->bSlaveInterface0,
|
||||||
dev_dbg(&intf->dev, "CDC union len %u\n",
|
info->data);
|
||||||
info->u->bLength);
|
/* fall back to hard-wiring for RNDIS */
|
||||||
goto bad_desc;
|
if (rndis) {
|
||||||
}
|
android_rndis_quirk = true;
|
||||||
|
goto skip;
|
||||||
/* 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
|
||||||
|
|
Loading…
Add table
Reference in a new issue