162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CDC Ethernet based networking peripherals 462306a36Sopenharmony_ci * Copyright (C) 2003-2005 by David Brownell 562306a36Sopenharmony_ci * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci// #define DEBUG // error path messages, extra info 962306a36Sopenharmony_ci// #define VERBOSE // more; success messages 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/ethtool.h> 1562306a36Sopenharmony_ci#include <linux/workqueue.h> 1662306a36Sopenharmony_ci#include <linux/mii.h> 1762306a36Sopenharmony_ci#include <linux/usb.h> 1862306a36Sopenharmony_ci#include <linux/usb/cdc.h> 1962306a36Sopenharmony_ci#include <linux/usb/usbnet.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_NET_RNDIS_HOST) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int is_rndis(struct usb_interface_descriptor *desc) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci return (desc->bInterfaceClass == USB_CLASS_COMM && 2762306a36Sopenharmony_ci desc->bInterfaceSubClass == 2 && 2862306a36Sopenharmony_ci desc->bInterfaceProtocol == 0xff); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int is_activesync(struct usb_interface_descriptor *desc) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci return (desc->bInterfaceClass == USB_CLASS_MISC && 3462306a36Sopenharmony_ci desc->bInterfaceSubClass == 1 && 3562306a36Sopenharmony_ci desc->bInterfaceProtocol == 1); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int is_wireless_rndis(struct usb_interface_descriptor *desc) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return (desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER && 4162306a36Sopenharmony_ci desc->bInterfaceSubClass == 1 && 4262306a36Sopenharmony_ci desc->bInterfaceProtocol == 3); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int is_novatel_rndis(struct usb_interface_descriptor *desc) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci return (desc->bInterfaceClass == USB_CLASS_MISC && 4862306a36Sopenharmony_ci desc->bInterfaceSubClass == 4 && 4962306a36Sopenharmony_ci desc->bInterfaceProtocol == 1); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#else 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define is_rndis(desc) 0 5562306a36Sopenharmony_ci#define is_activesync(desc) 0 5662306a36Sopenharmony_ci#define is_wireless_rndis(desc) 0 5762306a36Sopenharmony_ci#define is_novatel_rndis(desc) 0 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#endif 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const u8 mbm_guid[16] = { 6262306a36Sopenharmony_ci 0xa3, 0x17, 0xa8, 0x8b, 0x04, 0x5e, 0x4f, 0x01, 6362306a36Sopenharmony_ci 0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_civoid usbnet_cdc_update_filter(struct usbnet *dev) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct net_device *net = dev->net; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci u16 cdc_filter = USB_CDC_PACKET_TYPE_DIRECTED 7162306a36Sopenharmony_ci | USB_CDC_PACKET_TYPE_BROADCAST; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* filtering on the device is an optional feature and not worth 7462306a36Sopenharmony_ci * the hassle so we just roughly care about snooping and if any 7562306a36Sopenharmony_ci * multicast is requested, we take every multicast 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci if (net->flags & IFF_PROMISC) 7862306a36Sopenharmony_ci cdc_filter |= USB_CDC_PACKET_TYPE_PROMISCUOUS; 7962306a36Sopenharmony_ci if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI)) 8062306a36Sopenharmony_ci cdc_filter |= USB_CDC_PACKET_TYPE_ALL_MULTICAST; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci usb_control_msg(dev->udev, 8362306a36Sopenharmony_ci usb_sndctrlpipe(dev->udev, 0), 8462306a36Sopenharmony_ci USB_CDC_SET_ETHERNET_PACKET_FILTER, 8562306a36Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE, 8662306a36Sopenharmony_ci cdc_filter, 8762306a36Sopenharmony_ci dev->intf->cur_altsetting->desc.bInterfaceNumber, 8862306a36Sopenharmony_ci NULL, 8962306a36Sopenharmony_ci 0, 9062306a36Sopenharmony_ci USB_CTRL_SET_TIMEOUT 9162306a36Sopenharmony_ci ); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usbnet_cdc_update_filter); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* We need to override usbnet_*_link_ksettings in bind() */ 9662306a36Sopenharmony_cistatic const struct ethtool_ops cdc_ether_ethtool_ops = { 9762306a36Sopenharmony_ci .get_link = usbnet_get_link, 9862306a36Sopenharmony_ci .nway_reset = usbnet_nway_reset, 9962306a36Sopenharmony_ci .get_drvinfo = usbnet_get_drvinfo, 10062306a36Sopenharmony_ci .get_msglevel = usbnet_get_msglevel, 10162306a36Sopenharmony_ci .set_msglevel = usbnet_set_msglevel, 10262306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 10362306a36Sopenharmony_ci .get_link_ksettings = usbnet_get_link_ksettings_internal, 10462306a36Sopenharmony_ci .set_link_ksettings = NULL, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* probes control interface, claims data interface, collects the bulk 10862306a36Sopenharmony_ci * endpoints, activates data interface (if needed), maybe sets MTU. 10962306a36Sopenharmony_ci * all pure cdc, except for certain firmware workarounds, and knowing 11062306a36Sopenharmony_ci * that rndis uses one different rule. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ciint usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u8 *buf = intf->cur_altsetting->extra; 11562306a36Sopenharmony_ci int len = intf->cur_altsetting->extralen; 11662306a36Sopenharmony_ci struct usb_interface_descriptor *d; 11762306a36Sopenharmony_ci struct cdc_state *info = (void *) &dev->data; 11862306a36Sopenharmony_ci int status; 11962306a36Sopenharmony_ci int rndis; 12062306a36Sopenharmony_ci bool android_rndis_quirk = false; 12162306a36Sopenharmony_ci struct usb_driver *driver = driver_of(intf); 12262306a36Sopenharmony_ci struct usb_cdc_parsed_header header; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (sizeof(dev->data) < sizeof(*info)) 12562306a36Sopenharmony_ci return -EDOM; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* expect strict spec conformance for the descriptors, but 12862306a36Sopenharmony_ci * cope with firmware which stores them in the wrong place 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci if (len == 0 && dev->udev->actconfig->extralen) { 13162306a36Sopenharmony_ci /* Motorola SB4100 (and others: Brad Hards says it's 13262306a36Sopenharmony_ci * from a Broadcom design) put CDC descriptors here 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci buf = dev->udev->actconfig->extra; 13562306a36Sopenharmony_ci len = dev->udev->actconfig->extralen; 13662306a36Sopenharmony_ci dev_dbg(&intf->dev, "CDC descriptors on config\n"); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Maybe CDC descriptors are after the endpoint? This bug has 14062306a36Sopenharmony_ci * been seen on some 2Wire Inc RNDIS-ish products. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci if (len == 0) { 14362306a36Sopenharmony_ci struct usb_host_endpoint *hep; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci hep = intf->cur_altsetting->endpoint; 14662306a36Sopenharmony_ci if (hep) { 14762306a36Sopenharmony_ci buf = hep->extra; 14862306a36Sopenharmony_ci len = hep->extralen; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci if (len) 15162306a36Sopenharmony_ci dev_dbg(&intf->dev, 15262306a36Sopenharmony_ci "CDC descriptors on endpoint\n"); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* this assumes that if there's a non-RNDIS vendor variant 15662306a36Sopenharmony_ci * of cdc-acm, it'll fail RNDIS requests cleanly. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci rndis = (is_rndis(&intf->cur_altsetting->desc) || 15962306a36Sopenharmony_ci is_activesync(&intf->cur_altsetting->desc) || 16062306a36Sopenharmony_ci is_wireless_rndis(&intf->cur_altsetting->desc) || 16162306a36Sopenharmony_ci is_novatel_rndis(&intf->cur_altsetting->desc)); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci memset(info, 0, sizeof(*info)); 16462306a36Sopenharmony_ci info->control = intf; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci cdc_parse_cdc_header(&header, intf, buf, len); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci info->u = header.usb_cdc_union_desc; 16962306a36Sopenharmony_ci info->header = header.usb_cdc_header_desc; 17062306a36Sopenharmony_ci info->ether = header.usb_cdc_ether_desc; 17162306a36Sopenharmony_ci if (!info->u) { 17262306a36Sopenharmony_ci if (rndis) 17362306a36Sopenharmony_ci goto skip; 17462306a36Sopenharmony_ci else /* in that case a quirk is mandatory */ 17562306a36Sopenharmony_ci goto bad_desc; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci /* we need a master/control interface (what we're 17862306a36Sopenharmony_ci * probed with) and a slave/data interface; union 17962306a36Sopenharmony_ci * descriptors sort this all out. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci info->control = usb_ifnum_to_if(dev->udev, info->u->bMasterInterface0); 18262306a36Sopenharmony_ci info->data = usb_ifnum_to_if(dev->udev, info->u->bSlaveInterface0); 18362306a36Sopenharmony_ci if (!info->control || !info->data) { 18462306a36Sopenharmony_ci dev_dbg(&intf->dev, 18562306a36Sopenharmony_ci "master #%u/%p slave #%u/%p\n", 18662306a36Sopenharmony_ci info->u->bMasterInterface0, 18762306a36Sopenharmony_ci info->control, 18862306a36Sopenharmony_ci info->u->bSlaveInterface0, 18962306a36Sopenharmony_ci info->data); 19062306a36Sopenharmony_ci /* fall back to hard-wiring for RNDIS */ 19162306a36Sopenharmony_ci if (rndis) { 19262306a36Sopenharmony_ci android_rndis_quirk = true; 19362306a36Sopenharmony_ci goto skip; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci goto bad_desc; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci if (info->control != intf) { 19862306a36Sopenharmony_ci dev_dbg(&intf->dev, "bogus CDC Union\n"); 19962306a36Sopenharmony_ci /* Ambit USB Cable Modem (and maybe others) 20062306a36Sopenharmony_ci * interchanges master and slave interface. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci if (info->data == intf) { 20362306a36Sopenharmony_ci info->data = info->control; 20462306a36Sopenharmony_ci info->control = intf; 20562306a36Sopenharmony_ci } else 20662306a36Sopenharmony_ci goto bad_desc; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* some devices merge these - skip class check */ 21062306a36Sopenharmony_ci if (info->control == info->data) 21162306a36Sopenharmony_ci goto skip; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* a data interface altsetting does the real i/o */ 21462306a36Sopenharmony_ci d = &info->data->cur_altsetting->desc; 21562306a36Sopenharmony_ci if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { 21662306a36Sopenharmony_ci dev_dbg(&intf->dev, "slave class %u\n", d->bInterfaceClass); 21762306a36Sopenharmony_ci goto bad_desc; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ciskip: 22062306a36Sopenharmony_ci /* Communication class functions with bmCapabilities are not 22162306a36Sopenharmony_ci * RNDIS. But some Wireless class RNDIS functions use 22262306a36Sopenharmony_ci * bmCapabilities for their own purpose. The failsafe is 22362306a36Sopenharmony_ci * therefore applied only to Communication class RNDIS 22462306a36Sopenharmony_ci * functions. The rndis test is redundant, but a cheap 22562306a36Sopenharmony_ci * optimization. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci if (rndis && is_rndis(&intf->cur_altsetting->desc) && 22862306a36Sopenharmony_ci header.usb_cdc_acm_descriptor && 22962306a36Sopenharmony_ci header.usb_cdc_acm_descriptor->bmCapabilities) { 23062306a36Sopenharmony_ci dev_dbg(&intf->dev, 23162306a36Sopenharmony_ci "ACM capabilities %02x, not really RNDIS?\n", 23262306a36Sopenharmony_ci header.usb_cdc_acm_descriptor->bmCapabilities); 23362306a36Sopenharmony_ci goto bad_desc; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (header.usb_cdc_ether_desc && info->ether->wMaxSegmentSize) { 23762306a36Sopenharmony_ci dev->hard_mtu = le16_to_cpu(info->ether->wMaxSegmentSize); 23862306a36Sopenharmony_ci /* because of Zaurus, we may be ignoring the host 23962306a36Sopenharmony_ci * side link address we were given. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (header.usb_cdc_mdlm_desc && 24462306a36Sopenharmony_ci memcmp(header.usb_cdc_mdlm_desc->bGUID, mbm_guid, 16)) { 24562306a36Sopenharmony_ci dev_dbg(&intf->dev, "GUID doesn't match\n"); 24662306a36Sopenharmony_ci goto bad_desc; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (header.usb_cdc_mdlm_detail_desc && 25062306a36Sopenharmony_ci header.usb_cdc_mdlm_detail_desc->bLength < 25162306a36Sopenharmony_ci (sizeof(struct usb_cdc_mdlm_detail_desc) + 1)) { 25262306a36Sopenharmony_ci dev_dbg(&intf->dev, "Descriptor too short\n"); 25362306a36Sopenharmony_ci goto bad_desc; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Microsoft ActiveSync based and some regular RNDIS devices lack the 25962306a36Sopenharmony_ci * CDC descriptors, so we'll hard-wire the interfaces and not check 26062306a36Sopenharmony_ci * for descriptors. 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * Some Android RNDIS devices have a CDC Union descriptor pointing 26362306a36Sopenharmony_ci * to non-existing interfaces. Ignore that and attempt the same 26462306a36Sopenharmony_ci * hard-wired 0 and 1 interfaces. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci if (rndis && (!info->u || android_rndis_quirk)) { 26762306a36Sopenharmony_ci info->control = usb_ifnum_to_if(dev->udev, 0); 26862306a36Sopenharmony_ci info->data = usb_ifnum_to_if(dev->udev, 1); 26962306a36Sopenharmony_ci if (!info->control || !info->data || info->control != intf) { 27062306a36Sopenharmony_ci dev_dbg(&intf->dev, 27162306a36Sopenharmony_ci "rndis: master #0/%p slave #1/%p\n", 27262306a36Sopenharmony_ci info->control, 27362306a36Sopenharmony_ci info->data); 27462306a36Sopenharmony_ci goto bad_desc; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci } else if (!info->header || (!rndis && !info->ether)) { 27862306a36Sopenharmony_ci dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", 27962306a36Sopenharmony_ci info->header ? "" : "header ", 28062306a36Sopenharmony_ci info->u ? "" : "union ", 28162306a36Sopenharmony_ci info->ether ? "" : "ether "); 28262306a36Sopenharmony_ci goto bad_desc; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* claim data interface and set it up ... with side effects. 28662306a36Sopenharmony_ci * network traffic can't flow until an altsetting is enabled. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci if (info->data != info->control) { 28962306a36Sopenharmony_ci status = usb_driver_claim_interface(driver, info->data, dev); 29062306a36Sopenharmony_ci if (status < 0) 29162306a36Sopenharmony_ci return status; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci status = usbnet_get_endpoints(dev, info->data); 29462306a36Sopenharmony_ci if (status < 0) { 29562306a36Sopenharmony_ci /* ensure immediate exit from usbnet_disconnect */ 29662306a36Sopenharmony_ci usb_set_intfdata(info->data, NULL); 29762306a36Sopenharmony_ci if (info->data != info->control) 29862306a36Sopenharmony_ci usb_driver_release_interface(driver, info->data); 29962306a36Sopenharmony_ci return status; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ 30362306a36Sopenharmony_ci if (info->data != info->control) 30462306a36Sopenharmony_ci dev->status = NULL; 30562306a36Sopenharmony_ci if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { 30662306a36Sopenharmony_ci struct usb_endpoint_descriptor *desc; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci dev->status = &info->control->cur_altsetting->endpoint[0]; 30962306a36Sopenharmony_ci desc = &dev->status->desc; 31062306a36Sopenharmony_ci if (!usb_endpoint_is_int_in(desc) || 31162306a36Sopenharmony_ci (le16_to_cpu(desc->wMaxPacketSize) 31262306a36Sopenharmony_ci < sizeof(struct usb_cdc_notification)) || 31362306a36Sopenharmony_ci !desc->bInterval) { 31462306a36Sopenharmony_ci dev_dbg(&intf->dev, "bad notification endpoint\n"); 31562306a36Sopenharmony_ci dev->status = NULL; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci if (rndis && !dev->status) { 31962306a36Sopenharmony_ci dev_dbg(&intf->dev, "missing RNDIS status endpoint\n"); 32062306a36Sopenharmony_ci usb_set_intfdata(info->data, NULL); 32162306a36Sopenharmony_ci usb_driver_release_interface(driver, info->data); 32262306a36Sopenharmony_ci return -ENODEV; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* override ethtool_ops */ 32662306a36Sopenharmony_ci dev->net->ethtool_ops = &cdc_ether_ethtool_ops; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cibad_desc: 33162306a36Sopenharmony_ci dev_info(&dev->udev->dev, "bad CDC descriptors\n"); 33262306a36Sopenharmony_ci return -ENODEV; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/* like usbnet_generic_cdc_bind() but handles filter initialization 33862306a36Sopenharmony_ci * correctly 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ciint usbnet_ether_cdc_bind(struct usbnet *dev, struct usb_interface *intf) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci int rv; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci rv = usbnet_generic_cdc_bind(dev, intf); 34562306a36Sopenharmony_ci if (rv < 0) 34662306a36Sopenharmony_ci goto bail_out; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Some devices don't initialise properly. In particular 34962306a36Sopenharmony_ci * the packet filter is not reset. There are devices that 35062306a36Sopenharmony_ci * don't do reset all the way. So the packet filter should 35162306a36Sopenharmony_ci * be set to a sane initial value. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci usbnet_cdc_update_filter(dev); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cibail_out: 35662306a36Sopenharmony_ci return rv; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usbnet_ether_cdc_bind); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_civoid usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct cdc_state *info = (void *) &dev->data; 36362306a36Sopenharmony_ci struct usb_driver *driver = driver_of(intf); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* combined interface - nothing to do */ 36662306a36Sopenharmony_ci if (info->data == info->control) 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* disconnect master --> disconnect slave */ 37062306a36Sopenharmony_ci if (intf == info->control && info->data) { 37162306a36Sopenharmony_ci /* ensure immediate exit from usbnet_disconnect */ 37262306a36Sopenharmony_ci usb_set_intfdata(info->data, NULL); 37362306a36Sopenharmony_ci usb_driver_release_interface(driver, info->data); 37462306a36Sopenharmony_ci info->data = NULL; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* and vice versa (just in case) */ 37862306a36Sopenharmony_ci else if (intf == info->data && info->control) { 37962306a36Sopenharmony_ci /* ensure immediate exit from usbnet_disconnect */ 38062306a36Sopenharmony_ci usb_set_intfdata(info->control, NULL); 38162306a36Sopenharmony_ci usb_driver_release_interface(driver, info->control); 38262306a36Sopenharmony_ci info->control = NULL; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usbnet_cdc_unbind); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* Communications Device Class, Ethernet Control model 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * Takes two interfaces. The DATA interface is inactive till an altsetting 39062306a36Sopenharmony_ci * is selected. Configuration data includes class descriptors. There's 39162306a36Sopenharmony_ci * an optional status endpoint on the control interface. 39262306a36Sopenharmony_ci * 39362306a36Sopenharmony_ci * This should interop with whatever the 2.4 "CDCEther.c" driver 39462306a36Sopenharmony_ci * (by Brad Hards) talked with, with more functionality. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void speed_change(struct usbnet *dev, __le32 *speeds) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci dev->tx_speed = __le32_to_cpu(speeds[0]); 40062306a36Sopenharmony_ci dev->rx_speed = __le32_to_cpu(speeds[1]); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_civoid usbnet_cdc_status(struct usbnet *dev, struct urb *urb) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct usb_cdc_notification *event; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (urb->actual_length < sizeof(*event)) 40862306a36Sopenharmony_ci return; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* SPEED_CHANGE can get split into two 8-byte packets */ 41162306a36Sopenharmony_ci if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) { 41262306a36Sopenharmony_ci speed_change(dev, (__le32 *) urb->transfer_buffer); 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci event = urb->transfer_buffer; 41762306a36Sopenharmony_ci switch (event->bNotificationType) { 41862306a36Sopenharmony_ci case USB_CDC_NOTIFY_NETWORK_CONNECTION: 41962306a36Sopenharmony_ci netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n", 42062306a36Sopenharmony_ci event->wValue ? "on" : "off"); 42162306a36Sopenharmony_ci usbnet_link_change(dev, !!event->wValue, 0); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */ 42462306a36Sopenharmony_ci netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n", 42562306a36Sopenharmony_ci urb->actual_length); 42662306a36Sopenharmony_ci if (urb->actual_length != (sizeof(*event) + 8)) 42762306a36Sopenharmony_ci set_bit(EVENT_STS_SPLIT, &dev->flags); 42862306a36Sopenharmony_ci else 42962306a36Sopenharmony_ci speed_change(dev, (__le32 *) &event[1]); 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci /* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS), 43262306a36Sopenharmony_ci * but there are no standard formats for the response data. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci default: 43562306a36Sopenharmony_ci netdev_err(dev->net, "CDC: unexpected notification %02x!\n", 43662306a36Sopenharmony_ci event->bNotificationType); 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usbnet_cdc_status); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ciint usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci int status; 44562306a36Sopenharmony_ci struct cdc_state *info = (void *) &dev->data; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) 44862306a36Sopenharmony_ci < sizeof(struct cdc_state))); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci status = usbnet_ether_cdc_bind(dev, intf); 45162306a36Sopenharmony_ci if (status < 0) 45262306a36Sopenharmony_ci return status; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci status = usbnet_get_ethernet_addr(dev, info->ether->iMACAddress); 45562306a36Sopenharmony_ci if (status < 0) { 45662306a36Sopenharmony_ci usb_set_intfdata(info->data, NULL); 45762306a36Sopenharmony_ci usb_driver_release_interface(driver_of(intf), info->data); 45862306a36Sopenharmony_ci return status; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usbnet_cdc_bind); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int usbnet_cdc_zte_bind(struct usbnet *dev, struct usb_interface *intf) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci int status = usbnet_cdc_bind(dev, intf); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!status && (dev->net->dev_addr[0] & 0x02)) 47062306a36Sopenharmony_ci eth_hw_addr_random(dev->net); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return status; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/* Make sure packets have correct destination MAC address 47662306a36Sopenharmony_ci * 47762306a36Sopenharmony_ci * A firmware bug observed on some devices (ZTE MF823/831/910) is that the 47862306a36Sopenharmony_ci * device sends packets with a static, bogus, random MAC address (event if 47962306a36Sopenharmony_ci * device MAC address has been updated). Always set MAC address to that of the 48062306a36Sopenharmony_ci * device. 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ciint usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci if (skb->len < ETH_HLEN || !(skb->data[0] & 0x02)) 48562306a36Sopenharmony_ci return 1; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci skb_reset_mac_header(skb); 48862306a36Sopenharmony_ci ether_addr_copy(eth_hdr(skb)->h_dest, dev->net->dev_addr); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return 1; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usbnet_cdc_zte_rx_fixup); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/* Ensure correct link state 49562306a36Sopenharmony_ci * 49662306a36Sopenharmony_ci * Some devices (ZTE MF823/831/910) export two carrier on notifications when 49762306a36Sopenharmony_ci * connected. This causes the link state to be incorrect. Work around this by 49862306a36Sopenharmony_ci * always setting the state to off, then on. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_cistatic void usbnet_cdc_zte_status(struct usbnet *dev, struct urb *urb) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct usb_cdc_notification *event; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (urb->actual_length < sizeof(*event)) 50562306a36Sopenharmony_ci return; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci event = urb->transfer_buffer; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (event->bNotificationType != USB_CDC_NOTIFY_NETWORK_CONNECTION) { 51062306a36Sopenharmony_ci usbnet_cdc_status(dev, urb); 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n", 51562306a36Sopenharmony_ci event->wValue ? "on" : "off"); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (event->wValue && 51862306a36Sopenharmony_ci netif_carrier_ok(dev->net)) 51962306a36Sopenharmony_ci netif_carrier_off(dev->net); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci usbnet_link_change(dev, !!event->wValue, 0); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic const struct driver_info cdc_info = { 52562306a36Sopenharmony_ci .description = "CDC Ethernet Device", 52662306a36Sopenharmony_ci .flags = FLAG_ETHER | FLAG_POINTTOPOINT, 52762306a36Sopenharmony_ci .bind = usbnet_cdc_bind, 52862306a36Sopenharmony_ci .unbind = usbnet_cdc_unbind, 52962306a36Sopenharmony_ci .status = usbnet_cdc_status, 53062306a36Sopenharmony_ci .set_rx_mode = usbnet_cdc_update_filter, 53162306a36Sopenharmony_ci .manage_power = usbnet_manage_power, 53262306a36Sopenharmony_ci}; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic const struct driver_info zte_cdc_info = { 53562306a36Sopenharmony_ci .description = "ZTE CDC Ethernet Device", 53662306a36Sopenharmony_ci .flags = FLAG_ETHER | FLAG_POINTTOPOINT, 53762306a36Sopenharmony_ci .bind = usbnet_cdc_zte_bind, 53862306a36Sopenharmony_ci .unbind = usbnet_cdc_unbind, 53962306a36Sopenharmony_ci .status = usbnet_cdc_zte_status, 54062306a36Sopenharmony_ci .set_rx_mode = usbnet_cdc_update_filter, 54162306a36Sopenharmony_ci .manage_power = usbnet_manage_power, 54262306a36Sopenharmony_ci .rx_fixup = usbnet_cdc_zte_rx_fixup, 54362306a36Sopenharmony_ci}; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic const struct driver_info wwan_info = { 54662306a36Sopenharmony_ci .description = "Mobile Broadband Network Device", 54762306a36Sopenharmony_ci .flags = FLAG_WWAN, 54862306a36Sopenharmony_ci .bind = usbnet_cdc_bind, 54962306a36Sopenharmony_ci .unbind = usbnet_cdc_unbind, 55062306a36Sopenharmony_ci .status = usbnet_cdc_status, 55162306a36Sopenharmony_ci .set_rx_mode = usbnet_cdc_update_filter, 55262306a36Sopenharmony_ci .manage_power = usbnet_manage_power, 55362306a36Sopenharmony_ci}; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci#define HUAWEI_VENDOR_ID 0x12D1 55862306a36Sopenharmony_ci#define NOVATEL_VENDOR_ID 0x1410 55962306a36Sopenharmony_ci#define ZTE_VENDOR_ID 0x19D2 56062306a36Sopenharmony_ci#define DELL_VENDOR_ID 0x413C 56162306a36Sopenharmony_ci#define REALTEK_VENDOR_ID 0x0bda 56262306a36Sopenharmony_ci#define SAMSUNG_VENDOR_ID 0x04e8 56362306a36Sopenharmony_ci#define LENOVO_VENDOR_ID 0x17ef 56462306a36Sopenharmony_ci#define LINKSYS_VENDOR_ID 0x13b1 56562306a36Sopenharmony_ci#define NVIDIA_VENDOR_ID 0x0955 56662306a36Sopenharmony_ci#define HP_VENDOR_ID 0x03f0 56762306a36Sopenharmony_ci#define MICROSOFT_VENDOR_ID 0x045e 56862306a36Sopenharmony_ci#define UBLOX_VENDOR_ID 0x1546 56962306a36Sopenharmony_ci#define TPLINK_VENDOR_ID 0x2357 57062306a36Sopenharmony_ci#define AQUANTIA_VENDOR_ID 0x2eca 57162306a36Sopenharmony_ci#define ASIX_VENDOR_ID 0x0b95 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic const struct usb_device_id products[] = { 57462306a36Sopenharmony_ci/* BLACKLIST !! 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * First blacklist any products that are egregiously nonconformant 57762306a36Sopenharmony_ci * with the CDC Ethernet specs. Minor braindamage we cope with; when 57862306a36Sopenharmony_ci * they're not even trying, needing a separate driver is only the first 57962306a36Sopenharmony_ci * of the differences to show up. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci#define ZAURUS_MASTER_INTERFACE \ 58362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, \ 58462306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \ 58562306a36Sopenharmony_ci .bInterfaceProtocol = USB_CDC_PROTO_NONE 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci#define ZAURUS_FAKE_INTERFACE \ 58862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, \ 58962306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, \ 59062306a36Sopenharmony_ci .bInterfaceProtocol = USB_CDC_PROTO_NONE 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci/* SA-1100 based Sharp Zaurus ("collie"), or compatible; 59362306a36Sopenharmony_ci * wire-incompatible with true CDC Ethernet implementations. 59462306a36Sopenharmony_ci * (And, it seems, needlessly so...) 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 59862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 59962306a36Sopenharmony_ci .idVendor = 0x04DD, 60062306a36Sopenharmony_ci .idProduct = 0x8004, 60162306a36Sopenharmony_ci ZAURUS_MASTER_INTERFACE, 60262306a36Sopenharmony_ci .driver_info = 0, 60362306a36Sopenharmony_ci}, 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* PXA-25x based Sharp Zaurii. Note that it seems some of these 60662306a36Sopenharmony_ci * (later models especially) may have shipped only with firmware 60762306a36Sopenharmony_ci * advertising false "CDC MDLM" compatibility ... but we're not 60862306a36Sopenharmony_ci * clear which models did that, so for now let's assume the worst. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 61262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 61362306a36Sopenharmony_ci .idVendor = 0x04DD, 61462306a36Sopenharmony_ci .idProduct = 0x8005, /* A-300 */ 61562306a36Sopenharmony_ci ZAURUS_MASTER_INTERFACE, 61662306a36Sopenharmony_ci .driver_info = 0, 61762306a36Sopenharmony_ci}, { 61862306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 61962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 62062306a36Sopenharmony_ci .idVendor = 0x04DD, 62162306a36Sopenharmony_ci .idProduct = 0x8005, /* A-300 */ 62262306a36Sopenharmony_ci ZAURUS_FAKE_INTERFACE, 62362306a36Sopenharmony_ci .driver_info = 0, 62462306a36Sopenharmony_ci}, { 62562306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 62662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 62762306a36Sopenharmony_ci .idVendor = 0x04DD, 62862306a36Sopenharmony_ci .idProduct = 0x8006, /* B-500/SL-5600 */ 62962306a36Sopenharmony_ci ZAURUS_MASTER_INTERFACE, 63062306a36Sopenharmony_ci .driver_info = 0, 63162306a36Sopenharmony_ci}, { 63262306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 63362306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 63462306a36Sopenharmony_ci .idVendor = 0x04DD, 63562306a36Sopenharmony_ci .idProduct = 0x8006, /* B-500/SL-5600 */ 63662306a36Sopenharmony_ci ZAURUS_FAKE_INTERFACE, 63762306a36Sopenharmony_ci .driver_info = 0, 63862306a36Sopenharmony_ci}, { 63962306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 64062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 64162306a36Sopenharmony_ci .idVendor = 0x04DD, 64262306a36Sopenharmony_ci .idProduct = 0x8007, /* C-700 */ 64362306a36Sopenharmony_ci ZAURUS_MASTER_INTERFACE, 64462306a36Sopenharmony_ci .driver_info = 0, 64562306a36Sopenharmony_ci}, { 64662306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 64762306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 64862306a36Sopenharmony_ci .idVendor = 0x04DD, 64962306a36Sopenharmony_ci .idProduct = 0x8007, /* C-700 */ 65062306a36Sopenharmony_ci ZAURUS_FAKE_INTERFACE, 65162306a36Sopenharmony_ci .driver_info = 0, 65262306a36Sopenharmony_ci}, { 65362306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 65462306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 65562306a36Sopenharmony_ci .idVendor = 0x04DD, 65662306a36Sopenharmony_ci .idProduct = 0x9031, /* C-750 C-760 */ 65762306a36Sopenharmony_ci ZAURUS_MASTER_INTERFACE, 65862306a36Sopenharmony_ci .driver_info = 0, 65962306a36Sopenharmony_ci}, { 66062306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 66162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 66262306a36Sopenharmony_ci .idVendor = 0x04DD, 66362306a36Sopenharmony_ci .idProduct = 0x9032, /* SL-6000 */ 66462306a36Sopenharmony_ci ZAURUS_MASTER_INTERFACE, 66562306a36Sopenharmony_ci .driver_info = 0, 66662306a36Sopenharmony_ci}, { 66762306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 66862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 66962306a36Sopenharmony_ci .idVendor = 0x04DD, 67062306a36Sopenharmony_ci .idProduct = 0x9032, /* SL-6000 */ 67162306a36Sopenharmony_ci ZAURUS_FAKE_INTERFACE, 67262306a36Sopenharmony_ci .driver_info = 0, 67362306a36Sopenharmony_ci}, { 67462306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 67562306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 67662306a36Sopenharmony_ci .idVendor = 0x04DD, 67762306a36Sopenharmony_ci /* reported with some C860 units */ 67862306a36Sopenharmony_ci .idProduct = 0x9050, /* C-860 */ 67962306a36Sopenharmony_ci ZAURUS_MASTER_INTERFACE, 68062306a36Sopenharmony_ci .driver_info = 0, 68162306a36Sopenharmony_ci}, 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci/* Olympus has some models with a Zaurus-compatible option. 68462306a36Sopenharmony_ci * R-1000 uses a FreeScale i.MXL cpu (ARMv4T) 68562306a36Sopenharmony_ci */ 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_INT_INFO 68862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEVICE, 68962306a36Sopenharmony_ci .idVendor = 0x07B4, 69062306a36Sopenharmony_ci .idProduct = 0x0F02, /* R-1000 */ 69162306a36Sopenharmony_ci ZAURUS_MASTER_INTERFACE, 69262306a36Sopenharmony_ci .driver_info = 0, 69362306a36Sopenharmony_ci}, 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci/* LG Electronics VL600 wants additional headers on every frame */ 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM, 69862306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 69962306a36Sopenharmony_ci .driver_info = 0, 70062306a36Sopenharmony_ci}, 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci/* Logitech Harmony 900 - uses the pseudo-MDLM (BLAN) driver */ 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x046d, 0xc11f, USB_CLASS_COMM, 70562306a36Sopenharmony_ci USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), 70662306a36Sopenharmony_ci .driver_info = 0, 70762306a36Sopenharmony_ci}, 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci/* Novatel USB551L and MC551 - handled by qmi_wwan */ 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0xB001, USB_CLASS_COMM, 71262306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 71362306a36Sopenharmony_ci .driver_info = 0, 71462306a36Sopenharmony_ci}, 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci/* Novatel E362 - handled by qmi_wwan */ 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0x9010, USB_CLASS_COMM, 71962306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 72062306a36Sopenharmony_ci .driver_info = 0, 72162306a36Sopenharmony_ci}, 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci/* Dell Wireless 5800 (Novatel E362) - handled by qmi_wwan */ 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x8195, USB_CLASS_COMM, 72662306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 72762306a36Sopenharmony_ci .driver_info = 0, 72862306a36Sopenharmony_ci}, 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/* Dell Wireless 5800 (Novatel E362) - handled by qmi_wwan */ 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x8196, USB_CLASS_COMM, 73362306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 73462306a36Sopenharmony_ci .driver_info = 0, 73562306a36Sopenharmony_ci}, 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/* Dell Wireless 5804 (Novatel E371) - handled by qmi_wwan */ 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x819b, USB_CLASS_COMM, 74062306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 74162306a36Sopenharmony_ci .driver_info = 0, 74262306a36Sopenharmony_ci}, 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci/* Novatel Expedite E371 - handled by qmi_wwan */ 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0x9011, USB_CLASS_COMM, 74762306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 74862306a36Sopenharmony_ci .driver_info = 0, 74962306a36Sopenharmony_ci}, 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/* HP lt2523 (Novatel E371) - handled by qmi_wwan */ 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(HP_VENDOR_ID, 0x421d, USB_CLASS_COMM, 75462306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 75562306a36Sopenharmony_ci .driver_info = 0, 75662306a36Sopenharmony_ci}, 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci/* AnyDATA ADU960S - handled by qmi_wwan */ 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, USB_CLASS_COMM, 76162306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 76262306a36Sopenharmony_ci .driver_info = 0, 76362306a36Sopenharmony_ci}, 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci/* Huawei E1820 - handled by qmi_wwan */ 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci USB_DEVICE_INTERFACE_NUMBER(HUAWEI_VENDOR_ID, 0x14ac, 1), 76862306a36Sopenharmony_ci .driver_info = 0, 76962306a36Sopenharmony_ci}, 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci/* Realtek RTL8153 Based USB 3.0 Ethernet Adapters */ 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8153, USB_CLASS_COMM, 77462306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 77562306a36Sopenharmony_ci .driver_info = 0, 77662306a36Sopenharmony_ci}, 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci/* Lenovo Powered USB-C Travel Hub (4X90S92381, based on Realtek RTL8153) */ 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x721e, USB_CLASS_COMM, 78162306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 78262306a36Sopenharmony_ci .driver_info = 0, 78362306a36Sopenharmony_ci}, 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/* Aquantia AQtion USB to 5GbE Controller (based on AQC111U) */ 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(AQUANTIA_VENDOR_ID, 0xc101, 78862306a36Sopenharmony_ci USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, 78962306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 79062306a36Sopenharmony_ci .driver_info = 0, 79162306a36Sopenharmony_ci}, 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci/* ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter(based on AQC111U) */ 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2790, USB_CLASS_COMM, 79662306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 79762306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 79862306a36Sopenharmony_ci .driver_info = 0, 79962306a36Sopenharmony_ci}, 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/* ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter(based on AQC112U) */ 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2791, USB_CLASS_COMM, 80462306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 80562306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 80662306a36Sopenharmony_ci .driver_info = 0, 80762306a36Sopenharmony_ci}, 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci/* USB-C 3.1 to 5GBASE-T Ethernet Adapter (based on AQC111U) */ 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0xe05a, USB_CLASS_COMM, 81262306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 81362306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 81462306a36Sopenharmony_ci .driver_info = 0, 81562306a36Sopenharmony_ci}, 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci/* QNAP QNA-UC5G1T USB to 5GbE Adapter (based on AQC111U) */ 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1c04, 0x0015, USB_CLASS_COMM, 82062306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 82162306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 82262306a36Sopenharmony_ci .driver_info = 0, 82362306a36Sopenharmony_ci}, 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/* WHITELIST!!! 82662306a36Sopenharmony_ci * 82762306a36Sopenharmony_ci * CDC Ether uses two interfaces, not necessarily consecutive. 82862306a36Sopenharmony_ci * We match the main interface, ignoring the optional device 82962306a36Sopenharmony_ci * class so we could handle devices that aren't exclusively 83062306a36Sopenharmony_ci * CDC ether. 83162306a36Sopenharmony_ci * 83262306a36Sopenharmony_ci * NOTE: this match must come AFTER entries blacklisting devices 83362306a36Sopenharmony_ci * because of bugs/quirks in a given product (like Zaurus, above). 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci /* ZTE (Vodafone) K3805-Z */ 83762306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1003, USB_CLASS_COMM, 83862306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 83962306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 84062306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 84162306a36Sopenharmony_ci}, { 84262306a36Sopenharmony_ci /* ZTE (Vodafone) K3806-Z */ 84362306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1015, USB_CLASS_COMM, 84462306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 84562306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 84662306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 84762306a36Sopenharmony_ci}, { 84862306a36Sopenharmony_ci /* ZTE (Vodafone) K4510-Z */ 84962306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1173, USB_CLASS_COMM, 85062306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 85162306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 85262306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 85362306a36Sopenharmony_ci}, { 85462306a36Sopenharmony_ci /* ZTE (Vodafone) K3770-Z */ 85562306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1177, USB_CLASS_COMM, 85662306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 85762306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 85862306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 85962306a36Sopenharmony_ci}, { 86062306a36Sopenharmony_ci /* ZTE (Vodafone) K3772-Z */ 86162306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1181, USB_CLASS_COMM, 86262306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 86362306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 86462306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 86562306a36Sopenharmony_ci}, { 86662306a36Sopenharmony_ci /* Telit modules */ 86762306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(0x1bc7, USB_CLASS_COMM, 86862306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 86962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t) &wwan_info, 87062306a36Sopenharmony_ci}, { 87162306a36Sopenharmony_ci /* Dell DW5580 modules */ 87262306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x81ba, USB_CLASS_COMM, 87362306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), 87462306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&wwan_info, 87562306a36Sopenharmony_ci}, { 87662306a36Sopenharmony_ci /* Huawei ME906 and ME909 */ 87762306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x15c1, USB_CLASS_COMM, 87862306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 87962306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 88062306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 88162306a36Sopenharmony_ci}, { 88262306a36Sopenharmony_ci /* ZTE modules */ 88362306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, USB_CLASS_COMM, 88462306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 88562306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 88662306a36Sopenharmony_ci .driver_info = (unsigned long)&zte_cdc_info, 88762306a36Sopenharmony_ci}, { 88862306a36Sopenharmony_ci /* U-blox TOBY-L2 */ 88962306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(UBLOX_VENDOR_ID, 0x1143, USB_CLASS_COMM, 89062306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 89162306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 89262306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 89362306a36Sopenharmony_ci}, { 89462306a36Sopenharmony_ci /* U-blox SARA-U2 */ 89562306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(UBLOX_VENDOR_ID, 0x1104, USB_CLASS_COMM, 89662306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 89762306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 89862306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 89962306a36Sopenharmony_ci}, { 90062306a36Sopenharmony_ci /* U-blox LARA-R6 01B */ 90162306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(UBLOX_VENDOR_ID, 0x1313, USB_CLASS_COMM, 90262306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 90362306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 90462306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 90562306a36Sopenharmony_ci}, { 90662306a36Sopenharmony_ci /* U-blox LARA-L6 */ 90762306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(UBLOX_VENDOR_ID, 0x1343, USB_CLASS_COMM, 90862306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 90962306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 91062306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 91162306a36Sopenharmony_ci}, { 91262306a36Sopenharmony_ci /* Cinterion PLS8 modem by GEMALTO */ 91362306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0061, USB_CLASS_COMM, 91462306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 91562306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 91662306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 91762306a36Sopenharmony_ci}, { 91862306a36Sopenharmony_ci /* Cinterion AHS3 modem by GEMALTO */ 91962306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0055, USB_CLASS_COMM, 92062306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 92162306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 92262306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 92362306a36Sopenharmony_ci}, { 92462306a36Sopenharmony_ci /* Cinterion PLS62-W modem by GEMALTO/THALES */ 92562306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x005b, USB_CLASS_COMM, 92662306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 92762306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 92862306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 92962306a36Sopenharmony_ci}, { 93062306a36Sopenharmony_ci /* Cinterion PLS83/PLS63 modem by GEMALTO/THALES */ 93162306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0069, USB_CLASS_COMM, 93262306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 93362306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 93462306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 93562306a36Sopenharmony_ci}, { 93662306a36Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, 93762306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 93862306a36Sopenharmony_ci .driver_info = (unsigned long) &cdc_info, 93962306a36Sopenharmony_ci}, { 94062306a36Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, 94162306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 94262306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci}, { 94562306a36Sopenharmony_ci /* Various Huawei modems with a network port like the UMG1831 */ 94662306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_COMM, 94762306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 255), 94862306a36Sopenharmony_ci .driver_info = (unsigned long)&wwan_info, 94962306a36Sopenharmony_ci}, 95062306a36Sopenharmony_ci { }, /* END */ 95162306a36Sopenharmony_ci}; 95262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic struct usb_driver cdc_driver = { 95562306a36Sopenharmony_ci .name = "cdc_ether", 95662306a36Sopenharmony_ci .id_table = products, 95762306a36Sopenharmony_ci .probe = usbnet_probe, 95862306a36Sopenharmony_ci .disconnect = usbnet_disconnect, 95962306a36Sopenharmony_ci .suspend = usbnet_suspend, 96062306a36Sopenharmony_ci .resume = usbnet_resume, 96162306a36Sopenharmony_ci .reset_resume = usbnet_resume, 96262306a36Sopenharmony_ci .supports_autosuspend = 1, 96362306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 96462306a36Sopenharmony_ci}; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cimodule_usb_driver(cdc_driver); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 96962306a36Sopenharmony_ciMODULE_DESCRIPTION("USB CDC Ethernet devices"); 97062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 971