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 | 230 | ||||
-rw-r--r-- | drivers/net/usb/cdc_mbim.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/cdc_ncm.c | 69 | ||||
-rw-r--r-- | drivers/net/usb/dm9601.c | 1 | ||||
-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 | 75 | ||||
-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 | 138 |
16 files changed, 609 insertions, 934 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index e66805eeffb4..7f83504dfa69 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 5d049d00c2d7..a2d3ea6efb20 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 079069a060a6..bd9acff1eb7b 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 415ce8b882c6..ff2270ead2e6 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 35a2bffe848a..c78d3cb1b464 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. diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index efc18e05af0a..bbde9884ab8a 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -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 */, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index db40175b1a0b..a187f08113ec 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -698,6 +698,7 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ int len; int temp; u8 iface_no; + struct usb_cdc_parsed_header hdr; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -722,66 +723,14 @@ 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; - - 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; - } + cdc_parse_cdc_header(&hdr, intf, buf, len); + + 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) { diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 6e9c344c7a20..0b4bdd39106b 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/lan78xx.c b/drivers/net/usb/lan78xx.c index a39518fc93aa..226668ead0d8 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 ae7562ee72ad..a93fb653e7c5 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 82d844a8ebd0..4f345bd4e6e2 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 355842b85ee9..c54719984c4b 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 */ @@ -765,6 +715,10 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x9056, 8)}, /* Sierra Wireless Modem */ {QMI_FIXED_INTF(0x1199, 0x9057, 8)}, {QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */ + {QMI_FIXED_INTF(0x1199, 0x9070, 8)}, /* Sierra Wireless MC74xx/EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x9070, 10)}, /* Sierra Wireless MC74xx/EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx/EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx/EM74xx */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ @@ -787,7 +741,6 @@ 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) */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index d9e7892262fa..30033dbe6662 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 26423adc35ee..66b3ab9f614e 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 953de13267df..a50df0d8fb9a 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 b4cf10781348..060918f49fea 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> @@ -1962,6 +1963,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. |