// SPDX-License-Identifier: GPL-2.0-or-later /* * CDC Ethernet based networking peripherals * Copyright (C) 2003-2005 by David Brownell * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync)
*/
// #define DEBUG // error path messages, extra info // #define VERBOSE // more; success messages
/* filtering on the device is an optional feature and not worth * the hassle so we just roughly care about snooping and if any * multicast is requested, we take every multicast
*/ if (net->flags & IFF_PROMISC)
cdc_filter |= USB_CDC_PACKET_TYPE_PROMISCUOUS; if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI))
cdc_filter |= USB_CDC_PACKET_TYPE_ALL_MULTICAST;
/* We need to override usbnet_*_link_ksettings in bind() */ staticconststruct 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 = (void *) &dev->data; int status; 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));
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 */ 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;
} goto bad_desc;
} if (info->control != intf) {
dev_dbg(&intf->dev, "bogus CDC Union\n"); /* Ambit USB Cable Modem (and maybe others) * interchanges master and slave interface.
*/ if (info->data == intf) {
info->data = info->control;
info->control = intf;
} else goto bad_desc;
}
/* some devices merge these - skip class check */ if (info->control == info->data) goto skip;
/* a data interface altsetting does the real i/o */
d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
dev_dbg(&intf->dev, "slave class %u\n", d->bInterfaceClass); goto bad_desc;
}
skip: /* 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_dbg(&intf->dev, "ACM capabilities %02x, not really RNDIS?\n",
header.usb_cdc_acm_descriptor->bmCapabilities); goto bad_desc;
}
if (header.usb_cdc_ether_desc && 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_detail_desc &&
header.usb_cdc_mdlm_detail_desc->bLength <
(sizeof(struct usb_cdc_mdlm_detail_desc) + 1)) {
dev_dbg(&intf->dev, "Descriptor too short\n"); goto bad_desc;
}
/* Microsoft ActiveSync based and some regular RNDIS devices lack the * 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_dbg(&intf->dev, "rndis: master #0/%p slave #1/%p\n",
info->control,
info->data); 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) return status;
}
status = usbnet_get_endpoints(dev, info->data); if (status < 0) { /* ensure immediate exit from usbnet_disconnect */
usb_set_intfdata(info->data, NULL); if (info->data != info->control)
usb_driver_release_interface(driver, info->data); return status;
}
/* 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;
/* 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);
/* 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.
*/
/* SPEED_CHANGE can get split into two 8-byte packets */ if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
speed_change(dev, (__le32 *) urb->transfer_buffer); return;
}
event = urb->transfer_buffer; switch (event->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION:
netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
event->wValue ? "on" : "off"); if (netif_carrier_ok(dev->net) != !!event->wValue)
usbnet_link_change(dev, !!event->wValue, 0); break; case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n",
urb->actual_length); if (urb->actual_length != (sizeof(*event) + 8))
set_bit(EVENT_STS_SPLIT, &dev->flags); else
speed_change(dev, (__le32 *) &event[1]); break; /* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS), * but there are no standard formats for the response data.
*/ default:
netdev_err(dev->net, "CDC: unexpected notification %02x!\n",
event->bNotificationType); break;
}
}
EXPORT_SYMBOL_GPL(usbnet_cdc_status);
int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
{ int status; struct cdc_state *info = (void *) &dev->data;
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);
staticint usbnet_cdc_zte_bind(struct usbnet *dev, struct usb_interface *intf)
{ int status = usbnet_cdc_bind(dev, intf);
if (!status && (dev->net->dev_addr[0] & 0x02))
eth_hw_addr_random(dev->net);
return status;
}
/* Make sure packets have correct destination MAC address * * A firmware bug observed on some devices (ZTE MF823/831/910) is that the * device sends packets with a static, bogus, random MAC address (event if * device MAC address has been updated). Always set MAC address to that of the * device.
*/ int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{ if (skb->len < ETH_HLEN || !(skb->data[0] & 0x02)) return 1;
/* Ensure correct link state * * Some devices (ZTE MF823/831/910) export two carrier on notifications when * connected. This causes the link state to be incorrect. Work around this by * always setting the state to off, then on.
*/ staticvoid usbnet_cdc_zte_status(struct usbnet *dev, struct urb *urb)
{ struct usb_cdc_notification *event;
if (urb->actual_length < sizeof(*event)) return;
event = urb->transfer_buffer;
if (event->bNotificationType != USB_CDC_NOTIFY_NETWORK_CONNECTION) {
usbnet_cdc_status(dev, urb); return;
}
staticconststruct usb_device_id products[] = { /* BLACKLIST !! * * First blacklist any products that are egregiously nonconformant * with the CDC Ethernet specs. Minor braindamage we cope with; when * they're not even trying, needing a separate driver is only the first * of the differences to show up.
*/
}, { /* Various Huawei modems with a network port like the UMG1831 */
USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_COMM,
USB_CDC_SUBCLASS_ETHERNET, 255),
.driver_info = (unsignedlong)&wwan_info,
},
{ }, /* END */
};
MODULE_DEVICE_TABLE(usb, products);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.