Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+81 −149
Original line number Diff line number Diff line
@@ -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,59 +154,12 @@ 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;
			}
	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.
@@ -226,7 +178,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
		/* fall back to hard-wiring for RNDIS */
		if (rndis) {
			android_rndis_quirk = true;
					goto next_desc;
			goto skip;
		}
		goto bad_desc;
	}
@@ -244,7 +196,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)

	/* some devices merge these - skip class check */
	if (info->control == info->data)
				goto next_desc;
		goto skip;

	/* a data interface altsetting does the real i/o */
	d = &info->data->cur_altsetting->desc;
@@ -253,57 +205,37 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
			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);
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;
	}
			dev->hard_mtu = le16_to_cpu(
						info->ether->wMaxSegmentSize);

	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.
		 */
			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))
	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 (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");
	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;
	}

			detail = (void *)buf;

			if (detail->bGuidDescriptorType == 0) {
				if (detail->bLength < (sizeof(*detail) + 1))
					goto bad_desc;
			} else
				goto bad_desc;
			break;
		}
next_desc:
		len -= buf[0];	/* bLength */
		buf += buf[0];
	}

	/* Microsoft ActiveSync based and some regular RNDIS devices lack the
	 * CDC descriptors, so we'll hard-wire the interfaces and not check