The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Oliver Neukum <oneukum@suse.com>
To: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org,
	linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
	manuelebner@mailbox.org
Cc: Oliver Neukum <oneukum@suse.com>
Subject: [PATCHv2 net-next 5/7] net: usb: move exported symbols from cdc_ether to usbnet
Date: Thu,  9 Jul 2026 14:01:14 +0200	[thread overview]
Message-ID: <20260709120116.1423033-5-oneukum@suse.com> (raw)
In-Reply-To: <20260709120116.1423033-1-oneukum@suse.com>

Move the rest of the symbols to usbnet, so that the cdc_ether
driver does not need to be loaded as a library.

Signed-off-by: Oliver Neukum <oneukum@suse.com>
---
 drivers/net/usb/cdc_ether.c | 372 ------------------------------------
 drivers/net/usb/usbnet.c    | 356 ++++++++++++++++++++++++++++++++++
 include/linux/usb/usbnet.h  |  17 ++
 3 files changed, 373 insertions(+), 372 deletions(-)

diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 76ad4ffa950a..6bf6538f3468 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -19,378 +19,6 @@
 #include <linux/usb/usbnet.h>
 
 
-#if IS_ENABLED(CONFIG_USB_NET_RNDIS_HOST)
-
-static int is_rndis(struct usb_interface_descriptor *desc)
-{
-	return (desc->bInterfaceClass == USB_CLASS_COMM &&
-		desc->bInterfaceSubClass == 2 &&
-		desc->bInterfaceProtocol == 0xff);
-}
-
-static int is_activesync(struct usb_interface_descriptor *desc)
-{
-	return (desc->bInterfaceClass == USB_CLASS_MISC &&
-		desc->bInterfaceSubClass == 1 &&
-		desc->bInterfaceProtocol == 1);
-}
-
-static int is_wireless_rndis(struct usb_interface_descriptor *desc)
-{
-	return (desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER &&
-		desc->bInterfaceSubClass == 1 &&
-		desc->bInterfaceProtocol == 3);
-}
-
-static int is_novatel_rndis(struct usb_interface_descriptor *desc)
-{
-	return (desc->bInterfaceClass == USB_CLASS_MISC &&
-		desc->bInterfaceSubClass == 4 &&
-		desc->bInterfaceProtocol == 1);
-}
-
-#else
-
-#define is_rndis(desc)		0
-#define is_activesync(desc)	0
-#define is_wireless_rndis(desc)	0
-#define is_novatel_rndis(desc)	0
-
-#endif
-
-static const u8 mbm_guid[16] = {
-	0xa3, 0x17, 0xa8, 0x8b, 0x04, 0x5e, 0x4f, 0x01,
-	0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a,
-};
-
-/* We need to override usbnet_*_link_ksettings in bind() */
-static const struct ethtool_ops cdc_ether_ethtool_ops = {
-	.get_link		= usbnet_get_link,
-	.nway_reset		= usbnet_nway_reset,
-	.get_drvinfo		= usbnet_get_drvinfo,
-	.get_msglevel		= usbnet_get_msglevel,
-	.set_msglevel		= usbnet_set_msglevel,
-	.get_ts_info		= ethtool_op_get_ts_info,
-	.get_link_ksettings	= usbnet_get_link_ksettings_internal,
-	.set_link_ksettings	= NULL,
-};
-
-/* probes control interface, claims data interface, collects the bulk
- * endpoints, activates data interface (if needed), maybe sets MTU.
- * all pure cdc, except for certain firmware workarounds, and knowing
- * that rndis uses one different rule.
- */
-int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
-{
-	u8				*buf = intf->cur_altsetting->extra;
-	int				len = intf->cur_altsetting->extralen;
-	struct usb_interface_descriptor	*d;
-	struct cdc_state		*info = &dev->cdc;
-	int				status = -ENODEV;
-	int				rndis;
-	bool				android_rndis_quirk = false;
-	struct usb_driver		*driver = driver_of(intf);
-	struct usb_cdc_parsed_header header;
-
-	if (sizeof(dev->data) < sizeof(*info))
-		return -EDOM;
-
-	/* expect strict spec conformance for the descriptors, but
-	 * cope with firmware which stores them in the wrong place
-	 */
-	if (len == 0 && dev->udev->actconfig->extralen) {
-		/* Motorola SB4100 (and others: Brad Hards says it's
-		 * from a Broadcom design) put CDC descriptors here
-		 */
-		buf = dev->udev->actconfig->extra;
-		len = dev->udev->actconfig->extralen;
-		dev_dbg(&intf->dev, "CDC descriptors on config\n");
-	}
-
-	/* Maybe CDC descriptors are after the endpoint?  This bug has
-	 * been seen on some 2Wire Inc RNDIS-ish products.
-	 */
-	if (len == 0) {
-		struct usb_host_endpoint	*hep;
-
-		hep = intf->cur_altsetting->endpoint;
-		if (hep) {
-			buf = hep->extra;
-			len = hep->extralen;
-		}
-		if (len)
-			dev_dbg(&intf->dev,
-				"CDC descriptors on endpoint\n");
-	}
-
-	/* this assumes that if there's a non-RNDIS vendor variant
-	 * of cdc-acm, it'll fail RNDIS requests cleanly.
-	 */
-	rndis = (is_rndis(&intf->cur_altsetting->desc) ||
-		 is_activesync(&intf->cur_altsetting->desc) ||
-		 is_wireless_rndis(&intf->cur_altsetting->desc) ||
-		 is_novatel_rndis(&intf->cur_altsetting->desc));
-
-	memset(info, 0, sizeof(*info));
-	info->control = intf;
-
-	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;
-	if (!info->u) {
-		if (rndis) {
-			goto skip;
-		} else {
-			/* in that case a quirk is mandatory */
-			dev_err(&dev->udev->dev, "No union descriptors\n");
-			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 skip;
-		}
-		dev_err(&intf->dev, "bad CDC descriptors\n");
-		goto bad_desc;
-	}
-	if (info->control != intf) {
-		/* Ambit USB Cable Modem (and maybe others)
-		 * interchanges master and slave interface.
-		 */
-		if (info->data == intf) {
-			info->data = info->control;
-			info->control = intf;
-		} else {
-			dev_err(&intf->dev, "bogus CDC Union\n");
-			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_err(&intf->dev, "slave class %u\n", d->bInterfaceClass);
-		goto bad_desc;
-	}
-skip:
-	/* Communication class functions with bmCapabilities are not
-	 * RNDIS.  But some Wireless class RNDIS functions use
-	 * bmCapabilities for their own purpose. The failsafe is
-	 * therefore applied only to Communication class RNDIS
-	 * functions.  The rndis test is redundant, but a cheap
-	 * optimization.
-	 */
-	if (rndis && is_rndis(&intf->cur_altsetting->desc) &&
-	    header.usb_cdc_acm_descriptor &&
-	    header.usb_cdc_acm_descriptor->bmCapabilities) {
-		dev_err(&intf->dev,
-			"ACM capabilities %02x, not really RNDIS?\n",
-			header.usb_cdc_acm_descriptor->bmCapabilities);
-		goto bad_desc;
-	}
-
-	if (header.usb_cdc_ether_desc && info->ether->wMaxSegmentSize) {
-		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_err(&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_err(&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.
-	 *
-	 * Some Android RNDIS devices have a CDC Union descriptor pointing
-	 * to non-existing interfaces.  Ignore that and attempt the same
-	 * hard-wired 0 and 1 interfaces.
-	 */
-	if (rndis && (!info->u || android_rndis_quirk)) {
-		info->control = usb_ifnum_to_if(dev->udev, 0);
-		info->data = usb_ifnum_to_if(dev->udev, 1);
-		if (!info->control || !info->data || info->control != intf) {
-			dev_err(&intf->dev,
-				"rndis: master #0/%p slave #1/%p\n",
-				info->control,
-				info->data);
-			goto bad_desc;
-		}
-
-	} else if (!info->header || (!rndis && !info->ether)) {
-		dev_err(&intf->dev, "missing cdc %s%s%sdescriptor\n",
-			info->header ? "" : "header ",
-			info->u ? "" : "union ",
-			info->ether ? "" : "ether ");
-		goto bad_desc;
-	}
-
-	/* claim data interface and set it up ... with side effects.
-	 * network traffic can't flow until an altsetting is enabled.
-	 */
-	if (info->data != info->control) {
-		status = usb_driver_claim_interface(driver, info->data, dev);
-		if (status < 0) {
-			dev_err(&intf->dev, "Second interface unclaimable\n");
-			goto bad_desc;
-		}
-	}
-	status = usbnet_get_endpoints(dev, info->data);
-	if (status < 0) {
-		dev_dbg(&intf->dev, "Mandatory endpoints missing\n");
-		goto bail_out_and_release;
-	}
-
-	/* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
-	if (info->data != info->control)
-		dev->status = NULL;
-	if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
-		struct usb_endpoint_descriptor	*desc;
-
-		dev->status = &info->control->cur_altsetting->endpoint[0];
-		desc = &dev->status->desc;
-		if (!usb_endpoint_is_int_in(desc) ||
-		    (le16_to_cpu(desc->wMaxPacketSize)
-		     < sizeof(struct usb_cdc_notification)) ||
-		    !desc->bInterval) {
-			dev_dbg(&intf->dev, "bad notification endpoint\n");
-			dev->status = NULL;
-		}
-	}
-	if (rndis && !dev->status) {
-		dev_err(&intf->dev, "missing RNDIS status endpoint\n");
-		status = -ENODEV;
-		goto bail_out_and_release;
-	}
-
-	/* override ethtool_ops */
-	dev->net->ethtool_ops = &cdc_ether_ethtool_ops;
-
-	return 0;
-
-bail_out_and_release:
-	usb_set_intfdata(info->data, NULL);
-	if (info->data != info->control)
-		usb_driver_release_interface(driver, info->data);
-bad_desc:
-	return status;
-}
-EXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind);
-
-
-/* like usbnet_generic_cdc_bind() but handles filter initialization
- * correctly
- */
-int usbnet_ether_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
-{
-	int rv;
-
-	rv = usbnet_generic_cdc_bind(dev, intf);
-	if (rv < 0)
-		goto bail_out;
-
-	/* Some devices don't initialise properly. In particular
-	 * the packet filter is not reset. There are devices that
-	 * don't do reset all the way. So the packet filter should
-	 * be set to a sane initial value.
-	 */
-	usbnet_cdc_update_filter(dev);
-
-bail_out:
-	return rv;
-}
-EXPORT_SYMBOL_GPL(usbnet_ether_cdc_bind);
-
-void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
-{
-	struct cdc_state		*info = &dev->cdc;
-	struct usb_driver		*driver = driver_of(intf);
-
-	/* combined interface - nothing  to do */
-	if (info->data == info->control)
-		return;
-
-	/* disconnect master --> disconnect slave */
-	if (intf == info->control && info->data) {
-		/* ensure immediate exit from usbnet_disconnect */
-		usb_set_intfdata(info->data, NULL);
-		usb_driver_release_interface(driver, info->data);
-		info->data = NULL;
-	}
-
-	/* and vice versa (just in case) */
-	else if (intf == info->data && info->control) {
-		/* ensure immediate exit from usbnet_disconnect */
-		usb_set_intfdata(info->control, NULL);
-		usb_driver_release_interface(driver, info->control);
-		info->control = NULL;
-	}
-}
-EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
-
-/* Communications Device Class, Ethernet Control model
- *
- * Takes two interfaces.  The DATA interface is inactive till an altsetting
- * is selected.  Configuration data includes class descriptors.  There's
- * an optional status endpoint on the control interface.
- *
- * This should interop with whatever the 2.4 "CDCEther.c" driver
- * (by Brad Hards) talked with, with more functionality.
- */
-
-int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
-{
-	int				status;
-	struct cdc_state		*info = &dev->cdc;
-
-	status = usbnet_ether_cdc_bind(dev, intf);
-	if (status < 0)
-		return status;
-
-	status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress);
-	if (status < 0) {
-		usb_set_intfdata(info->data, NULL);
-		usb_driver_release_interface(driver_of(intf), info->data);
-		return status;
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(usbnet_cdc_bind);
-
 static int usbnet_cdc_zte_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	int status = usbnet_cdc_bind(dev, intf);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 14e9f1b1e0a2..ce932c81382e 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -2369,6 +2369,362 @@ int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 	return 1;
 }
 EXPORT_SYMBOL_GPL(usbnet_cdc_zte_rx_fixup);
+
+
+#if IS_ENABLED(CONFIG_USB_NET_RNDIS_HOST)
+
+static int is_rndis(struct usb_interface_descriptor *desc)
+{
+	return (desc->bInterfaceClass == USB_CLASS_COMM &&
+		desc->bInterfaceSubClass == 2 &&
+		desc->bInterfaceProtocol == 0xff);
+}
+
+static int is_activesync(struct usb_interface_descriptor *desc)
+{
+	return (desc->bInterfaceClass == USB_CLASS_MISC &&
+		desc->bInterfaceSubClass == 1 &&
+		desc->bInterfaceProtocol == 1);
+}
+
+static int is_wireless_rndis(struct usb_interface_descriptor *desc)
+{
+	return (desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER &&
+		desc->bInterfaceSubClass == 1 &&
+		desc->bInterfaceProtocol == 3);
+}
+
+static int is_novatel_rndis(struct usb_interface_descriptor *desc)
+{
+	return (desc->bInterfaceClass == USB_CLASS_MISC &&
+		desc->bInterfaceSubClass == 4 &&
+		desc->bInterfaceProtocol == 1);
+}
+
+#else
+
+#define is_rndis(desc)		0
+#define is_activesync(desc)	0
+#define is_wireless_rndis(desc)	0
+#define is_novatel_rndis(desc)	0
+
+#endif
+
+/* Communications Device Class, Ethernet Control model
+ *
+ * Takes two interfaces.  The DATA interface is inactive till an altsetting
+ * is selected.  Configuration data includes class descriptors.  There's
+ * an optional status endpoint on the control interface.
+ *
+ * This should interop with whatever the 2.4 "CDCEther.c" driver
+ * (by Brad Hards) talked with, with more functionality.
+ */
+
+int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int				status;
+	struct cdc_state		*info = &dev->cdc;
+
+	status = usbnet_ether_cdc_bind(dev, intf);
+	if (status < 0)
+		return status;
+
+	status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress);
+	if (status < 0) {
+		usb_set_intfdata(info->data, NULL);
+		usb_driver_release_interface(driver_of(intf), info->data);
+		return status;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usbnet_cdc_bind);
+
+/* probes control interface, claims data interface, collects the bulk
+ * endpoints, activates data interface (if needed), maybe sets MTU.
+ * all pure cdc, except for certain firmware workarounds, and knowing
+ * that rndis uses one different rule.
+ */
+int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u8				*buf = intf->cur_altsetting->extra;
+	int				len = intf->cur_altsetting->extralen;
+	struct usb_interface_descriptor	*d;
+	struct cdc_state		*info = &dev->cdc;
+	int				status = -ENODEV;
+	int				rndis;
+	bool				android_rndis_quirk = false;
+	struct usb_driver		*driver = driver_of(intf);
+	struct usb_cdc_parsed_header header;
+
+	if (sizeof(dev->data) < sizeof(*info))
+		return -EDOM;
+
+	/* expect strict spec conformance for the descriptors, but
+	 * cope with firmware which stores them in the wrong place
+	 */
+	if (len == 0 && dev->udev->actconfig->extralen) {
+		/* Motorola SB4100 (and others: Brad Hards says it's
+		 * from a Broadcom design) put CDC descriptors here
+		 */
+		buf = dev->udev->actconfig->extra;
+		len = dev->udev->actconfig->extralen;
+		dev_dbg(&intf->dev, "CDC descriptors on config\n");
+	}
+
+	/* Maybe CDC descriptors are after the endpoint?  This bug has
+	 * been seen on some 2Wire Inc RNDIS-ish products.
+	 */
+	if (len == 0) {
+		struct usb_host_endpoint	*hep;
+
+		hep = intf->cur_altsetting->endpoint;
+		if (hep) {
+			buf = hep->extra;
+			len = hep->extralen;
+		}
+		if (len)
+			dev_dbg(&intf->dev,
+				"CDC descriptors on endpoint\n");
+	}
+
+	/* this assumes that if there's a non-RNDIS vendor variant
+	 * of cdc-acm, it'll fail RNDIS requests cleanly.
+	 */
+	rndis = (is_rndis(&intf->cur_altsetting->desc) ||
+		 is_activesync(&intf->cur_altsetting->desc) ||
+		 is_wireless_rndis(&intf->cur_altsetting->desc) ||
+		 is_novatel_rndis(&intf->cur_altsetting->desc));
+
+	memset(info, 0, sizeof(*info));
+	info->control = intf;
+
+	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;
+	if (!info->u) {
+		if (rndis) {
+			goto skip;
+		} else {
+			/* in that case a quirk is mandatory */
+			dev_err(&dev->udev->dev, "No union descriptors\n");
+			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 skip;
+		}
+		dev_err(&intf->dev, "bad CDC descriptors\n");
+		goto bad_desc;
+	}
+	if (info->control != intf) {
+		/* Ambit USB Cable Modem (and maybe others)
+		 * interchanges master and slave interface.
+		 */
+		if (info->data == intf) {
+			info->data = info->control;
+			info->control = intf;
+		} else {
+			dev_err(&intf->dev, "bogus CDC Union\n");
+			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_err(&intf->dev, "slave class %u\n", d->bInterfaceClass);
+		goto bad_desc;
+	}
+skip:
+	/* Communication class functions with bmCapabilities are not
+	 * RNDIS.  But some Wireless class RNDIS functions use
+	 * bmCapabilities for their own purpose. The failsafe is
+	 * therefore applied only to Communication class RNDIS
+	 * functions.  The rndis test is redundant, but a cheap
+	 * optimization.
+	 */
+	if (rndis && is_rndis(&intf->cur_altsetting->desc) &&
+	    header.usb_cdc_acm_descriptor &&
+	    header.usb_cdc_acm_descriptor->bmCapabilities) {
+		dev_err(&intf->dev,
+			"ACM capabilities %02x, not really RNDIS?\n",
+			header.usb_cdc_acm_descriptor->bmCapabilities);
+		goto bad_desc;
+	}
+
+	if (header.usb_cdc_ether_desc && info->ether->wMaxSegmentSize) {
+		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_err(&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_err(&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.
+	 *
+	 * Some Android RNDIS devices have a CDC Union descriptor pointing
+	 * to non-existing interfaces.  Ignore that and attempt the same
+	 * hard-wired 0 and 1 interfaces.
+	 */
+	if (rndis && (!info->u || android_rndis_quirk)) {
+		info->control = usb_ifnum_to_if(dev->udev, 0);
+		info->data = usb_ifnum_to_if(dev->udev, 1);
+		if (!info->control || !info->data || info->control != intf) {
+			dev_err(&intf->dev,
+				"rndis: master #0/%p slave #1/%p\n",
+				info->control,
+				info->data);
+			goto bad_desc;
+		}
+
+	} else if (!info->header || (!rndis && !info->ether)) {
+		dev_err(&intf->dev, "missing cdc %s%s%sdescriptor\n",
+			info->header ? "" : "header ",
+			info->u ? "" : "union ",
+			info->ether ? "" : "ether ");
+		goto bad_desc;
+	}
+
+	/* claim data interface and set it up ... with side effects.
+	 * network traffic can't flow until an altsetting is enabled.
+	 */
+	if (info->data != info->control) {
+		status = usb_driver_claim_interface(driver, info->data, dev);
+		if (status < 0) {
+			dev_err(&intf->dev, "Second interface unclaimable\n");
+			goto bad_desc;
+		}
+	}
+	status = usbnet_get_endpoints(dev, info->data);
+	if (status < 0) {
+		dev_dbg(&intf->dev, "Mandatory endpoints missing\n");
+		goto bail_out_and_release;
+	}
+
+	/* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
+	if (info->data != info->control)
+		dev->status = NULL;
+	if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
+		struct usb_endpoint_descriptor	*desc;
+
+		dev->status = &info->control->cur_altsetting->endpoint[0];
+		desc = &dev->status->desc;
+		if (!usb_endpoint_is_int_in(desc) ||
+		    (le16_to_cpu(desc->wMaxPacketSize)
+		     < sizeof(struct usb_cdc_notification)) ||
+		    !desc->bInterval) {
+			dev_dbg(&intf->dev, "bad notification endpoint\n");
+			dev->status = NULL;
+		}
+	}
+	if (rndis && !dev->status) {
+		dev_err(&intf->dev, "missing RNDIS status endpoint\n");
+		status = -ENODEV;
+		goto bail_out_and_release;
+	}
+
+	/* override ethtool_ops */
+	dev->net->ethtool_ops = &cdc_ether_ethtool_ops;
+
+	return 0;
+
+bail_out_and_release:
+	usb_set_intfdata(info->data, NULL);
+	if (info->data != info->control)
+		usb_driver_release_interface(driver, info->data);
+bad_desc:
+	return status;
+}
+EXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind);
+
+/* like usbnet_generic_cdc_bind() but handles filter initialization
+ * correctly
+ */
+int usbnet_ether_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int rv;
+
+	rv = usbnet_generic_cdc_bind(dev, intf);
+	if (rv < 0)
+		goto bail_out;
+
+	/* Some devices don't initialise properly. In particular
+	 * the packet filter is not reset. There are devices that
+	 * don't do reset all the way. So the packet filter should
+	 * be set to a sane initial value.
+	 */
+	usbnet_cdc_update_filter(dev);
+
+bail_out:
+	return rv;
+}
+EXPORT_SYMBOL_GPL(usbnet_ether_cdc_bind);
+
+void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct cdc_state		*info = &dev->cdc;
+	struct usb_driver		*driver = driver_of(intf);
+
+	/* combined interface - nothing  to do */
+	if (info->data == info->control)
+		return;
+
+	/* disconnect master --> disconnect slave */
+	if (intf == info->control && info->data) {
+		/* ensure immediate exit from usbnet_disconnect */
+		usb_set_intfdata(info->data, NULL);
+		usb_driver_release_interface(driver, info->data);
+		info->data = NULL;
+	}
+
+	/* and vice versa (just in case) */
+	else if (intf == info->data && info->control) {
+		/* ensure immediate exit from usbnet_disconnect */
+		usb_set_intfdata(info->control, NULL);
+		usb_driver_release_interface(driver, info->control);
+		info->control = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
+
 /*-------------------------------------------------------------------------*/
 
 static int __init usbnet_init(void)
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 79f48eb388ee..b3a77078fb10 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -16,6 +16,11 @@
 #include <linux/usb.h>
 #include <linux/spinlock.h>
 
+static const u8 mbm_guid[16] = {
+	0xa3, 0x17, 0xa8, 0x8b, 0x04, 0x5e, 0x4f, 0x01,
+	0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a,
+};
+
 struct cdc_state {
 	struct usb_cdc_header_desc      *header;
 	struct usb_cdc_union_desc       *u;
@@ -304,4 +309,16 @@ extern void usbnet_status_stop(struct usbnet *dev);
 
 extern void usbnet_update_max_qlen(struct usbnet *dev);
 
+/* We need to override usbnet_*_link_ksettings in bind() */
+static const struct ethtool_ops cdc_ether_ethtool_ops = {
+	.get_link		= usbnet_get_link,
+	.nway_reset		= usbnet_nway_reset,
+	.get_drvinfo		= usbnet_get_drvinfo,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_ts_info		= ethtool_op_get_ts_info,
+	.get_link_ksettings	= usbnet_get_link_ksettings_internal,
+	.set_link_ksettings	= NULL,
+};
+
 #endif /* __LINUX_USB_USBNET_H */
-- 
2.54.0


  parent reply	other threads:[~2026-07-09 12:01 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-09 12:01 [PATCHv2 net-next 1/7] net: usb: centralize usbnet_cdc_zte_rx_fixup in usbnet Oliver Neukum
2026-07-09 12:01 ` [PATCHv2 net-next 2/7] net: usb: usbnet: add cdc_state to struct usbnet Oliver Neukum
2026-07-09 13:49   ` Andrew Lunn
2026-07-09 12:01 ` [PATCHv2 net-next 3/7] net: usb: use cdc_state in " Oliver Neukum
2026-07-09 13:49   ` Andrew Lunn
2026-07-09 12:01 ` [PATCHv2 net-next 4/7] net: usb: int51x1: drop dependency on cdc_ether Oliver Neukum
2026-07-09 13:50   ` Andrew Lunn
2026-07-09 12:01 ` Oliver Neukum [this message]
2026-07-09 13:51   ` [PATCHv2 net-next 5/7] net: usb: move exported symbols from cdc_ether to usbnet Andrew Lunn
2026-07-09 12:01 ` [PATCHv2 net-next 6/7] net: usb: remove dependencies on cdc_ether Oliver Neukum
2026-07-09 13:52   ` Andrew Lunn
2026-07-09 18:08   ` Sergey Shtylyov
2026-07-09 12:01 ` [PATCHv2 net-next 7/7] net: usb: usbnet: remove outdated sanity check Oliver Neukum
2026-07-09 13:56   ` Andrew Lunn
2026-07-09 12:37 ` [PATCHv2 net-next 1/7] net: usb: centralize usbnet_cdc_zte_rx_fixup in usbnet Manuel Ebner
2026-07-09 13:55   ` Andrew Lunn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260709120116.1423033-5-oneukum@suse.com \
    --to=oneukum@suse.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=manuelebner@mailbox.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox