[PATCHv3 net-next 6/9] net: usb: move exported symbols from cdc_ether to usbnet
From: Oliver Neukum <oneukum@suse.com>
Date: 2026-07-14 11:44:58
Also in:
lkml
Subsystem:
networking drivers, the rest, usb "usbnet" driver framework, usb cdc ethernet driver, usb networking drivers, usb subsystem · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds, Oliver Neukum, Greg Kroah-Hartman
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