diff options
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/usb/asix.h | 2 | ||||
-rw-r--r-- | drivers/net/usb/asix_common.c | 115 | ||||
-rw-r--r-- | drivers/net/usb/cdc-phonet.c | 27 | ||||
-rw-r--r-- | drivers/net/usb/cdc_ether.c | 235 | ||||
-rw-r--r-- | drivers/net/usb/cdc_mbim.c | 30 | ||||
-rw-r--r-- | drivers/net/usb/cdc_ncm.c | 130 | ||||
-rw-r--r-- | drivers/net/usb/dm9601.c | 1 | ||||
-rw-r--r-- | drivers/net/usb/kaweth.c | 6 | ||||
-rw-r--r-- | drivers/net/usb/lan78xx.c | 672 | ||||
-rw-r--r-- | drivers/net/usb/lan78xx.h | 193 | ||||
-rw-r--r-- | drivers/net/usb/mcs7830.c | 1 | ||||
-rw-r--r-- | drivers/net/usb/qmi_wwan.c | 93 | ||||
-rw-r--r-- | drivers/net/usb/r8152.c | 31 | ||||
-rw-r--r-- | drivers/net/usb/smsc75xx.c | 5 | ||||
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 5 | ||||
-rw-r--r-- | drivers/net/usb/sr9800.c | 4 | ||||
-rw-r--r-- | drivers/net/usb/usbnet.c | 144 |
18 files changed, 729 insertions, 969 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index e66805eef..7f83504df 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -109,6 +109,8 @@ config USB_RTL8152 config USB_LAN78XX tristate "Microchip LAN78XX Based USB Ethernet Adapters" select MII + select PHYLIB + select MICROCHIP_PHY help This option adds support for Microchip LAN78XX based USB 2 & USB 3 10/100/1000 Ethernet adapters. @@ -542,7 +544,7 @@ config USB_NET_INT51X1 config USB_CDC_PHONET tristate "CDC Phonet support" - depends on PHONET + depends on PHONET && USB_USBNET help Choose this option to support the Phonet interface to a Nokia cellular modem, as found on most Nokia handsets with the diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index 5d049d00c..a2d3ea6ef 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -168,7 +168,7 @@ struct asix_data { struct asix_rx_fixup_info { struct sk_buff *ax_skb; u32 header; - u16 size; + u16 remaining; bool split_head; }; diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 079069a06..bd9acff1e 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -54,71 +54,101 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, struct asix_rx_fixup_info *rx) { int offset = 0; + u16 size; + + /* When an Ethernet frame spans multiple URB socket buffers, + * do a sanity test for the Data header synchronisation. + * Attempt to detect the situation of the previous socket buffer having + * been truncated or a socket buffer was missing. These situations + * cause a discontinuity in the data stream and therefore need to avoid + * appending bad data to the end of the current netdev socket buffer. + * Also avoid unnecessarily discarding a good current netdev socket + * buffer. + */ + if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { + offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32); + rx->header = get_unaligned_le32(skb->data + offset); + offset = 0; + + size = (u16)(rx->header & 0x7ff); + if (size != ((~rx->header >> 16) & 0x7ff)) { + netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", + rx->remaining); + if (rx->ax_skb) { + kfree_skb(rx->ax_skb); + rx->ax_skb = NULL; + /* Discard the incomplete netdev Ethernet frame + * and assume the Data header is at the start of + * the current URB socket buffer. + */ + } + rx->remaining = 0; + } + } while (offset + sizeof(u16) <= skb->len) { - u16 remaining = 0; + u16 copy_length; unsigned char *data; - if (!rx->size) { - if ((skb->len - offset == sizeof(u16)) || - rx->split_head) { - if(!rx->split_head) { - rx->header = get_unaligned_le16( - skb->data + offset); - rx->split_head = true; - offset += sizeof(u16); - break; - } else { - rx->header |= (get_unaligned_le16( - skb->data + offset) - << 16); - rx->split_head = false; - offset += sizeof(u16); - } + if (!rx->remaining) { + if (skb->len - offset == sizeof(u16)) { + rx->header = get_unaligned_le16( + skb->data + offset); + rx->split_head = true; + offset += sizeof(u16); + break; + } + + if (rx->split_head == true) { + rx->header |= (get_unaligned_le16( + skb->data + offset) << 16); + rx->split_head = false; + offset += sizeof(u16); } else { rx->header = get_unaligned_le32(skb->data + offset); offset += sizeof(u32); } - /* get the packet length */ - rx->size = (u16) (rx->header & 0x7ff); - if (rx->size != ((~rx->header >> 16) & 0x7ff)) { + /* take frame length from Data header 32-bit word */ + size = (u16)(rx->header & 0x7ff); + if (size != ((~rx->header >> 16) & 0x7ff)) { netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", rx->header, offset); - rx->size = 0; return 0; } - rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, - rx->size); - if (!rx->ax_skb) { - rx->size = 0; + if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { + netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", + size); return 0; } - } - if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { - netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", - rx->size); - kfree_skb(rx->ax_skb); - rx->ax_skb = NULL; - rx->size = 0U; + /* Sometimes may fail to get a netdev socket buffer but + * continue to process the URB socket buffer so that + * synchronisation of the Ethernet frame Data header + * word is maintained. + */ + rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, size); - return 0; + rx->remaining = size; } - if (rx->size > skb->len - offset) { - remaining = rx->size - (skb->len - offset); - rx->size = skb->len - offset; + if (rx->remaining > skb->len - offset) { + copy_length = skb->len - offset; + rx->remaining -= copy_length; + } else { + copy_length = rx->remaining; + rx->remaining = 0; } - data = skb_put(rx->ax_skb, rx->size); - memcpy(data, skb->data + offset, rx->size); - if (!remaining) - usbnet_skb_return(dev, rx->ax_skb); + if (rx->ax_skb) { + data = skb_put(rx->ax_skb, copy_length); + memcpy(data, skb->data + offset, copy_length); + if (!rx->remaining) + usbnet_skb_return(dev, rx->ax_skb); + } - offset += (rx->size + 1) & 0xfffe; - rx->size = remaining; + offset += (copy_length + 1) & 0xfffe; } if (skb->len != offset) { @@ -558,7 +588,6 @@ void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) usbnet_get_drvinfo(net, info); strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); - info->eedump_len = AX_EEPROM_LEN; } int asix_set_mac_address(struct net_device *net, void *p) diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 415ce8b88..ff2270ead 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -340,32 +340,13 @@ static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *i u8 *data; int phonet = 0; int len, err; + struct usb_cdc_parsed_header hdr; data = intf->altsetting->extra; len = intf->altsetting->extralen; - while (len >= 3) { - u8 dlen = data[0]; - if (dlen < 3) - return -EINVAL; - - /* bDescriptorType */ - if (data[1] == USB_DT_CS_INTERFACE) { - /* bDescriptorSubType */ - switch (data[2]) { - case USB_CDC_UNION_TYPE: - if (union_header || dlen < 5) - break; - union_header = - (struct usb_cdc_union_desc *)data; - break; - case 0xAB: - phonet = 1; - break; - } - } - data += dlen; - len -= dlen; - } + cdc_parse_cdc_header(&hdr, intf, data, len); + union_header = hdr.usb_cdc_union_desc; + phonet = hdr.phonet_magic_present; if (!union_header || !phonet) return -EINVAL; diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 35a2bffe8..3da70bf99 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -112,8 +112,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) int rndis; bool android_rndis_quirk = false; struct usb_driver *driver = driver_of(intf); - struct usb_cdc_mdlm_desc *desc = NULL; - struct usb_cdc_mdlm_detail_desc *detail = NULL; + struct usb_cdc_parsed_header header; if (sizeof(dev->data) < sizeof(*info)) return -EDOM; @@ -155,156 +154,89 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) memset(info, 0, sizeof(*info)); info->control = intf; - while (len > 3) { - if (buf[1] != USB_DT_CS_INTERFACE) - goto next_desc; - - /* use bDescriptorSubType to identify the CDC descriptors. - * We expect devices with CDC header and union descriptors. - * For CDC Ethernet we need the ethernet descriptor. - * For RNDIS, ignore two (pointless) CDC modem descriptors - * in favor of a complicated OID-based RPC scheme doing what - * CDC Ethernet achieves with a simple descriptor. - */ - switch (buf[2]) { - case USB_CDC_HEADER_TYPE: - if (info->header) { - dev_dbg(&intf->dev, "extra CDC header\n"); - goto bad_desc; - } - info->header = (void *) buf; - if (info->header->bLength != sizeof(*info->header)) { - dev_dbg(&intf->dev, "CDC header len %u\n", - info->header->bLength); - goto bad_desc; - } - break; - case USB_CDC_ACM_TYPE: - /* paranoia: disambiguate a "real" vendor-specific - * modem interface from an RNDIS non-modem. - */ - if (rndis) { - struct usb_cdc_acm_descriptor *acm; - - acm = (void *) buf; - if (acm->bmCapabilities) { - dev_dbg(&intf->dev, - "ACM capabilities %02x, " - "not really RNDIS?\n", - acm->bmCapabilities); - goto bad_desc; - } - } - break; - case USB_CDC_UNION_TYPE: - if (info->u) { - dev_dbg(&intf->dev, "extra CDC union\n"); - goto bad_desc; - } - info->u = (void *) buf; - if (info->u->bLength != sizeof(*info->u)) { - dev_dbg(&intf->dev, "CDC union len %u\n", - info->u->bLength); - goto bad_desc; - } - - /* 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 next_desc; - } - 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 next_desc; - - /* 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; - } - break; - case USB_CDC_ETHERNET_TYPE: - if (info->ether) { - dev_dbg(&intf->dev, "extra CDC ether\n"); - goto bad_desc; - } - info->ether = (void *) buf; - if (info->ether->bLength != sizeof(*info->ether)) { - dev_dbg(&intf->dev, "CDC ether len %u\n", - info->ether->bLength); - goto bad_desc; - } - dev->hard_mtu = le16_to_cpu( - info->ether->wMaxSegmentSize); - /* because of Zaurus, we may be ignoring the host - * side link address we were given. - */ - break; - case USB_CDC_MDLM_TYPE: - if (desc) { - dev_dbg(&intf->dev, "extra MDLM descriptor\n"); - goto bad_desc; - } - - desc = (void *)buf; - - if (desc->bLength != sizeof(*desc)) - goto bad_desc; - - if (memcmp(&desc->bGUID, mbm_guid, 16)) - goto bad_desc; - break; - case USB_CDC_MDLM_DETAIL_TYPE: - if (detail) { - dev_dbg(&intf->dev, "extra MDLM detail descriptor\n"); - goto bad_desc; - } - - detail = (void *)buf; - - if (detail->bGuidDescriptorType == 0) { - if (detail->bLength < (sizeof(*detail) + 1)) - goto bad_desc; - } else - goto bad_desc; - break; + + cdc_parse_cdc_header(&header, intf, buf, len); + + info->u = header.usb_cdc_union_desc; + info->header = header.usb_cdc_header_desc; + info->ether = header.usb_cdc_ether_desc; + /* we need a master/control interface (what we're + * probed with) and a slave/data interface; union + * descriptors sort this all out. + */ + 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; } -next_desc: - len -= buf[0]; /* bLength */ - buf += buf[0]; + 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: + if ( rndis && + header.usb_cdc_acm_descriptor && + header.usb_cdc_acm_descriptor->bmCapabilities) { + dev_dbg(&intf->dev, + "ACM capabilities %02x, not really RNDIS?\n", + header.usb_cdc_acm_descriptor->bmCapabilities); + goto bad_desc; + } + + if (header.usb_cdc_ether_desc) { + dev->hard_mtu = le16_to_cpu(info->ether->wMaxSegmentSize); + /* because of Zaurus, we may be ignoring the host + * side link address we were given. + */ + } + + if (header.usb_cdc_mdlm_desc && + memcmp(header.usb_cdc_mdlm_desc->bGUID, mbm_guid, 16)) { + dev_dbg(&intf->dev, "GUID doesn't match\n"); + goto bad_desc; } + if (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. @@ -764,6 +696,11 @@ static const struct usb_device_id products[] = { USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), .driver_info = (kernel_ulong_t) &wwan_info, }, { + /* Dell DW5580 modules */ + USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x81ba, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = (kernel_ulong_t)&wwan_info, +}, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index efc18e05a..bdd83d95e 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -100,7 +100,7 @@ static const struct net_device_ops cdc_mbim_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, - .ndo_change_mtu = usbnet_change_mtu, + .ndo_change_mtu = cdc_ncm_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_add_vid = cdc_mbim_rx_add_vid, @@ -158,7 +158,7 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) goto err; - ret = cdc_ncm_bind_common(dev, intf, data_altsetting, 0); + ret = cdc_ncm_bind_common(dev, intf, data_altsetting, dev->driver_info->data); if (ret) goto err; @@ -342,7 +342,7 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) in6_dev_put(in6_dev); /* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */ - ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target, + ipv6_stub->ndisc_send_na(netdev, &iph->saddr, &msg->target, is_router /* router */, true /* solicited */, false /* override */, @@ -582,6 +582,26 @@ static const struct driver_info cdc_mbim_info_zlp = { .tx_fixup = cdc_mbim_tx_fixup, }; +/* The spefication explicitly allows NDPs to be placed anywhere in the + * frame, but some devices fail unless the NDP is placed after the IP + * packets. Using the CDC_NCM_FLAG_NDP_TO_END flags to force this + * behaviour. + * + * Note: The current implementation of this feature restricts each NTB + * to a single NDP, implying that multiplexed sessions cannot share an + * NTB. This might affect performace for multiplexed sessions. + */ +static const struct driver_info cdc_mbim_info_ndp_to_end = { + .description = "CDC MBIM", + .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, + .bind = cdc_mbim_bind, + .unbind = cdc_mbim_unbind, + .manage_power = cdc_mbim_manage_power, + .rx_fixup = cdc_mbim_rx_fixup, + .tx_fixup = cdc_mbim_tx_fixup, + .data = CDC_NCM_FLAG_NDP_TO_END, +}; + static const struct usb_device_id mbim_devs[] = { /* This duplicate NCM entry is intentional. MBIM devices can * be disguised as NCM by default, and this is necessary to @@ -597,6 +617,10 @@ static const struct usb_device_id mbim_devs[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x0bdb, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info, }, + /* Huawei E3372 fails unless NDP comes after the IP packets */ + { USB_DEVICE_AND_INTERFACE_INFO(0x12d1, 0x157d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&cdc_mbim_info_ndp_to_end, + }, /* default entry */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info_zlp, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index db40175b1..e8a1144c5 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -41,6 +41,7 @@ #include <linux/module.h> #include <linux/netdevice.h> #include <linux/ctype.h> +#include <linux/etherdevice.h> #include <linux/ethtool.h> #include <linux/workqueue.h> #include <linux/mii.h> @@ -689,15 +690,42 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) kfree(ctx); } +/* we need to override the usbnet change_mtu ndo for two reasons: + * - respect the negotiated maximum datagram size + * - avoid unwanted changes to rx and tx buffers + */ +int cdc_ncm_change_mtu(struct net_device *net, int new_mtu) +{ + struct usbnet *dev = netdev_priv(net); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + int maxmtu = ctx->max_datagram_size - cdc_ncm_eth_hlen(dev); + + if (new_mtu <= 0 || new_mtu > maxmtu) + return -EINVAL; + net->mtu = new_mtu; + return 0; +} +EXPORT_SYMBOL_GPL(cdc_ncm_change_mtu); + +static const struct net_device_ops cdc_ncm_netdev_ops = { + .ndo_open = usbnet_open, + .ndo_stop = usbnet_stop, + .ndo_start_xmit = usbnet_start_xmit, + .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_change_mtu = cdc_ncm_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags) { - const struct usb_cdc_union_desc *union_desc = NULL; struct cdc_ncm_ctx *ctx; struct usb_driver *driver; u8 *buf; int len; int temp; u8 iface_no; + struct usb_cdc_parsed_header hdr; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -722,69 +750,18 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ len = intf->cur_altsetting->extralen; /* parse through descriptors associated with control interface */ - while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) { - - if (buf[1] != USB_DT_CS_INTERFACE) - goto advance; - - switch (buf[2]) { - case USB_CDC_UNION_TYPE: - if (buf[0] < sizeof(*union_desc)) - break; - - union_desc = (const struct usb_cdc_union_desc *)buf; - /* the master must be the interface we are probing */ - if (intf->cur_altsetting->desc.bInterfaceNumber != - union_desc->bMasterInterface0) { - dev_dbg(&intf->dev, "bogus CDC Union\n"); - goto error; - } - ctx->data = usb_ifnum_to_if(dev->udev, - union_desc->bSlaveInterface0); - break; - - case USB_CDC_ETHERNET_TYPE: - if (buf[0] < sizeof(*(ctx->ether_desc))) - break; - - ctx->ether_desc = - (const struct usb_cdc_ether_desc *)buf; - break; - - case USB_CDC_NCM_TYPE: - if (buf[0] < sizeof(*(ctx->func_desc))) - break; - - ctx->func_desc = (const struct usb_cdc_ncm_desc *)buf; - break; - - case USB_CDC_MBIM_TYPE: - if (buf[0] < sizeof(*(ctx->mbim_desc))) - break; - - ctx->mbim_desc = (const struct usb_cdc_mbim_desc *)buf; - break; - - case USB_CDC_MBIM_EXTENDED_TYPE: - if (buf[0] < sizeof(*(ctx->mbim_extended_desc))) - break; + cdc_parse_cdc_header(&hdr, intf, buf, len); - ctx->mbim_extended_desc = - (const struct usb_cdc_mbim_extended_desc *)buf; - break; - - default: - break; - } -advance: - /* advance to next descriptor */ - temp = buf[0]; - buf += temp; - len -= temp; - } + if (hdr.usb_cdc_union_desc) + ctx->data = usb_ifnum_to_if(dev->udev, + hdr.usb_cdc_union_desc->bSlaveInterface0); + ctx->ether_desc = hdr.usb_cdc_ether_desc; + ctx->func_desc = hdr.usb_cdc_ncm_desc; + ctx->mbim_desc = hdr.usb_cdc_mbim_desc; + ctx->mbim_extended_desc = hdr.usb_cdc_mbim_extended_desc; /* some buggy devices have an IAD but no CDC Union */ - if (!union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { + if (!hdr.usb_cdc_union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1); dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n"); } @@ -874,6 +851,9 @@ advance: /* add our sysfs attrs */ dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group; + /* must handle MTU changes */ + dev->net->netdev_ops = &cdc_ncm_netdev_ops; + return 0; error2: @@ -1006,10 +986,18 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ * NTH16 header as we would normally do. NDP isn't written to the SKB yet, and * the wNdpIndex field in the header is actually not consistent with reality. It will be later. */ - if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) + if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { if (ctx->delayed_ndp16->dwSignature == sign) return ctx->delayed_ndp16; + /* We can only push a single NDP to the end. Return + * NULL to send what we've already got and queue this + * skb for later. + */ + else if (ctx->delayed_ndp16->dwSignature) + return NULL; + } + /* follow the chain of NDPs, looking for a match */ while (ndpoffset) { ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset); @@ -1601,6 +1589,24 @@ static const struct usb_device_id cdc_devs[] = { .driver_info = (unsigned long) &wwan_info, }, + /* DW5812 LTE Verizon Mobile Broadband Card + * Unlike DW5550 this device requires FLAG_NOARP + */ + { USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bb, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&wwan_noarp_info, + }, + + /* DW5813 LTE AT&T Mobile Broadband Card + * Unlike DW5550 this device requires FLAG_NOARP + */ + { USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bc, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&wwan_noarp_info, + }, + /* Dell branded MBM devices like DW5550 */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_VENDOR, diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 6e9c344c7..0b4bdd391 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -258,7 +258,6 @@ static void dm9601_get_drvinfo(struct net_device *net, { /* Inherit standard device info */ usbnet_get_drvinfo(net, info); - info->eedump_len = DM_EEPROM_LEN; } static u32 dm9601_get_link(struct net_device *net) diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index 2b231579d..b0f65bcfa 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -1174,12 +1174,6 @@ err_fw: INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl); usb_set_intfdata(intf, kaweth); -#if 0 -// dma_supported() is deeply broken on almost all architectures - if (dma_supported (dev, 0xffffffffffffffffULL)) - kaweth->net->features |= NETIF_F_HIGHDMA; -#endif - SET_NETDEV_DEV(netdev, dev); if (register_netdev(netdev) != 0) { dev_err(dev, "Error registering netdev.\n"); diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index a39518fc9..226668ead 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -19,7 +19,6 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> -#include <linux/mii.h> #include <linux/usb.h> #include <linux/crc32.h> #include <linux/signal.h> @@ -31,12 +30,13 @@ #include <linux/ipv6.h> #include <linux/mdio.h> #include <net/ip6_checksum.h> +#include <linux/microchipphy.h> #include "lan78xx.h" #define DRIVER_AUTHOR "WOOJUNG HUH <woojung.huh@microchip.com>" #define DRIVER_DESC "LAN78XX USB 3.0 Gigabit Ethernet Devices" #define DRIVER_NAME "lan78xx" -#define DRIVER_VERSION "1.0.0" +#define DRIVER_VERSION "1.0.1" #define TX_TIMEOUT_JIFFIES (5 * HZ) #define THROTTLE_JIFFIES (HZ / 8) @@ -57,7 +57,6 @@ #define DEFAULT_RX_CSUM_ENABLE (true) #define DEFAULT_TSO_CSUM_ENABLE (true) #define DEFAULT_VLAN_FILTER_ENABLE (true) -#define INTERNAL_PHY_ID (2) /* 2: GMII */ #define TX_OVERHEAD (8) #define RXW_PADDING 2 @@ -275,10 +274,12 @@ struct lan78xx_net { struct timer_list delay; unsigned long data[5]; - struct mii_if_info mii; int link_on; u8 mdix_ctrl; + + u32 devid; + struct mii_bus *mdiobus; }; /* use ethtool to change the level for any given device */ @@ -411,222 +412,6 @@ static inline u32 mii_access(int id, int index, int read) return ret; } -static int lan78xx_mdio_read(struct net_device *netdev, int phy_id, int idx) -{ - struct lan78xx_net *dev = netdev_priv(netdev); - u32 val, addr; - int ret; - - ret = usb_autopm_get_interface(dev->intf); - if (ret < 0) - return ret; - - mutex_lock(&dev->phy_mutex); - - /* confirm MII not busy */ - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* set the address, index & direction (read from PHY) */ - phy_id &= dev->mii.phy_id_mask; - idx &= dev->mii.reg_num_mask; - addr = mii_access(phy_id, idx, MII_READ); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - ret = lan78xx_read_reg(dev, MII_DATA, &val); - - ret = (int)(val & 0xFFFF); - -done: - mutex_unlock(&dev->phy_mutex); - usb_autopm_put_interface(dev->intf); - return ret; -} - -static void lan78xx_mdio_write(struct net_device *netdev, int phy_id, - int idx, int regval) -{ - struct lan78xx_net *dev = netdev_priv(netdev); - u32 val, addr; - int ret; - - if (usb_autopm_get_interface(dev->intf) < 0) - return; - - mutex_lock(&dev->phy_mutex); - - /* confirm MII not busy */ - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - val = regval; - ret = lan78xx_write_reg(dev, MII_DATA, val); - - /* set the address, index & direction (write to PHY) */ - phy_id &= dev->mii.phy_id_mask; - idx &= dev->mii.reg_num_mask; - addr = mii_access(phy_id, idx, MII_WRITE); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - -done: - mutex_unlock(&dev->phy_mutex); - usb_autopm_put_interface(dev->intf); -} - -static void lan78xx_mmd_write(struct net_device *netdev, int phy_id, - int mmddev, int mmdidx, int regval) -{ - struct lan78xx_net *dev = netdev_priv(netdev); - u32 val, addr; - int ret; - - if (usb_autopm_get_interface(dev->intf) < 0) - return; - - mutex_lock(&dev->phy_mutex); - - /* confirm MII not busy */ - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - mmddev &= 0x1F; - - /* set up device address for MMD */ - ret = lan78xx_write_reg(dev, MII_DATA, mmddev); - - phy_id &= dev->mii.phy_id_mask; - addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* select register of MMD */ - val = mmdidx; - ret = lan78xx_write_reg(dev, MII_DATA, val); - - phy_id &= dev->mii.phy_id_mask; - addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* select register data for MMD */ - val = PHY_MMD_CTRL_OP_DNI_ | mmddev; - ret = lan78xx_write_reg(dev, MII_DATA, val); - - phy_id &= dev->mii.phy_id_mask; - addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* write to MMD */ - val = regval; - ret = lan78xx_write_reg(dev, MII_DATA, val); - - phy_id &= dev->mii.phy_id_mask; - addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - -done: - mutex_unlock(&dev->phy_mutex); - usb_autopm_put_interface(dev->intf); -} - -static int lan78xx_mmd_read(struct net_device *netdev, int phy_id, - int mmddev, int mmdidx) -{ - struct lan78xx_net *dev = netdev_priv(netdev); - u32 val, addr; - int ret; - - ret = usb_autopm_get_interface(dev->intf); - if (ret < 0) - return ret; - - mutex_lock(&dev->phy_mutex); - - /* confirm MII not busy */ - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* set up device address for MMD */ - ret = lan78xx_write_reg(dev, MII_DATA, mmddev); - - phy_id &= dev->mii.phy_id_mask; - addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* select register of MMD */ - val = mmdidx; - ret = lan78xx_write_reg(dev, MII_DATA, val); - - phy_id &= dev->mii.phy_id_mask; - addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_WRITE); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* select register data for MMD */ - val = PHY_MMD_CTRL_OP_DNI_ | mmddev; - ret = lan78xx_write_reg(dev, MII_DATA, val); - - phy_id &= dev->mii.phy_id_mask; - addr = mii_access(phy_id, PHY_MMD_CTL, MII_WRITE); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* set the address, index & direction (read from PHY) */ - phy_id &= dev->mii.phy_id_mask; - addr = mii_access(phy_id, PHY_MMD_REG_DATA, MII_READ); - ret = lan78xx_write_reg(dev, MII_ACC, addr); - - ret = lan78xx_phy_wait_not_busy(dev); - if (ret < 0) - goto done; - - /* read from MMD */ - ret = lan78xx_read_reg(dev, MII_DATA, &val); - - ret = (int)(val & 0xFFFF); - -done: - mutex_unlock(&dev->phy_mutex); - usb_autopm_put_interface(dev->intf); - return ret; -} - static int lan78xx_wait_eeprom(struct lan78xx_net *dev) { unsigned long start_time = jiffies; @@ -1047,14 +832,13 @@ static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex, static int lan78xx_link_reset(struct lan78xx_net *dev) { - struct mii_if_info *mii = &dev->mii; + struct phy_device *phydev = dev->net->phydev; struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; int ladv, radv, ret; u32 buf; /* clear PHY interrupt status */ - /* VTSE PHY */ - ret = lan78xx_mdio_read(dev->net, mii->phy_id, PHY_VTSE_INT_STS); + ret = phy_read(phydev, LAN88XX_INT_STS); if (unlikely(ret < 0)) return -EIO; @@ -1063,7 +847,9 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) if (unlikely(ret < 0)) return -EIO; - if (!mii_link_ok(mii) && dev->link_on) { + phy_read_status(phydev); + + if (!phydev->link && dev->link_on) { dev->link_on = false; netif_carrier_off(dev->net); @@ -1075,13 +861,12 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) ret = lan78xx_write_reg(dev, MAC_CR, buf); if (unlikely(ret < 0)) return -EIO; - } else if (mii_link_ok(mii) && !dev->link_on) { + } else if (phydev->link && !dev->link_on) { dev->link_on = true; - mii_check_media(mii, 1, 1); - mii_ethtool_gset(&dev->mii, &ecmd); + phy_ethtool_gset(phydev, &ecmd); - mii->mdio_read(mii->dev, mii->phy_id, PHY_VTSE_INT_STS); + ret = phy_read(phydev, LAN88XX_INT_STS); if (dev->udev->speed == USB_SPEED_SUPER) { if (ethtool_cmd_speed(&ecmd) == 1000) { @@ -1102,11 +887,11 @@ static int lan78xx_link_reset(struct lan78xx_net *dev) } } - ladv = lan78xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); + ladv = phy_read(phydev, MII_ADVERTISE); if (ladv < 0) return ladv; - radv = lan78xx_mdio_read(dev->net, mii->phy_id, MII_LPA); + radv = phy_read(phydev, MII_LPA); if (radv < 0) return radv; @@ -1279,6 +1064,8 @@ static int lan78xx_set_wol(struct net_device *netdev, device_set_wakeup_enable(&dev->udev->dev, (bool)wol->wolopts); + phy_ethtool_set_wol(netdev->phydev, wol); + usb_autopm_put_interface(dev->intf); return ret; @@ -1287,49 +1074,39 @@ static int lan78xx_set_wol(struct net_device *netdev, static int lan78xx_get_eee(struct net_device *net, struct ethtool_eee *edata) { struct lan78xx_net *dev = netdev_priv(net); + struct phy_device *phydev = net->phydev; int ret; u32 buf; - u32 adv, lpadv; ret = usb_autopm_get_interface(dev->intf); if (ret < 0) return ret; + ret = phy_ethtool_get_eee(phydev, edata); + if (ret < 0) + goto exit; + ret = lan78xx_read_reg(dev, MAC_CR, &buf); if (buf & MAC_CR_EEE_EN_) { - buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id, - PHY_MMD_DEV_7, PHY_EEE_ADVERTISEMENT); - adv = mmd_eee_adv_to_ethtool_adv_t(buf); - buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id, - PHY_MMD_DEV_7, PHY_EEE_LP_ADVERTISEMENT); - lpadv = mmd_eee_adv_to_ethtool_adv_t(buf); - edata->eee_enabled = true; - edata->supported = true; - edata->eee_active = !!(adv & lpadv); - edata->advertised = adv; - edata->lp_advertised = lpadv; + edata->eee_active = !!(edata->advertised & + edata->lp_advertised); edata->tx_lpi_enabled = true; /* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */ ret = lan78xx_read_reg(dev, EEE_TX_LPI_REQ_DLY, &buf); edata->tx_lpi_timer = buf; } else { - buf = lan78xx_mmd_read(dev->net, dev->mii.phy_id, - PHY_MMD_DEV_7, PHY_EEE_LP_ADVERTISEMENT); - lpadv = mmd_eee_adv_to_ethtool_adv_t(buf); - edata->eee_enabled = false; edata->eee_active = false; - edata->supported = false; - edata->advertised = 0; - edata->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(lpadv); edata->tx_lpi_enabled = false; edata->tx_lpi_timer = 0; } + ret = 0; +exit: usb_autopm_put_interface(dev->intf); - return 0; + return ret; } static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata) @@ -1347,9 +1124,10 @@ static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata) buf |= MAC_CR_EEE_EN_; ret = lan78xx_write_reg(dev, MAC_CR, buf); - buf = ethtool_adv_to_mmd_eee_adv_t(edata->advertised); - lan78xx_mmd_write(dev->net, dev->mii.phy_id, - PHY_MMD_DEV_7, PHY_EEE_ADVERTISEMENT, buf); + phy_ethtool_set_eee(net->phydev, edata); + + buf = (u32)edata->tx_lpi_timer; + ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, buf); } else { ret = lan78xx_read_reg(dev, MAC_CR, &buf); buf &= ~MAC_CR_EEE_EN_; @@ -1363,19 +1141,14 @@ static int lan78xx_set_eee(struct net_device *net, struct ethtool_eee *edata) static u32 lan78xx_get_link(struct net_device *net) { - struct lan78xx_net *dev = netdev_priv(net); + phy_read_status(net->phydev); - return mii_link_ok(&dev->mii); + return net->phydev->link; } int lan78xx_nway_reset(struct net_device *net) { - struct lan78xx_net *dev = netdev_priv(net); - - if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write)) - return -EOPNOTSUPP; - - return mii_nway_restart(&dev->mii); + return phy_start_aneg(net->phydev); } static void lan78xx_get_drvinfo(struct net_device *net, @@ -1402,36 +1175,78 @@ static void lan78xx_set_msglevel(struct net_device *net, u32 level) dev->msg_enable = level; } +static int lan78xx_get_mdix_status(struct net_device *net) +{ + struct phy_device *phydev = net->phydev; + int buf; + + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1); + buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0); + + return buf; +} + +static void lan78xx_set_mdix_status(struct net_device *net, __u8 mdix_ctrl) +{ + struct lan78xx_net *dev = netdev_priv(net); + struct phy_device *phydev = net->phydev; + int buf; + + if (mdix_ctrl == ETH_TP_MDI) { + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, + LAN88XX_EXT_PAGE_SPACE_1); + buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); + buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; + phy_write(phydev, LAN88XX_EXT_MODE_CTRL, + buf | LAN88XX_EXT_MODE_CTRL_MDI_); + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, + LAN88XX_EXT_PAGE_SPACE_0); + } else if (mdix_ctrl == ETH_TP_MDI_X) { + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, + LAN88XX_EXT_PAGE_SPACE_1); + buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); + buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; + phy_write(phydev, LAN88XX_EXT_MODE_CTRL, + buf | LAN88XX_EXT_MODE_CTRL_MDI_X_); + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, + LAN88XX_EXT_PAGE_SPACE_0); + } else if (mdix_ctrl == ETH_TP_MDI_AUTO) { + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, + LAN88XX_EXT_PAGE_SPACE_1); + buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL); + buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; + phy_write(phydev, LAN88XX_EXT_MODE_CTRL, + buf | LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_); + phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, + LAN88XX_EXT_PAGE_SPACE_0); + } + dev->mdix_ctrl = mdix_ctrl; +} + static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd) { struct lan78xx_net *dev = netdev_priv(net); - struct mii_if_info *mii = &dev->mii; + struct phy_device *phydev = net->phydev; int ret; int buf; - if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write)) - return -EOPNOTSUPP; - ret = usb_autopm_get_interface(dev->intf); if (ret < 0) return ret; - ret = mii_ethtool_gset(&dev->mii, cmd); + ret = phy_ethtool_gset(phydev, cmd); - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_1); - buf = mii->mdio_read(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL); - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_0); + buf = lan78xx_get_mdix_status(net); - buf &= PHY_EXT_MODE_CTRL_MDIX_MASK_; - if (buf == PHY_EXT_MODE_CTRL_AUTO_MDIX_) { + buf &= LAN88XX_EXT_MODE_CTRL_MDIX_MASK_; + if (buf == LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_) { cmd->eth_tp_mdix = ETH_TP_MDI_AUTO; cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; - } else if (buf == PHY_EXT_MODE_CTRL_MDI_) { + } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_) { cmd->eth_tp_mdix = ETH_TP_MDI; cmd->eth_tp_mdix_ctrl = ETH_TP_MDI; - } else if (buf == PHY_EXT_MODE_CTRL_MDI_X_) { + } else if (buf == LAN88XX_EXT_MODE_CTRL_MDI_X_) { cmd->eth_tp_mdix = ETH_TP_MDI_X; cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X; } @@ -1444,70 +1259,27 @@ static int lan78xx_get_settings(struct net_device *net, struct ethtool_cmd *cmd) static int lan78xx_set_settings(struct net_device *net, struct ethtool_cmd *cmd) { struct lan78xx_net *dev = netdev_priv(net); - struct mii_if_info *mii = &dev->mii; + struct phy_device *phydev = net->phydev; int ret = 0; int temp; - if ((!dev->mii.mdio_read) || (!dev->mii.mdio_write)) - return -EOPNOTSUPP; - ret = usb_autopm_get_interface(dev->intf); if (ret < 0) return ret; if (dev->mdix_ctrl != cmd->eth_tp_mdix_ctrl) { - if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI) { - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, - PHY_EXT_GPIO_PAGE_SPACE_1); - temp = mii->mdio_read(mii->dev, mii->phy_id, - PHY_EXT_MODE_CTRL); - temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_; - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_MODE_CTRL, - temp | PHY_EXT_MODE_CTRL_MDI_); - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, - PHY_EXT_GPIO_PAGE_SPACE_0); - } else if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI_X) { - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, - PHY_EXT_GPIO_PAGE_SPACE_1); - temp = mii->mdio_read(mii->dev, mii->phy_id, - PHY_EXT_MODE_CTRL); - temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_; - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_MODE_CTRL, - temp | PHY_EXT_MODE_CTRL_MDI_X_); - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, - PHY_EXT_GPIO_PAGE_SPACE_0); - } else if (cmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) { - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, - PHY_EXT_GPIO_PAGE_SPACE_1); - temp = mii->mdio_read(mii->dev, mii->phy_id, - PHY_EXT_MODE_CTRL); - temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_; - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_MODE_CTRL, - temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_); - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, - PHY_EXT_GPIO_PAGE_SPACE_0); - } + lan78xx_set_mdix_status(net, cmd->eth_tp_mdix_ctrl); } /* change speed & duplex */ - ret = mii_ethtool_sset(&dev->mii, cmd); + ret = phy_ethtool_sset(phydev, cmd); if (!cmd->autoneg) { /* force link down */ - temp = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR); - mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, - temp | BMCR_LOOPBACK); + temp = phy_read(phydev, MII_BMCR); + phy_write(phydev, MII_BMCR, temp | BMCR_LOOPBACK); mdelay(1); - mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, temp); + phy_write(phydev, MII_BMCR, temp); } usb_autopm_put_interface(dev->intf); @@ -1537,12 +1309,10 @@ static const struct ethtool_ops lan78xx_ethtool_ops = { static int lan78xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) { - struct lan78xx_net *dev = netdev_priv(netdev); - if (!netif_running(netdev)) return -EINVAL; - return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); + return phy_mii_ioctl(netdev->phydev, rq, cmd); } static void lan78xx_init_mac_address(struct lan78xx_net *dev) @@ -1598,53 +1368,183 @@ static void lan78xx_init_mac_address(struct lan78xx_net *dev) ether_addr_copy(dev->net->dev_addr, addr); } -static void lan78xx_mii_init(struct lan78xx_net *dev) +/* MDIO read and write wrappers for phylib */ +static int lan78xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx) { - /* Initialize MII structure */ - dev->mii.dev = dev->net; - dev->mii.mdio_read = lan78xx_mdio_read; - dev->mii.mdio_write = lan78xx_mdio_write; - dev->mii.phy_id_mask = 0x1f; - dev->mii.reg_num_mask = 0x1f; - dev->mii.phy_id = INTERNAL_PHY_ID; - dev->mii.supports_gmii = true; + struct lan78xx_net *dev = bus->priv; + u32 val, addr; + int ret; + + ret = usb_autopm_get_interface(dev->intf); + if (ret < 0) + return ret; + + mutex_lock(&dev->phy_mutex); + + /* confirm MII not busy */ + ret = lan78xx_phy_wait_not_busy(dev); + if (ret < 0) + goto done; + + /* set the address, index & direction (read from PHY) */ + addr = mii_access(phy_id, idx, MII_READ); + ret = lan78xx_write_reg(dev, MII_ACC, addr); + + ret = lan78xx_phy_wait_not_busy(dev); + if (ret < 0) + goto done; + + ret = lan78xx_read_reg(dev, MII_DATA, &val); + + ret = (int)(val & 0xFFFF); + +done: + mutex_unlock(&dev->phy_mutex); + usb_autopm_put_interface(dev->intf); + return ret; +} + +static int lan78xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx, + u16 regval) +{ + struct lan78xx_net *dev = bus->priv; + u32 val, addr; + int ret; + + ret = usb_autopm_get_interface(dev->intf); + if (ret < 0) + return ret; + + mutex_lock(&dev->phy_mutex); + + /* confirm MII not busy */ + ret = lan78xx_phy_wait_not_busy(dev); + if (ret < 0) + goto done; + + val = (u32)regval; + ret = lan78xx_write_reg(dev, MII_DATA, val); + + /* set the address, index & direction (write to PHY) */ + addr = mii_access(phy_id, idx, MII_WRITE); + ret = lan78xx_write_reg(dev, MII_ACC, addr); + + ret = lan78xx_phy_wait_not_busy(dev); + if (ret < 0) + goto done; + +done: + mutex_unlock(&dev->phy_mutex); + usb_autopm_put_interface(dev->intf); + return 0; +} + +static int lan78xx_mdio_init(struct lan78xx_net *dev) +{ + int ret; + int i; + + dev->mdiobus = mdiobus_alloc(); + if (!dev->mdiobus) { + netdev_err(dev->net, "can't allocate MDIO bus\n"); + return -ENOMEM; + } + + dev->mdiobus->priv = (void *)dev; + dev->mdiobus->read = lan78xx_mdiobus_read; + dev->mdiobus->write = lan78xx_mdiobus_write; + dev->mdiobus->name = "lan78xx-mdiobus"; + + snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", + dev->udev->bus->busnum, dev->udev->devnum); + + dev->mdiobus->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (!dev->mdiobus->irq) { + ret = -ENOMEM; + goto exit1; + } + + /* handle our own interrupt */ + for (i = 0; i < PHY_MAX_ADDR; i++) + dev->mdiobus->irq[i] = PHY_IGNORE_INTERRUPT; + + switch (dev->devid & ID_REV_CHIP_ID_MASK_) { + case 0x78000000: + case 0x78500000: + /* set to internal PHY id */ + dev->mdiobus->phy_mask = ~(1 << 1); + break; + } + + ret = mdiobus_register(dev->mdiobus); + if (ret) { + netdev_err(dev->net, "can't register MDIO bus\n"); + goto exit2; + } + + netdev_dbg(dev->net, "registered mdiobus bus %s\n", dev->mdiobus->id); + return 0; +exit2: + kfree(dev->mdiobus->irq); +exit1: + mdiobus_free(dev->mdiobus); + return ret; +} + +static void lan78xx_remove_mdio(struct lan78xx_net *dev) +{ + mdiobus_unregister(dev->mdiobus); + kfree(dev->mdiobus->irq); + mdiobus_free(dev->mdiobus); +} + +static void lan78xx_link_status_change(struct net_device *net) +{ + /* nothing to do */ } static int lan78xx_phy_init(struct lan78xx_net *dev) { - int temp; - struct mii_if_info *mii = &dev->mii; + int ret; + struct phy_device *phydev = dev->net->phydev; - if ((!mii->mdio_write) || (!mii->mdio_read)) - return -EOPNOTSUPP; + phydev = phy_find_first(dev->mdiobus); + if (!phydev) { + netdev_err(dev->net, "no PHY found\n"); + return -EIO; + } - temp = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE); - temp |= ADVERTISE_ALL; - mii->mdio_write(mii->dev, mii->phy_id, MII_ADVERTISE, - temp | ADVERTISE_CSMA | - ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + ret = phy_connect_direct(dev->net, phydev, + lan78xx_link_status_change, + PHY_INTERFACE_MODE_GMII); + if (ret) { + netdev_err(dev->net, "can't attach PHY to %s\n", + dev->mdiobus->id); + return -EIO; + } /* set to AUTOMDIX */ - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_1); - temp = mii->mdio_read(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL); - temp &= ~PHY_EXT_MODE_CTRL_MDIX_MASK_; - mii->mdio_write(mii->dev, mii->phy_id, PHY_EXT_MODE_CTRL, - temp | PHY_EXT_MODE_CTRL_AUTO_MDIX_); - mii->mdio_write(mii->dev, mii->phy_id, - PHY_EXT_GPIO_PAGE, PHY_EXT_GPIO_PAGE_SPACE_0); - dev->mdix_ctrl = ETH_TP_MDI_AUTO; - - /* MAC doesn't support 1000HD */ - temp = mii->mdio_read(mii->dev, mii->phy_id, MII_CTRL1000); - mii->mdio_write(mii->dev, mii->phy_id, MII_CTRL1000, - temp & ~ADVERTISE_1000HALF); - - /* clear interrupt */ - mii->mdio_read(mii->dev, mii->phy_id, PHY_VTSE_INT_STS); - mii->mdio_write(mii->dev, mii->phy_id, PHY_VTSE_INT_MASK, - PHY_VTSE_INT_MASK_MDINTPIN_EN_ | - PHY_VTSE_INT_MASK_LINK_CHANGE_); + lan78xx_set_mdix_status(dev->net, ETH_TP_MDI_AUTO); + + /* MAC doesn't support 1000T Half */ + phydev->supported &= ~SUPPORTED_1000baseT_Half; + phydev->supported |= (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_Pause | SUPPORTED_Asym_Pause); + genphy_config_aneg(phydev); + + /* Workaround to enable PHY interrupt. + * phy_start_interrupts() is API for requesting and enabling + * PHY interrupt. However, USB-to-Ethernet device can't use + * request_irq() called in phy_start_interrupts(). + * Set PHY to PHY_HALTED and call phy_start() + * to make a call to phy_enable_interrupts() + */ + phy_stop(phydev); + phy_start(phydev); netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); @@ -1930,6 +1830,10 @@ static int lan78xx_reset(struct lan78xx_net *dev) lan78xx_init_mac_address(dev); + /* save DEVID for later usage */ + ret = lan78xx_read_reg(dev, ID_REV, &buf); + dev->devid = buf; + /* Respond to the IN token with a NAK */ ret = lan78xx_read_reg(dev, USB_CFG0, &buf); buf |= USB_CFG_BIR_; @@ -2002,23 +1906,12 @@ static int lan78xx_reset(struct lan78xx_net *dev) netdev_warn(dev->net, "timeout waiting for PHY Reset"); return -EIO; } - } while (buf & PMT_CTL_PHY_RST_); - - lan78xx_mii_init(dev); - - ret = lan78xx_phy_init(dev); + } while ((buf & PMT_CTL_PHY_RST_) || !(buf & PMT_CTL_READY_)); ret = lan78xx_read_reg(dev, MAC_CR, &buf); - - buf |= MAC_CR_GMII_EN_; buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; - ret = lan78xx_write_reg(dev, MAC_CR, buf); - /* enable on PHY */ - if (buf & MAC_CR_EEE_EN_) - lan78xx_mmd_write(dev->net, dev->mii.phy_id, 0x07, 0x3C, 0x06); - /* enable PHY interrupts */ ret = lan78xx_read_reg(dev, INT_EP_CTL, &buf); buf |= INT_ENP_PHY_INT; @@ -2042,9 +1935,6 @@ static int lan78xx_reset(struct lan78xx_net *dev) buf |= FCT_RX_CTL_EN_; ret = lan78xx_write_reg(dev, FCT_RX_CTL, buf); - if (!mii_nway_restart(&dev->mii)) - netif_dbg(dev, link, dev->net, "autoneg initiated"); - return 0; } @@ -2061,6 +1951,10 @@ static int lan78xx_open(struct net_device *net) if (ret < 0) goto done; + ret = lan78xx_phy_init(dev); + if (ret < 0) + goto done; + /* for Link Check */ if (dev->urb_intr) { ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL); @@ -2115,6 +2009,10 @@ int lan78xx_stop(struct net_device *net) { struct lan78xx_net *dev = netdev_priv(net); + phy_stop(net->phydev); + phy_disconnect(net->phydev); + net->phydev = NULL; + clear_bit(EVENT_DEV_OPEN, &dev->flags); netif_stop_queue(net); @@ -2395,6 +2293,8 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf) /* Init all registers */ ret = lan78xx_reset(dev); + lan78xx_mdio_init(dev); + dev->net->flags |= IFF_MULTICAST; pdata->wol = WAKE_MAGIC; @@ -2406,6 +2306,8 @@ static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf) { struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); + lan78xx_remove_mdio(dev); + if (pdata) { netif_dbg(dev, ifdown, dev->net, "free pdata"); kfree(pdata); @@ -2522,11 +2424,6 @@ static int lan78xx_rx(struct lan78xx_net *dev, struct sk_buff *skb) skb_pull(skb, align_count); } - if (unlikely(skb->len < 0)) { - netdev_warn(dev->net, "invalid rx length<0 %d", skb->len); - return 0; - } - return 1; } @@ -3307,7 +3204,6 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) int ret; int event; - ret = 0; event = message.event; if (!dev->suspend_count++) { @@ -3389,6 +3285,7 @@ int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) } } + ret = 0; out: return ret; } @@ -3459,6 +3356,9 @@ int lan78xx_reset_resume(struct usb_interface *intf) struct lan78xx_net *dev = usb_get_intfdata(intf); lan78xx_reset(dev); + + lan78xx_phy_init(dev); + return lan78xx_resume(intf); } diff --git a/drivers/net/usb/lan78xx.h b/drivers/net/usb/lan78xx.h index ae7562ee7..a93fb653e 100644 --- a/drivers/net/usb/lan78xx.h +++ b/drivers/net/usb/lan78xx.h @@ -549,7 +549,6 @@ #define LTM_INACTIVE1_TIMER10_ (0x0000FFFF) #define MAC_CR (0x100) -#define MAC_CR_GMII_EN_ (0x00080000) #define MAC_CR_EEE_TX_CLK_STOP_EN_ (0x00040000) #define MAC_CR_EEE_EN_ (0x00020000) #define MAC_CR_EEE_TLAR_EN_ (0x00010000) @@ -874,196 +873,4 @@ #define OTP_TPVSR_VAL (OTP_BASE_ADDR + 4 * 0x3A) #define OTP_TPVHR_VAL (OTP_BASE_ADDR + 4 * 0x3B) #define OTP_TPVSA_VAL (OTP_BASE_ADDR + 4 * 0x3C) - -#define PHY_ID1 (0x02) -#define PHY_ID2 (0x03) - -#define PHY_DEV_ID_OUI_VTSE (0x04001C) -#define PHY_DEV_ID_MODEL_VTSE_8502 (0x23) - -#define PHY_AUTONEG_ADV (0x04) -#define NWAY_AR_NEXT_PAGE_ (0x8000) -#define NWAY_AR_REMOTE_FAULT_ (0x2000) -#define NWAY_AR_ASM_DIR_ (0x0800) -#define NWAY_AR_PAUSE_ (0x0400) -#define NWAY_AR_100T4_CAPS_ (0x0200) -#define NWAY_AR_100TX_FD_CAPS_ (0x0100) -#define NWAY_AR_SELECTOR_FIELD_ (0x001F) -#define NWAY_AR_100TX_HD_CAPS_ (0x0080) -#define NWAY_AR_10T_FD_CAPS_ (0x0040) -#define NWAY_AR_10T_HD_CAPS_ (0x0020) -#define NWAY_AR_ALL_CAPS_ (NWAY_AR_10T_HD_CAPS_ | \ - NWAY_AR_10T_FD_CAPS_ | \ - NWAY_AR_100TX_HD_CAPS_ | \ - NWAY_AR_100TX_FD_CAPS_) -#define NWAY_AR_PAUSE_MASK (NWAY_AR_PAUSE_ | NWAY_AR_ASM_DIR_) - -#define PHY_LP_ABILITY (0x05) -#define NWAY_LPAR_NEXT_PAGE_ (0x8000) -#define NWAY_LPAR_ACKNOWLEDGE_ (0x4000) -#define NWAY_LPAR_REMOTE_FAULT_ (0x2000) -#define NWAY_LPAR_ASM_DIR_ (0x0800) -#define NWAY_LPAR_PAUSE_ (0x0400) -#define NWAY_LPAR_100T4_CAPS_ (0x0200) -#define NWAY_LPAR_100TX_FD_CAPS_ (0x0100) -#define NWAY_LPAR_100TX_HD_CAPS_ (0x0080) -#define NWAY_LPAR_10T_FD_CAPS_ (0x0040) -#define NWAY_LPAR_10T_HD_CAPS_ (0x0020) -#define NWAY_LPAR_SELECTOR_FIELD_ (0x001F) - -#define PHY_AUTONEG_EXP (0x06) -#define NWAY_ER_PAR_DETECT_FAULT_ (0x0010) -#define NWAY_ER_LP_NEXT_PAGE_CAPS_ (0x0008) -#define NWAY_ER_NEXT_PAGE_CAPS_ (0x0004) -#define NWAY_ER_PAGE_RXD_ (0x0002) -#define NWAY_ER_LP_NWAY_CAPS_ (0x0001) - -#define PHY_NEXT_PAGE_TX (0x07) -#define NPTX_NEXT_PAGE_ (0x8000) -#define NPTX_MSG_PAGE_ (0x2000) -#define NPTX_ACKNOWLDGE2_ (0x1000) -#define NPTX_TOGGLE_ (0x0800) -#define NPTX_MSG_CODE_FIELD_ (0x0001) - -#define PHY_LP_NEXT_PAGE (0x08) -#define LP_RNPR_NEXT_PAGE_ (0x8000) -#define LP_RNPR_ACKNOWLDGE_ (0x4000) -#define LP_RNPR_MSG_PAGE_ (0x2000) -#define LP_RNPR_ACKNOWLDGE2_ (0x1000) -#define LP_RNPR_TOGGLE_ (0x0800) -#define LP_RNPR_MSG_CODE_FIELD_ (0x0001) - -#define PHY_1000T_CTRL (0x09) -#define CR_1000T_TEST_MODE_4_ (0x8000) -#define CR_1000T_TEST_MODE_3_ (0x6000) -#define CR_1000T_TEST_MODE_2_ (0x4000) -#define CR_1000T_TEST_MODE_1_ (0x2000) -#define CR_1000T_MS_ENABLE_ (0x1000) -#define CR_1000T_MS_VALUE_ (0x0800) -#define CR_1000T_REPEATER_DTE_ (0x0400) -#define CR_1000T_FD_CAPS_ (0x0200) -#define CR_1000T_HD_CAPS_ (0x0100) -#define CR_1000T_ASYM_PAUSE_ (0x0080) -#define CR_1000T_TEST_MODE_NORMAL_ (0x0000) - -#define PHY_1000T_STATUS (0x0A) -#define SR_1000T_MS_CONFIG_FAULT_ (0x8000) -#define SR_1000T_MS_CONFIG_RES_ (0x4000) -#define SR_1000T_LOCAL_RX_STATUS_ (0x2000) -#define SR_1000T_REMOTE_RX_STATUS_ (0x1000) -#define SR_1000T_LP_FD_CAPS_ (0x0800) -#define SR_1000T_LP_HD_CAPS_ (0x0400) -#define SR_1000T_ASYM_PAUSE_DIR_ (0x0100) -#define SR_1000T_IDLE_ERROR_CNT_ (0x00FF) -#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 -#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 -#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 -#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 -#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 - -#define PHY_EXT_STATUS (0x0F) -#define IEEE_ESR_1000X_FD_CAPS_ (0x8000) -#define IEEE_ESR_1000X_HD_CAPS_ (0x4000) -#define IEEE_ESR_1000T_FD_CAPS_ (0x2000) -#define IEEE_ESR_1000T_HD_CAPS_ (0x1000) -#define PHY_TX_POLARITY_MASK_ (0x0100) -#define PHY_TX_NORMAL_POLARITY_ (0x0000) -#define AUTO_POLARITY_DISABLE_ (0x0010) - -#define PHY_MMD_CTL (0x0D) -#define PHY_MMD_CTRL_OP_MASK_ (0xC000) -#define PHY_MMD_CTRL_OP_REG_ (0x0000) -#define PHY_MMD_CTRL_OP_DNI_ (0x4000) -#define PHY_MMD_CTRL_OP_DPIRW_ (0x8000) -#define PHY_MMD_CTRL_OP_DPIWO_ (0xC000) -#define PHY_MMD_CTRL_DEV_ADDR_MASK_ (0x001F) - -#define PHY_MMD_REG_DATA (0x0E) - -/* VTSE Vendor Specific registers */ -#define PHY_VTSE_BYPASS (0x12) -#define PHY_VTSE_BYPASS_DISABLE_PAIR_SWAP_ (0x0020) - -#define PHY_VTSE_INT_MASK (0x19) -#define PHY_VTSE_INT_MASK_MDINTPIN_EN_ (0x8000) -#define PHY_VTSE_INT_MASK_SPEED_CHANGE_ (0x4000) -#define PHY_VTSE_INT_MASK_LINK_CHANGE_ (0x2000) -#define PHY_VTSE_INT_MASK_FDX_CHANGE_ (0x1000) -#define PHY_VTSE_INT_MASK_AUTONEG_ERR_ (0x0800) -#define PHY_VTSE_INT_MASK_AUTONEG_DONE_ (0x0400) -#define PHY_VTSE_INT_MASK_POE_DETECT_ (0x0200) -#define PHY_VTSE_INT_MASK_SYMBOL_ERR_ (0x0100) -#define PHY_VTSE_INT_MASK_FAST_LINK_FAIL_ (0x0080) -#define PHY_VTSE_INT_MASK_WOL_EVENT_ (0x0040) -#define PHY_VTSE_INT_MASK_EXTENDED_INT_ (0x0020) -#define PHY_VTSE_INT_MASK_RESERVED_ (0x0010) -#define PHY_VTSE_INT_MASK_FALSE_CARRIER_ (0x0008) -#define PHY_VTSE_INT_MASK_LINK_SPEED_DS_ (0x0004) -#define PHY_VTSE_INT_MASK_MASTER_SLAVE_DONE_ (0x0002) -#define PHY_VTSE_INT_MASK_RX__ER_ (0x0001) - -#define PHY_VTSE_INT_STS (0x1A) -#define PHY_VTSE_INT_STS_INT_ACTIVE_ (0x8000) -#define PHY_VTSE_INT_STS_SPEED_CHANGE_ (0x4000) -#define PHY_VTSE_INT_STS_LINK_CHANGE_ (0x2000) -#define PHY_VTSE_INT_STS_FDX_CHANGE_ (0x1000) -#define PHY_VTSE_INT_STS_AUTONEG_ERR_ (0x0800) -#define PHY_VTSE_INT_STS_AUTONEG_DONE_ (0x0400) -#define PHY_VTSE_INT_STS_POE_DETECT_ (0x0200) -#define PHY_VTSE_INT_STS_SYMBOL_ERR_ (0x0100) -#define PHY_VTSE_INT_STS_FAST_LINK_FAIL_ (0x0080) -#define PHY_VTSE_INT_STS_WOL_EVENT_ (0x0040) -#define PHY_VTSE_INT_STS_EXTENDED_INT_ (0x0020) -#define PHY_VTSE_INT_STS_RESERVED_ (0x0010) -#define PHY_VTSE_INT_STS_FALSE_CARRIER_ (0x0008) -#define PHY_VTSE_INT_STS_LINK_SPEED_DS_ (0x0004) -#define PHY_VTSE_INT_STS_MASTER_SLAVE_DONE_ (0x0002) -#define PHY_VTSE_INT_STS_RX_ER_ (0x0001) - -/* VTSE PHY registers */ -#define PHY_EXT_GPIO_PAGE (0x1F) -#define PHY_EXT_GPIO_PAGE_SPACE_0 (0x0000) -#define PHY_EXT_GPIO_PAGE_SPACE_1 (0x0001) -#define PHY_EXT_GPIO_PAGE_SPACE_2 (0x0002) - -/* Extended Register Page 1 space */ -#define PHY_EXT_MODE_CTRL (0x13) -#define PHY_EXT_MODE_CTRL_MDIX_MASK_ (0x000C) -#define PHY_EXT_MODE_CTRL_AUTO_MDIX_ (0x0000) -#define PHY_EXT_MODE_CTRL_MDI_ (0x0008) -#define PHY_EXT_MODE_CTRL_MDI_X_ (0x000C) - -#define PHY_ANA_10BASE_T_HD 0x01 -#define PHY_ANA_10BASE_T_FD 0x02 -#define PHY_ANA_100BASE_TX_HD 0x04 -#define PHY_ANA_100BASE_TX_FD 0x08 -#define PHY_ANA_1000BASE_T_FD 0x10 -#define PHY_ANA_ALL_SUPPORTED_MEDIA (PHY_ANA_10BASE_T_HD | \ - PHY_ANA_10BASE_T_FD | \ - PHY_ANA_100BASE_TX_HD | \ - PHY_ANA_100BASE_TX_FD | \ - PHY_ANA_1000BASE_T_FD) -/* PHY MMD registers */ -#define PHY_MMD_DEV_3 3 - -#define PHY_EEE_PCS_STATUS (0x1) -#define PHY_EEE_PCS_STATUS_TX_LPI_RCVD_ ((WORD)0x0800) -#define PHY_EEE_PCS_STATUS_RX_LPI_RCVD_ ((WORD)0x0400) -#define PHY_EEE_PCS_STATUS_TX_LPI_IND_ ((WORD)0x0200) -#define PHY_EEE_PCS_STATUS_RX_LPI_IND_ ((WORD)0x0100) -#define PHY_EEE_PCS_STATUS_PCS_RCV_LNK_STS_ ((WORD)0x0004) - -#define PHY_EEE_CAPABILITIES (0x14) -#define PHY_EEE_CAPABILITIES_1000BT_EEE_ ((WORD)0x0004) -#define PHY_EEE_CAPABILITIES_100BT_EEE_ ((WORD)0x0002) - -#define PHY_MMD_DEV_7 7 - -#define PHY_EEE_ADVERTISEMENT (0x3C) -#define PHY_EEE_ADVERTISEMENT_1000BT_EEE_ ((WORD)0x0004) -#define PHY_EEE_ADVERTISEMENT_100BT_EEE_ ((WORD)0x0002) - -#define PHY_EEE_LP_ADVERTISEMENT (0x3D) -#define PHY_EEE_1000BT_EEE_CAPABLE_ ((WORD)0x0004) -#define PHY_EEE_100BT_EEE_CAPABLE_ ((WORD)0x0002) #endif /* _LAN78XX_H */ diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 82d844a8e..4f345bd4e 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -445,7 +445,6 @@ static int mcs7830_get_regs_len(struct net_device *net) static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo) { usbnet_get_drvinfo(net, drvinfo); - drvinfo->regdump_len = mcs7830_get_regs_len(net); } static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 66e0853d1..5fccc5a81 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -229,11 +229,11 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; - struct usb_cdc_union_desc *cdc_union = NULL; - struct usb_cdc_ether_desc *cdc_ether = NULL; - u32 found = 0; + struct usb_cdc_union_desc *cdc_union; + struct usb_cdc_ether_desc *cdc_ether; struct usb_driver *driver = driver_of(intf); struct qmi_wwan_state *info = (void *)&dev->data; + struct usb_cdc_parsed_header hdr; BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); @@ -243,63 +243,9 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) info->data = intf; /* and a number of CDC descriptors */ - while (len > 3) { - struct usb_descriptor_header *h = (void *)buf; - - /* ignore any misplaced descriptors */ - if (h->bDescriptorType != USB_DT_CS_INTERFACE) - goto next_desc; - - /* buf[2] is CDC descriptor subtype */ - switch (buf[2]) { - case USB_CDC_HEADER_TYPE: - if (found & 1 << USB_CDC_HEADER_TYPE) { - dev_dbg(&intf->dev, "extra CDC header\n"); - goto err; - } - if (h->bLength != sizeof(struct usb_cdc_header_desc)) { - dev_dbg(&intf->dev, "CDC header len %u\n", - h->bLength); - goto err; - } - break; - case USB_CDC_UNION_TYPE: - if (found & 1 << USB_CDC_UNION_TYPE) { - dev_dbg(&intf->dev, "extra CDC union\n"); - goto err; - } - if (h->bLength != sizeof(struct usb_cdc_union_desc)) { - dev_dbg(&intf->dev, "CDC union len %u\n", - h->bLength); - goto err; - } - cdc_union = (struct usb_cdc_union_desc *)buf; - break; - case USB_CDC_ETHERNET_TYPE: - if (found & 1 << USB_CDC_ETHERNET_TYPE) { - dev_dbg(&intf->dev, "extra CDC ether\n"); - goto err; - } - if (h->bLength != sizeof(struct usb_cdc_ether_desc)) { - dev_dbg(&intf->dev, "CDC ether len %u\n", - h->bLength); - goto err; - } - cdc_ether = (struct usb_cdc_ether_desc *)buf; - break; - } - - /* Remember which CDC functional descriptors we've seen. Works - * for all types we care about, of which USB_CDC_ETHERNET_TYPE - * (0x0f) is the highest numbered - */ - if (buf[2] < 32) - found |= 1 << buf[2]; - -next_desc: - len -= h->bLength; - buf += h->bLength; - } + cdc_parse_cdc_header(&hdr, intf, buf, len); + cdc_union = hdr.usb_cdc_union_desc; + cdc_ether = hdr.usb_cdc_ether_desc; /* Use separate control and data interfaces if we found a CDC Union */ if (cdc_union) { @@ -539,6 +485,10 @@ static const struct usb_device_id products[] = { USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&qmi_wwan_info, }, + { /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */ + USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), + .driver_info = (unsigned long)&qmi_wwan_info, + }, /* 3. Combined interface devices matching on interface number */ {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ @@ -792,7 +742,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ - {QMI_FIXED_INTF(0x03f0, 0x581d, 4)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */ + {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ @@ -823,6 +773,7 @@ static const struct usb_device_id products[] = { {QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */ {QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */ {QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */ + {QMI_FIXED_INTF(0x05c6, 0x9215, 4)}, /* Quectel EC20 Mini PCIe */ {QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */ {QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */ {QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */ @@ -854,10 +805,24 @@ static const struct usb_device_id products[] = { }; MODULE_DEVICE_TABLE(usb, products); +static bool quectel_ec20_detected(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev(intf); + + if (dev->actconfig && + le16_to_cpu(dev->descriptor.idVendor) == 0x05c6 && + le16_to_cpu(dev->descriptor.idProduct) == 0x9215 && + dev->actconfig->desc.bNumInterfaces == 5) + return true; + + return false; +} + static int qmi_wwan_probe(struct usb_interface *intf, const struct usb_device_id *prod) { struct usb_device_id *id = (struct usb_device_id *)prod; + struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; /* Workaround to enable dynamic IDs. This disables usbnet * blacklisting functionality. Which, if required, can be @@ -869,6 +834,12 @@ static int qmi_wwan_probe(struct usb_interface *intf, id->driver_info = (unsigned long)&qmi_wwan_info; } + /* Quectel EC20 quirk where we've QMI on interface 4 instead of 0 */ + if (quectel_ec20_detected(intf) && desc->bInterfaceNumber == 0) { + dev_dbg(&intf->dev, "Quectel EC20 quirk, skipping interface 0\n"); + return -ENODEV; + } + return usbnet_probe(intf, id); } diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index d9427ca3d..2fb637ad5 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -3067,17 +3067,6 @@ static int rtl8152_open(struct net_device *netdev) mutex_lock(&tp->control); - /* The WORK_ENABLE may be set when autoresume occurs */ - if (test_bit(WORK_ENABLE, &tp->flags)) { - clear_bit(WORK_ENABLE, &tp->flags); - usb_kill_urb(tp->intr_urb); - cancel_delayed_work_sync(&tp->schedule); - - /* disable the tx/rx, if the workqueue has enabled them. */ - if (netif_carrier_ok(netdev)) - tp->rtl_ops.disable(tp); - } - tp->rtl_ops.up(tp); rtl8152_set_speed(tp, AUTONEG_ENABLE, @@ -3124,12 +3113,6 @@ static int rtl8152_close(struct net_device *netdev) } else { mutex_lock(&tp->control); - /* The autosuspend may have been enabled and wouldn't - * be disable when autoresume occurs, because the - * netif_running() would be false. - */ - rtl_runtime_suspend_enable(tp, false); - tp->rtl_ops.down(tp); mutex_unlock(&tp->control); @@ -3512,7 +3495,7 @@ static int rtl8152_resume(struct usb_interface *intf) netif_device_attach(tp->netdev); } - if (netif_running(tp->netdev)) { + if (netif_running(tp->netdev) && tp->netdev->flags & IFF_UP) { if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { rtl_runtime_suspend_enable(tp, false); clear_bit(SELECTIVE_SUSPEND, &tp->flags); @@ -3532,6 +3515,8 @@ static int rtl8152_resume(struct usb_interface *intf) } usb_submit_urb(tp->intr_urb, GFP_KERNEL); } else if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { + if (tp->netdev->flags & IFF_UP) + rtl_runtime_suspend_enable(tp, false); clear_bit(SELECTIVE_SUSPEND, &tp->flags); } @@ -3540,6 +3525,14 @@ static int rtl8152_resume(struct usb_interface *intf) return 0; } +static int rtl8152_reset_resume(struct usb_interface *intf) +{ + struct r8152 *tp = usb_get_intfdata(intf); + + clear_bit(SELECTIVE_SUSPEND, &tp->flags); + return rtl8152_resume(intf); +} + static void rtl8152_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct r8152 *tp = netdev_priv(dev); @@ -4291,7 +4284,7 @@ static struct usb_driver rtl8152_driver = { .disconnect = rtl8152_disconnect, .suspend = rtl8152_suspend, .resume = rtl8152_resume, - .reset_resume = rtl8152_resume, + .reset_resume = rtl8152_reset_resume, .pre_reset = rtl8152_pre_reset, .post_reset = rtl8152_post_reset, .supports_autosuspend = 1, diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index d9e789226..30033dbe6 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -2185,11 +2185,6 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_pull(skb, align_count); } - if (unlikely(skb->len < 0)) { - netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len); - return 0; - } - return 1; } diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 26423adc3..66b3ab9f6 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1815,11 +1815,6 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_pull(skb, align_count); } - if (unlikely(skb->len < 0)) { - netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len); - return 0; - } - return 1; } diff --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c index 953de1326..a50df0d8f 100644 --- a/drivers/net/usb/sr9800.c +++ b/drivers/net/usb/sr9800.c @@ -470,14 +470,10 @@ static int sr_get_eeprom(struct net_device *net, static void sr_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { - struct usbnet *dev = netdev_priv(net); - struct sr_data *data = (struct sr_data *)&dev->data; - /* Inherit standard device info */ usbnet_get_drvinfo(net, info); strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); - info->eedump_len = data->eeprom_len; } static u32 sr_get_link(struct net_device *net) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index b4cf10781..0744bf2ef 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -42,6 +42,7 @@ #include <linux/mii.h> #include <linux/usb.h> #include <linux/usb/usbnet.h> +#include <linux/usb/cdc.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/pm_runtime.h> @@ -1661,12 +1662,6 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) * bind() should set rx_urb_size in that case. */ dev->hard_mtu = net->mtu + net->hard_header_len; -#if 0 -// dma_supported() is deeply broken on almost all architectures - // possible with some EHCI controllers - if (dma_supported (&udev->dev, DMA_BIT_MASK(64))) - net->features |= NETIF_F_HIGHDMA; -#endif net->netdev_ops = &usbnet_netdev_ops; net->watchdog_timeo = TX_TIMEOUT_JIFFIES; @@ -1962,6 +1957,143 @@ out: return err; } +int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, + struct usb_interface *intf, + u8 *buffer, + int buflen) +{ + /* duplicates are ignored */ + struct usb_cdc_union_desc *union_header = NULL; + + /* duplicates are not tolerated */ + struct usb_cdc_header_desc *header = NULL; + struct usb_cdc_ether_desc *ether = NULL; + struct usb_cdc_mdlm_detail_desc *detail = NULL; + struct usb_cdc_mdlm_desc *desc = NULL; + + unsigned int elength; + int cnt = 0; + + memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header)); + hdr->phonet_magic_present = false; + while (buflen > 0) { + elength = buffer[0]; + if (!elength) { + dev_err(&intf->dev, "skipping garbage byte\n"); + elength = 1; + goto next_desc; + } + if (buffer[1] != USB_DT_CS_INTERFACE) { + dev_err(&intf->dev, "skipping garbage\n"); + goto next_desc; + } + + switch (buffer[2]) { + case USB_CDC_UNION_TYPE: /* we've found it */ + if (elength < sizeof(struct usb_cdc_union_desc)) + goto next_desc; + if (union_header) { + dev_err(&intf->dev, "More than one union descriptor, skipping ...\n"); + goto next_desc; + } + union_header = (struct usb_cdc_union_desc *)buffer; + break; + case USB_CDC_COUNTRY_TYPE: + if (elength < sizeof(struct usb_cdc_country_functional_desc)) + goto next_desc; + hdr->usb_cdc_country_functional_desc = + (struct usb_cdc_country_functional_desc *)buffer; + break; + case USB_CDC_HEADER_TYPE: + if (elength != sizeof(struct usb_cdc_header_desc)) + goto next_desc; + if (header) + return -EINVAL; + header = (struct usb_cdc_header_desc *)buffer; + break; + case USB_CDC_ACM_TYPE: + if (elength < sizeof(struct usb_cdc_acm_descriptor)) + goto next_desc; + hdr->usb_cdc_acm_descriptor = + (struct usb_cdc_acm_descriptor *)buffer; + break; + case USB_CDC_ETHERNET_TYPE: + if (elength != sizeof(struct usb_cdc_ether_desc)) + goto next_desc; + if (ether) + return -EINVAL; + ether = (struct usb_cdc_ether_desc *)buffer; + break; + case USB_CDC_CALL_MANAGEMENT_TYPE: + if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor)) + goto next_desc; + hdr->usb_cdc_call_mgmt_descriptor = + (struct usb_cdc_call_mgmt_descriptor *)buffer; + break; + case USB_CDC_DMM_TYPE: + if (elength < sizeof(struct usb_cdc_dmm_desc)) + goto next_desc; + hdr->usb_cdc_dmm_desc = + (struct usb_cdc_dmm_desc *)buffer; + break; + case USB_CDC_MDLM_TYPE: + if (elength < sizeof(struct usb_cdc_mdlm_desc *)) + goto next_desc; + if (desc) + return -EINVAL; + desc = (struct usb_cdc_mdlm_desc *)buffer; + break; + case USB_CDC_MDLM_DETAIL_TYPE: + if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *)) + goto next_desc; + if (detail) + return -EINVAL; + detail = (struct usb_cdc_mdlm_detail_desc *)buffer; + break; + case USB_CDC_NCM_TYPE: + if (elength < sizeof(struct usb_cdc_ncm_desc)) + goto next_desc; + hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer; + break; + case USB_CDC_MBIM_TYPE: + if (elength < sizeof(struct usb_cdc_mbim_desc)) + goto next_desc; + + hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer; + break; + case USB_CDC_MBIM_EXTENDED_TYPE: + if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) + break; + hdr->usb_cdc_mbim_extended_desc = + (struct usb_cdc_mbim_extended_desc *)buffer; + break; + case CDC_PHONET_MAGIC_NUMBER: + hdr->phonet_magic_present = true; + break; + default: + /* + * there are LOTS more CDC descriptors that + * could legitimately be found here. + */ + dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n", + buffer[2], elength); + goto next_desc; + } + cnt++; +next_desc: + buflen -= elength; + buffer += elength; + } + hdr->usb_cdc_union_desc = union_header; + hdr->usb_cdc_header_desc = header; + hdr->usb_cdc_mdlm_detail_desc = detail; + hdr->usb_cdc_mdlm_desc = desc; + hdr->usb_cdc_ether_desc = ether; + return cnt; +} + +EXPORT_SYMBOL(cdc_parse_cdc_header); + /* * The function can't be called inside suspend/resume callback, * otherwise deadlock will be caused. |