summaryrefslogtreecommitdiff
path: root/drivers/net/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/Kconfig4
-rw-r--r--drivers/net/usb/asix.h2
-rw-r--r--drivers/net/usb/asix_common.c115
-rw-r--r--drivers/net/usb/cdc-phonet.c27
-rw-r--r--drivers/net/usb/cdc_ether.c235
-rw-r--r--drivers/net/usb/cdc_mbim.c30
-rw-r--r--drivers/net/usb/cdc_ncm.c130
-rw-r--r--drivers/net/usb/dm9601.c1
-rw-r--r--drivers/net/usb/kaweth.c6
-rw-r--r--drivers/net/usb/lan78xx.c672
-rw-r--r--drivers/net/usb/lan78xx.h193
-rw-r--r--drivers/net/usb/mcs7830.c1
-rw-r--r--drivers/net/usb/qmi_wwan.c93
-rw-r--r--drivers/net/usb/r8152.c31
-rw-r--r--drivers/net/usb/smsc75xx.c5
-rw-r--r--drivers/net/usb/smsc95xx.c5
-rw-r--r--drivers/net/usb/sr9800.c4
-rw-r--r--drivers/net/usb/usbnet.c144
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.