summaryrefslogtreecommitdiff
path: root/drivers/net/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/Kconfig17
-rw-r--r--drivers/net/usb/Makefile1
-rw-r--r--drivers/net/usb/cdc_eem.c4
-rw-r--r--drivers/net/usb/cdc_ether.c23
-rw-r--r--drivers/net/usb/cdc_ncm.c2
-rw-r--r--drivers/net/usb/cdc_subset.c8
-rw-r--r--drivers/net/usb/gl620a.c2
-rw-r--r--drivers/net/usb/kaweth.c2
-rw-r--r--drivers/net/usb/lg-vl600.c346
-rw-r--r--drivers/net/usb/net1080.c2
-rw-r--r--drivers/net/usb/plusb.c34
-rw-r--r--drivers/net/usb/rndis_host.c41
-rw-r--r--drivers/net/usb/smsc75xx.c124
-rw-r--r--drivers/net/usb/smsc95xx.c98
-rw-r--r--drivers/net/usb/usbnet.c13
-rw-r--r--drivers/net/usb/zaurus.c8
16 files changed, 545 insertions, 180 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 6f600cced6e1..9d4f9117260f 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -258,7 +258,7 @@ config USB_NET_NET1080
optionally with LEDs that indicate traffic
config USB_NET_PLUSB
- tristate "Prolific PL-2301/2302 based cables"
+ tristate "Prolific PL-2301/2302/25A1 based cables"
# if the handshake/init/reset problems, from original 'plusb',
# are ever resolved ... then remove "experimental"
depends on USB_USBNET && EXPERIMENTAL
@@ -433,4 +433,19 @@ config USB_SIERRA_NET
To compile this driver as a module, choose M here: the
module will be called sierra_net.
+config USB_VL600
+ tristate "LG VL600 modem dongle"
+ depends on USB_NET_CDCETHER
+ select USB_ACM
+ help
+ Select this if you want to use an LG Electronics 4G/LTE usb modem
+ called VL600. This driver only handles the ethernet
+ interface exposed by the modem firmware. To establish a connection
+ you will first need a userspace program that sends the right
+ command to the modem through its CDC ACM port, and most
+ likely also a DHCP client. See this thread about using the
+ 4G modem from Verizon:
+
+ http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
+
endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index cac170301187..c7ec8a5f0a90 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -27,4 +27,5 @@ obj-$(CONFIG_USB_IPHETH) += ipheth.o
obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o
obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o
+obj-$(CONFIG_USB_VL600) += lg-vl600.o
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c
index 5f3b97668e63..882f53f708df 100644
--- a/drivers/net/usb/cdc_eem.c
+++ b/drivers/net/usb/cdc_eem.c
@@ -190,7 +190,7 @@ static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
/*
* EEM packet header format:
- * b0..14: EEM type dependant (Data or Command)
+ * b0..14: EEM type dependent (Data or Command)
* b15: bmType
*/
header = get_unaligned_le16(skb->data);
@@ -340,7 +340,7 @@ next:
static const struct driver_info eem_info = {
.description = "CDC EEM Device",
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
.bind = eem_bind,
.rx_fixup = eem_rx_fixup,
.tx_fixup = eem_tx_fixup,
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 9a60e415d76b..341f7056a800 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -378,7 +378,7 @@ static void dumpspeed(struct usbnet *dev, __le32 *speeds)
__le32_to_cpu(speeds[1]) / 1000);
}
-static void cdc_status(struct usbnet *dev, struct urb *urb)
+void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
{
struct usb_cdc_notification *event;
@@ -418,8 +418,9 @@ static void cdc_status(struct usbnet *dev, struct urb *urb)
break;
}
}
+EXPORT_SYMBOL_GPL(usbnet_cdc_status);
-static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
{
int status;
struct cdc_state *info = (void *) &dev->data;
@@ -441,6 +442,7 @@ static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
*/
return 0;
}
+EXPORT_SYMBOL_GPL(usbnet_cdc_bind);
static int cdc_manage_power(struct usbnet *dev, int on)
{
@@ -450,20 +452,20 @@ static int cdc_manage_power(struct usbnet *dev, int on)
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
// .check_connect = cdc_check_connect,
- .bind = cdc_bind,
+ .bind = usbnet_cdc_bind,
.unbind = usbnet_cdc_unbind,
- .status = cdc_status,
+ .status = usbnet_cdc_status,
.manage_power = cdc_manage_power,
};
static const struct driver_info mbm_info = {
.description = "Mobile Broadband Network Device",
.flags = FLAG_WWAN,
- .bind = cdc_bind,
+ .bind = usbnet_cdc_bind,
.unbind = usbnet_cdc_unbind,
- .status = cdc_status,
+ .status = usbnet_cdc_status,
.manage_power = cdc_manage_power,
};
@@ -560,6 +562,13 @@ static const struct usb_device_id products [] = {
.driver_info = 0,
},
+/* LG Electronics VL600 wants additional headers on every frame */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/*
* WHITELIST!!!
*
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 7113168473cf..967371f04454 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1237,7 +1237,7 @@ static int cdc_ncm_manage_power(struct usbnet *dev, int status)
static const struct driver_info cdc_ncm_info = {
.description = "CDC NCM",
- .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET,
+ .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET,
.bind = cdc_ncm_bind,
.unbind = cdc_ncm_unbind,
.check_connect = cdc_ncm_check_connect,
diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c
index ca39ace0b0eb..fc5f13d47ad9 100644
--- a/drivers/net/usb/cdc_subset.c
+++ b/drivers/net/usb/cdc_subset.c
@@ -89,6 +89,7 @@ static int always_connected (struct usbnet *dev)
static const struct driver_info ali_m5632_info = {
.description = "ALi M5632",
+ .flags = FLAG_POINTTOPOINT,
};
#endif
@@ -110,6 +111,7 @@ static const struct driver_info ali_m5632_info = {
static const struct driver_info an2720_info = {
.description = "AnchorChips/Cypress 2720",
+ .flags = FLAG_POINTTOPOINT,
// no reset available!
// no check_connect available!
@@ -132,6 +134,7 @@ static const struct driver_info an2720_info = {
static const struct driver_info belkin_info = {
.description = "Belkin, eTEK, or compatible",
+ .flags = FLAG_POINTTOPOINT,
};
#endif /* CONFIG_USB_BELKIN */
@@ -157,6 +160,7 @@ static const struct driver_info belkin_info = {
static const struct driver_info epson2888_info = {
.description = "Epson USB Device",
.check_connect = always_connected,
+ .flags = FLAG_POINTTOPOINT,
.in = 4, .out = 3,
};
@@ -173,6 +177,7 @@ static const struct driver_info epson2888_info = {
#define HAVE_HARDWARE
static const struct driver_info kc2190_info = {
.description = "KC Technology KC-190",
+ .flags = FLAG_POINTTOPOINT,
};
#endif /* CONFIG_USB_KC2190 */
@@ -200,16 +205,19 @@ static const struct driver_info kc2190_info = {
static const struct driver_info linuxdev_info = {
.description = "Linux Device",
.check_connect = always_connected,
+ .flags = FLAG_POINTTOPOINT,
};
static const struct driver_info yopy_info = {
.description = "Yopy",
.check_connect = always_connected,
+ .flags = FLAG_POINTTOPOINT,
};
static const struct driver_info blob_info = {
.description = "Boot Loader OBject",
.check_connect = always_connected,
+ .flags = FLAG_POINTTOPOINT,
};
#endif /* CONFIG_USB_ARMLINUX */
diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c
index dcd57c37ef73..c4cfd1dea881 100644
--- a/drivers/net/usb/gl620a.c
+++ b/drivers/net/usb/gl620a.c
@@ -193,7 +193,7 @@ static int genelink_bind(struct usbnet *dev, struct usb_interface *intf)
static const struct driver_info genelink_info = {
.description = "Genesys GeneLink",
- .flags = FLAG_FRAMING_GL | FLAG_NO_SETINT,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_GL | FLAG_NO_SETINT,
.bind = genelink_bind,
.rx_fixup = genelink_rx_fixup,
.tx_fixup = genelink_tx_fixup,
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c
index 7dc84971f26f..ad0298f9b5f9 100644
--- a/drivers/net/usb/kaweth.c
+++ b/drivers/net/usb/kaweth.c
@@ -1221,7 +1221,7 @@ static void kaweth_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
if (!kaweth) {
- dev_warn(&intf->dev, "unregistering non-existant device\n");
+ dev_warn(&intf->dev, "unregistering non-existent device\n");
return;
}
netdev = kaweth->net;
diff --git a/drivers/net/usb/lg-vl600.c b/drivers/net/usb/lg-vl600.c
new file mode 100644
index 000000000000..1d83ccfd7277
--- /dev/null
+++ b/drivers/net/usb/lg-vl600.c
@@ -0,0 +1,346 @@
+/*
+ * Ethernet interface part of the LG VL600 LTE modem (4G dongle)
+ *
+ * Copyright (C) 2011 Intel Corporation
+ * Author: Andrzej Zaborowski <balrogg@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+
+/*
+ * The device has a CDC ACM port for modem control (it claims to be
+ * CDC ACM anyway) and a CDC Ethernet port for actual network data.
+ * It will however ignore data on both ports that is not encapsulated
+ * in a specific way, any data returned is also encapsulated the same
+ * way. The headers don't seem to follow any popular standard.
+ *
+ * This driver adds and strips these headers from the ethernet frames
+ * sent/received from the CDC Ethernet port. The proprietary header
+ * replaces the standard ethernet header in a packet so only actual
+ * ethernet frames are allowed. The headers allow some form of
+ * multiplexing by using non standard values of the .h_proto field.
+ * Windows/Mac drivers do send a couple of such frames to the device
+ * during initialisation, with protocol set to 0x0906 or 0x0b06 and (what
+ * seems to be) a flag in the .dummy_flags. This doesn't seem necessary
+ * for modem operation but can possibly be used for GPS or other funcitons.
+ */
+
+struct vl600_frame_hdr {
+ __le32 len;
+ __le32 serial;
+ __le32 pkt_cnt;
+ __le32 dummy_flags;
+ __le32 dummy;
+ __le32 magic;
+} __attribute__((packed));
+
+struct vl600_pkt_hdr {
+ __le32 dummy[2];
+ __le32 len;
+ __be16 h_proto;
+} __attribute__((packed));
+
+struct vl600_state {
+ struct sk_buff *current_rx_buf;
+};
+
+static int vl600_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int ret;
+ struct vl600_state *s = kzalloc(sizeof(struct vl600_state), GFP_KERNEL);
+
+ if (!s)
+ return -ENOMEM;
+
+ ret = usbnet_cdc_bind(dev, intf);
+ if (ret) {
+ kfree(s);
+ return ret;
+ }
+
+ dev->driver_priv = s;
+
+ /* ARP packets don't go through, but they're also of no use. The
+ * subnet has only two hosts anyway: us and the gateway / DHCP
+ * server (probably simulated by modem firmware or network operator)
+ * whose address changes everytime we connect to the intarwebz and
+ * who doesn't bother answering ARP requests either. So hardware
+ * addresses have no meaning, the destination and the source of every
+ * packet depend only on whether it is on the IN or OUT endpoint. */
+ dev->net->flags |= IFF_NOARP;
+
+ return ret;
+}
+
+static void vl600_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct vl600_state *s = dev->driver_priv;
+
+ if (s->current_rx_buf)
+ dev_kfree_skb(s->current_rx_buf);
+
+ kfree(s);
+
+ return usbnet_cdc_unbind(dev, intf);
+}
+
+static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ struct vl600_frame_hdr *frame;
+ struct vl600_pkt_hdr *packet;
+ struct ethhdr *ethhdr;
+ int packet_len, count;
+ struct sk_buff *buf = skb;
+ struct sk_buff *clone;
+ struct vl600_state *s = dev->driver_priv;
+
+ /* Frame lengths are generally 4B multiplies but every couple of
+ * hours there's an odd number of bytes sized yet correct frame,
+ * so don't require this. */
+
+ /* Allow a packet (or multiple packets batched together) to be
+ * split across many frames. We don't allow a new batch to
+ * begin in the same frame another one is ending however, and no
+ * leading or trailing pad bytes. */
+ if (s->current_rx_buf) {
+ frame = (struct vl600_frame_hdr *) s->current_rx_buf->data;
+ if (skb->len + s->current_rx_buf->len >
+ le32_to_cpup(&frame->len)) {
+ netif_err(dev, ifup, dev->net, "Fragment too long\n");
+ dev->net->stats.rx_length_errors++;
+ goto error;
+ }
+
+ buf = s->current_rx_buf;
+ memcpy(skb_put(buf, skb->len), skb->data, skb->len);
+ } else if (skb->len < 4) {
+ netif_err(dev, ifup, dev->net, "Frame too short\n");
+ dev->net->stats.rx_length_errors++;
+ goto error;
+ }
+
+ frame = (struct vl600_frame_hdr *) buf->data;
+ /* NOTE: Should check that frame->magic == 0x53544448?
+ * Otherwise if we receive garbage at the beginning of the frame
+ * we may end up allocating a huge buffer and saving all the
+ * future incoming data into it. */
+
+ if (buf->len < sizeof(*frame) ||
+ buf->len != le32_to_cpup(&frame->len)) {
+ /* Save this fragment for later assembly */
+ if (s->current_rx_buf)
+ return 0;
+
+ s->current_rx_buf = skb_copy_expand(skb, 0,
+ le32_to_cpup(&frame->len), GFP_ATOMIC);
+ if (!s->current_rx_buf) {
+ netif_err(dev, ifup, dev->net, "Reserving %i bytes "
+ "for packet assembly failed.\n",
+ le32_to_cpup(&frame->len));
+ dev->net->stats.rx_errors++;
+ }
+
+ return 0;
+ }
+
+ count = le32_to_cpup(&frame->pkt_cnt);
+
+ skb_pull(buf, sizeof(*frame));
+
+ while (count--) {
+ if (buf->len < sizeof(*packet)) {
+ netif_err(dev, ifup, dev->net, "Packet too short\n");
+ goto error;
+ }
+
+ packet = (struct vl600_pkt_hdr *) buf->data;
+ packet_len = sizeof(*packet) + le32_to_cpup(&packet->len);
+ if (packet_len > buf->len) {
+ netif_err(dev, ifup, dev->net,
+ "Bad packet length stored in header\n");
+ goto error;
+ }
+
+ /* Packet header is same size as the ethernet header
+ * (sizeof(*packet) == sizeof(*ethhdr)), additionally
+ * the h_proto field is in the same place so we just leave it
+ * alone and fill in the remaining fields.
+ */
+ ethhdr = (struct ethhdr *) skb->data;
+ if (be16_to_cpup(&ethhdr->h_proto) == ETH_P_ARP &&
+ buf->len > 0x26) {
+ /* Copy the addresses from packet contents */
+ memcpy(ethhdr->h_source,
+ &buf->data[sizeof(*ethhdr) + 0x8],
+ ETH_ALEN);
+ memcpy(ethhdr->h_dest,
+ &buf->data[sizeof(*ethhdr) + 0x12],
+ ETH_ALEN);
+ } else {
+ memset(ethhdr->h_source, 0, ETH_ALEN);
+ memcpy(ethhdr->h_dest, dev->net->dev_addr, ETH_ALEN);
+ }
+
+ if (count) {
+ /* Not the last packet in this batch */
+ clone = skb_clone(buf, GFP_ATOMIC);
+ if (!clone)
+ goto error;
+
+ skb_trim(clone, packet_len);
+ usbnet_skb_return(dev, clone);
+
+ skb_pull(buf, (packet_len + 3) & ~3);
+ } else {
+ skb_trim(buf, packet_len);
+
+ if (s->current_rx_buf) {
+ usbnet_skb_return(dev, buf);
+ s->current_rx_buf = NULL;
+ return 0;
+ }
+
+ return 1;
+ }
+ }
+
+error:
+ if (s->current_rx_buf) {
+ dev_kfree_skb_any(s->current_rx_buf);
+ s->current_rx_buf = NULL;
+ }
+ dev->net->stats.rx_errors++;
+ return 0;
+}
+
+static struct sk_buff *vl600_tx_fixup(struct usbnet *dev,
+ struct sk_buff *skb, gfp_t flags)
+{
+ struct sk_buff *ret;
+ struct vl600_frame_hdr *frame;
+ struct vl600_pkt_hdr *packet;
+ static uint32_t serial = 1;
+ int orig_len = skb->len - sizeof(struct ethhdr);
+ int full_len = (skb->len + sizeof(struct vl600_frame_hdr) + 3) & ~3;
+
+ frame = (struct vl600_frame_hdr *) skb->data;
+ if (skb->len > sizeof(*frame) && skb->len == le32_to_cpup(&frame->len))
+ return skb; /* Already encapsulated? */
+
+ if (skb->len < sizeof(struct ethhdr))
+ /* Drop, device can only deal with ethernet packets */
+ return NULL;
+
+ if (!skb_cloned(skb)) {
+ int headroom = skb_headroom(skb);
+ int tailroom = skb_tailroom(skb);
+
+ if (tailroom >= full_len - skb->len - sizeof(*frame) &&
+ headroom >= sizeof(*frame))
+ /* There's enough head and tail room */
+ goto encapsulate;
+
+ if (headroom + tailroom + skb->len >= full_len) {
+ /* There's enough total room, just readjust */
+ skb->data = memmove(skb->head + sizeof(*frame),
+ skb->data, skb->len);
+ skb_set_tail_pointer(skb, skb->len);
+ goto encapsulate;
+ }
+ }
+
+ /* Alloc a new skb with the required size */
+ ret = skb_copy_expand(skb, sizeof(struct vl600_frame_hdr), full_len -
+ skb->len - sizeof(struct vl600_frame_hdr), flags);
+ dev_kfree_skb_any(skb);
+ if (!ret)
+ return ret;
+ skb = ret;
+
+encapsulate:
+ /* Packet header is same size as ethernet packet header
+ * (sizeof(*packet) == sizeof(struct ethhdr)), additionally the
+ * h_proto field is in the same place so we just leave it alone and
+ * overwrite the remaining fields.
+ */
+ packet = (struct vl600_pkt_hdr *) skb->data;
+ memset(&packet->dummy, 0, sizeof(packet->dummy));
+ packet->len = cpu_to_le32(orig_len);
+
+ frame = (struct vl600_frame_hdr *) skb_push(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ frame->len = cpu_to_le32(full_len);
+ frame->serial = cpu_to_le32(serial++);
+ frame->pkt_cnt = cpu_to_le32(1);
+
+ if (skb->len < full_len) /* Pad */
+ skb_put(skb, full_len - skb->len);
+
+ return skb;
+}
+
+static const struct driver_info vl600_info = {
+ .description = "LG VL600 modem",
+ .flags = FLAG_ETHER | FLAG_RX_ASSEMBLE,
+ .bind = vl600_bind,
+ .unbind = vl600_unbind,
+ .status = usbnet_cdc_status,
+ .rx_fixup = vl600_rx_fixup,
+ .tx_fixup = vl600_tx_fixup,
+};
+
+static const struct usb_device_id products[] = {
+ {
+ USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long) &vl600_info,
+ },
+ {}, /* End */
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver lg_vl600_driver = {
+ .name = "lg-vl600",
+ .id_table = products,
+ .probe = usbnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = usbnet_suspend,
+ .resume = usbnet_resume,
+};
+
+static int __init vl600_init(void)
+{
+ return usb_register(&lg_vl600_driver);
+}
+module_init(vl600_init);
+
+static void __exit vl600_exit(void)
+{
+ usb_deregister(&lg_vl600_driver);
+}
+module_exit(vl600_exit);
+
+MODULE_AUTHOR("Anrzej Zaborowski");
+MODULE_DESCRIPTION("LG-VL600 modem's ethernet link");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/net1080.c b/drivers/net/usb/net1080.c
index ba72a7281cb0..01db4602a39e 100644
--- a/drivers/net/usb/net1080.c
+++ b/drivers/net/usb/net1080.c
@@ -560,7 +560,7 @@ static int net1080_bind(struct usbnet *dev, struct usb_interface *intf)
static const struct driver_info net1080_info = {
.description = "NetChip TurboCONNECT",
- .flags = FLAG_FRAMING_NC,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_NC,
.bind = net1080_bind,
.reset = net1080_reset,
.check_connect = net1080_check_connect,
diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c
index 08ad269f6b4e..217aec8a768f 100644
--- a/drivers/net/usb/plusb.c
+++ b/drivers/net/usb/plusb.c
@@ -45,6 +45,14 @@
* seems to get wedged under load. Prolific docs are weak, and
* don't identify differences between PL2301 and PL2302, much less
* anything to explain the different PL2302 versions observed.
+ *
+ * NOTE: pl2501 has several modes, including pl2301 and pl2302
+ * compatibility. Some docs suggest the difference between 2301
+ * and 2302 is only to make MS-Windows use a different driver...
+ *
+ * pl25a1 glue based on patch from Tony Gibbs. Prolific "docs" on
+ * this chip are as usual incomplete about what control messages
+ * are supported.
*/
/*
@@ -86,17 +94,21 @@ pl_set_QuickLink_features(struct usbnet *dev, int val)
static int pl_reset(struct usbnet *dev)
{
+ int status;
+
/* some units seem to need this reset, others reject it utterly.
* FIXME be more like "naplink" or windows drivers.
*/
- (void) pl_set_QuickLink_features(dev,
+ status = pl_set_QuickLink_features(dev,
PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E);
+ if (status != 0 && netif_msg_probe(dev))
+ netif_dbg(dev, link, dev->net, "pl_reset --> %d\n", status);
return 0;
}
static const struct driver_info prolific_info = {
- .description = "Prolific PL-2301/PL-2302",
- .flags = FLAG_NO_SETINT,
+ .description = "Prolific PL-2301/PL-2302/PL-25A1",
+ .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT,
/* some PL-2302 versions seem to fail usb_set_interface() */
.reset = pl_reset,
};
@@ -111,6 +123,7 @@ static const struct driver_info prolific_info = {
static const struct usb_device_id products [] = {
+/* full speed cables */
{
USB_DEVICE(0x067b, 0x0000), // PL-2301
.driver_info = (unsigned long) &prolific_info,
@@ -119,6 +132,15 @@ static const struct usb_device_id products [] = {
.driver_info = (unsigned long) &prolific_info,
},
+/* high speed cables */
+{
+ USB_DEVICE(0x067b, 0x25a1), /* PL-25A1, no eeprom */
+ .driver_info = (unsigned long) &prolific_info,
+}, {
+ USB_DEVICE(0x050d, 0x258a), /* Belkin F5U258/F5U279 (PL-25A1) */
+ .driver_info = (unsigned long) &prolific_info,
+},
+
{ }, // END
};
MODULE_DEVICE_TABLE(usb, products);
@@ -134,16 +156,16 @@ static struct usb_driver plusb_driver = {
static int __init plusb_init(void)
{
- return usb_register(&plusb_driver);
+ return usb_register(&plusb_driver);
}
module_init(plusb_init);
static void __exit plusb_exit(void)
{
- usb_deregister(&plusb_driver);
+ usb_deregister(&plusb_driver);
}
module_exit(plusb_exit);
MODULE_AUTHOR("David Brownell");
-MODULE_DESCRIPTION("Prolific PL-2301/2302 USB Host to Host Link Driver");
+MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index dd8a4adf48ca..255d6a424a6b 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -104,8 +104,10 @@ static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg,
int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)
{
struct cdc_state *info = (void *) &dev->data;
+ struct usb_cdc_notification notification;
int master_ifnum;
int retval;
+ int partial;
unsigned count;
__le32 rsp;
u32 xid = 0, msg_len, request_id;
@@ -133,13 +135,20 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)
if (unlikely(retval < 0 || xid == 0))
return retval;
- // FIXME Seems like some devices discard responses when
- // we time out and cancel our "get response" requests...
- // so, this is fragile. Probably need to poll for status.
+ /* Some devices don't respond on the control channel until
+ * polled on the status channel, so do that first. */
+ if (dev->driver_info->data & RNDIS_DRIVER_DATA_POLL_STATUS) {
+ retval = usb_interrupt_msg(
+ dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->status->desc.bEndpointAddress),
+ &notification, sizeof(notification), &partial,
+ RNDIS_CONTROL_TIMEOUT_MS);
+ if (unlikely(retval < 0))
+ return retval;
+ }
- /* ignore status endpoint, just poll the control channel;
- * the request probably completed immediately
- */
+ /* Poll the control channel; the request probably completed immediately */
rsp = buf->msg_type | RNDIS_MSG_COMPLETION;
for (count = 0; count < 10; count++) {
memset(buf, 0, CONTROL_BUFFER_SIZE);
@@ -573,7 +582,18 @@ EXPORT_SYMBOL_GPL(rndis_tx_fixup);
static const struct driver_info rndis_info = {
.description = "RNDIS device",
- .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .bind = rndis_bind,
+ .unbind = rndis_unbind,
+ .status = rndis_status,
+ .rx_fixup = rndis_rx_fixup,
+ .tx_fixup = rndis_tx_fixup,
+};
+
+static const struct driver_info rndis_poll_status_info = {
+ .description = "RNDIS device (poll status before control)",
+ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .data = RNDIS_DRIVER_DATA_POLL_STATUS,
.bind = rndis_bind,
.unbind = rndis_unbind,
.status = rndis_status,
@@ -585,13 +605,18 @@ static const struct driver_info rndis_info = {
static const struct usb_device_id products [] = {
{
+ /* 2Wire HomePortal 1000SW */
+ USB_DEVICE_AND_INTERFACE_INFO(0x1630, 0x0042,
+ USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+ .driver_info = (unsigned long) &rndis_poll_status_info,
+}, {
/* RNDIS is MSFT's un-official variant of CDC ACM */
USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
.driver_info = (unsigned long) &rndis_info,
}, {
/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
- .driver_info = (unsigned long) &rndis_info,
+ .driver_info = (unsigned long) &rndis_poll_status_info,
}, {
/* RNDIS for tethering */
USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index 753ee6eb7edd..860a20c938b4 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -65,7 +65,6 @@ struct smsc75xx_priv {
struct usbnet *dev;
u32 rfe_ctl;
u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN];
- bool use_rx_csum;
struct mutex dataport_mutex;
spinlock_t rfe_ctl_lock;
struct work_struct set_multicast;
@@ -548,28 +547,6 @@ static void smsc75xx_status(struct usbnet *dev, struct urb *urb)
"unexpected interrupt, intdata=0x%08X", intdata);
}
-/* Enable or disable Rx checksum offload engine */
-static int smsc75xx_set_rx_csum_offload(struct usbnet *dev)
-{
- struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&pdata->rfe_ctl_lock, flags);
-
- if (pdata->use_rx_csum)
- pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM;
- else
- pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM);
-
- spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
-
- ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
- check_warn_return(ret, "Error writing RFE_CTL");
-
- return 0;
-}
-
static int smsc75xx_ethtool_get_eeprom_len(struct net_device *net)
{
return MAX_EEPROM_SIZE;
@@ -599,34 +576,6 @@ static int smsc75xx_ethtool_set_eeprom(struct net_device *netdev,
return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data);
}
-static u32 smsc75xx_ethtool_get_rx_csum(struct net_device *netdev)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
-
- return pdata->use_rx_csum;
-}
-
-static int smsc75xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
-
- pdata->use_rx_csum = !!val;
-
- return smsc75xx_set_rx_csum_offload(dev);
-}
-
-static int smsc75xx_ethtool_set_tso(struct net_device *netdev, u32 data)
-{
- if (data)
- netdev->features |= NETIF_F_TSO | NETIF_F_TSO6;
- else
- netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
-
- return 0;
-}
-
static const struct ethtool_ops smsc75xx_ethtool_ops = {
.get_link = usbnet_get_link,
.nway_reset = usbnet_nway_reset,
@@ -638,12 +587,6 @@ static const struct ethtool_ops smsc75xx_ethtool_ops = {
.get_eeprom_len = smsc75xx_ethtool_get_eeprom_len,
.get_eeprom = smsc75xx_ethtool_get_eeprom,
.set_eeprom = smsc75xx_ethtool_set_eeprom,
- .get_tx_csum = ethtool_op_get_tx_csum,
- .set_tx_csum = ethtool_op_set_tx_hw_csum,
- .get_rx_csum = smsc75xx_ethtool_get_rx_csum,
- .set_rx_csum = smsc75xx_ethtool_set_rx_csum,
- .get_tso = ethtool_op_get_tso,
- .set_tso = smsc75xx_ethtool_set_tso,
};
static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
@@ -782,6 +725,30 @@ static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu)
return usbnet_change_mtu(netdev, new_mtu);
}
+/* Enable or disable Rx checksum offload engine */
+static int smsc75xx_set_features(struct net_device *netdev, u32 features)
+{
+ struct usbnet *dev = netdev_priv(netdev);
+ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pdata->rfe_ctl_lock, flags);
+
+ if (features & NETIF_F_RXCSUM)
+ pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM;
+ else
+ pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM);
+
+ spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags);
+ /* it's racing here! */
+
+ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl);
+ check_warn_return(ret, "Error writing RFE_CTL");
+
+ return 0;
+}
+
static int smsc75xx_reset(struct usbnet *dev)
{
struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
@@ -960,11 +927,7 @@ static int smsc75xx_reset(struct usbnet *dev)
netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x", pdata->rfe_ctl);
/* Enable or disable checksum offload engines */
- ethtool_op_set_tx_hw_csum(dev->net, DEFAULT_TX_CSUM_ENABLE);
- ret = smsc75xx_set_rx_csum_offload(dev);
- check_warn_return(ret, "Failed to set rx csum offload: %d", ret);
-
- smsc75xx_ethtool_set_tso(dev->net, DEFAULT_TSO_ENABLE);
+ smsc75xx_set_features(dev->net, dev->net->features);
smsc75xx_set_multicast(dev->net);
@@ -1037,6 +1000,7 @@ static const struct net_device_ops smsc75xx_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = smsc75xx_ioctl,
.ndo_set_multicast_list = smsc75xx_set_multicast,
+ .ndo_set_features = smsc75xx_set_features,
};
static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf)
@@ -1065,10 +1029,17 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf)
INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write);
- pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE;
+ if (DEFAULT_TX_CSUM_ENABLE) {
+ dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+ if (DEFAULT_TSO_ENABLE)
+ dev->net->features |= NETIF_F_SG |
+ NETIF_F_TSO | NETIF_F_TSO6;
+ }
+ if (DEFAULT_RX_CSUM_ENABLE)
+ dev->net->features |= NETIF_F_RXCSUM;
- /* We have to advertise SG otherwise TSO cannot be enabled */
- dev->net->features |= NETIF_F_SG;
+ dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_RXCSUM;
/* Init all registers */
ret = smsc75xx_reset(dev);
@@ -1091,10 +1062,11 @@ static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf)
}
}
-static void smsc75xx_rx_csum_offload(struct sk_buff *skb, u32 rx_cmd_a,
- u32 rx_cmd_b)
+static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb,
+ u32 rx_cmd_a, u32 rx_cmd_b)
{
- if (unlikely(rx_cmd_a & RX_CMD_A_LCSM)) {
+ if (!(dev->net->features & NETIF_F_RXCSUM) ||
+ unlikely(rx_cmd_a & RX_CMD_A_LCSM)) {
skb->ip_summed = CHECKSUM_NONE;
} else {
skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT));
@@ -1104,8 +1076,6 @@ static void smsc75xx_rx_csum_offload(struct sk_buff *skb, u32 rx_cmd_a,
static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
- struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]);
-
while (skb->len > 0) {
u32 rx_cmd_a, rx_cmd_b, align_count, size;
struct sk_buff *ax_skb;
@@ -1145,11 +1115,8 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
/* last frame in this batch */
if (skb->len == size) {
- if (pdata->use_rx_csum)
- smsc75xx_rx_csum_offload(skb, rx_cmd_a,
- rx_cmd_b);
- else
- skb->ip_summed = CHECKSUM_NONE;
+ smsc75xx_rx_csum_offload(dev, skb, rx_cmd_a,
+ rx_cmd_b);
skb_trim(skb, skb->len - 4); /* remove fcs */
skb->truesize = size + sizeof(struct sk_buff);
@@ -1167,11 +1134,8 @@ static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
ax_skb->data = packet;
skb_set_tail_pointer(ax_skb, size);
- if (pdata->use_rx_csum)
- smsc75xx_rx_csum_offload(ax_skb, rx_cmd_a,
- rx_cmd_b);
- else
- ax_skb->ip_summed = CHECKSUM_NONE;
+ smsc75xx_rx_csum_offload(dev, ax_skb, rx_cmd_a,
+ rx_cmd_b);
skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */
ax_skb->truesize = size + sizeof(struct sk_buff);
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 727874d9deb6..24f4b3739dd2 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -52,8 +52,6 @@ struct smsc95xx_priv {
u32 hash_hi;
u32 hash_lo;
spinlock_t mac_cr_lock;
- bool use_tx_csum;
- bool use_rx_csum;
};
struct usb_context {
@@ -517,22 +515,24 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
}
/* Enable or disable Tx & Rx checksum offload engines */
-static int smsc95xx_set_csums(struct usbnet *dev)
+static int smsc95xx_set_features(struct net_device *netdev, u32 features)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct usbnet *dev = netdev_priv(netdev);
u32 read_buf;
- int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
+ int ret;
+
+ ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
if (ret < 0) {
netdev_warn(dev->net, "Failed to read COE_CR: %d\n", ret);
return ret;
}
- if (pdata->use_tx_csum)
+ if (features & NETIF_F_HW_CSUM)
read_buf |= Tx_COE_EN_;
else
read_buf &= ~Tx_COE_EN_;
- if (pdata->use_rx_csum)
+ if (features & NETIF_F_RXCSUM)
read_buf |= Rx_COE_EN_;
else
read_buf &= ~Rx_COE_EN_;
@@ -576,43 +576,6 @@ static int smsc95xx_ethtool_set_eeprom(struct net_device *netdev,
return smsc95xx_write_eeprom(dev, ee->offset, ee->len, data);
}
-static u32 smsc95xx_ethtool_get_rx_csum(struct net_device *netdev)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
- return pdata->use_rx_csum;
-}
-
-static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
- pdata->use_rx_csum = !!val;
-
- return smsc95xx_set_csums(dev);
-}
-
-static u32 smsc95xx_ethtool_get_tx_csum(struct net_device *netdev)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
- return pdata->use_tx_csum;
-}
-
-static int smsc95xx_ethtool_set_tx_csum(struct net_device *netdev, u32 val)
-{
- struct usbnet *dev = netdev_priv(netdev);
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
- pdata->use_tx_csum = !!val;
-
- ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum);
- return smsc95xx_set_csums(dev);
-}
-
static const struct ethtool_ops smsc95xx_ethtool_ops = {
.get_link = usbnet_get_link,
.nway_reset = usbnet_nway_reset,
@@ -624,10 +587,6 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = {
.get_eeprom_len = smsc95xx_ethtool_get_eeprom_len,
.get_eeprom = smsc95xx_ethtool_get_eeprom,
.set_eeprom = smsc95xx_ethtool_set_eeprom,
- .get_tx_csum = smsc95xx_ethtool_get_tx_csum,
- .set_tx_csum = smsc95xx_ethtool_set_tx_csum,
- .get_rx_csum = smsc95xx_ethtool_get_rx_csum,
- .set_rx_csum = smsc95xx_ethtool_set_rx_csum,
};
static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
@@ -755,7 +714,6 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)
static int smsc95xx_reset(struct usbnet *dev)
{
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
- struct net_device *netdev = dev->net;
u32 read_buf, write_buf, burst_cap;
int ret = 0, timeout;
@@ -975,12 +933,7 @@ static int smsc95xx_reset(struct usbnet *dev)
}
/* Enable or disable checksum offload engines */
- ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum);
- ret = smsc95xx_set_csums(dev);
- if (ret < 0) {
- netdev_warn(dev->net, "Failed to set csum offload: %d\n", ret);
- return ret;
- }
+ smsc95xx_set_features(dev->net, dev->net->features);
smsc95xx_set_multicast(dev->net);
@@ -1019,6 +972,7 @@ static const struct net_device_ops smsc95xx_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = smsc95xx_ioctl,
.ndo_set_multicast_list = smsc95xx_set_multicast,
+ .ndo_set_features = smsc95xx_set_features,
};
static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
@@ -1045,8 +999,12 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
spin_lock_init(&pdata->mac_cr_lock);
- pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE;
- pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE;
+ if (DEFAULT_TX_CSUM_ENABLE)
+ dev->net->features |= NETIF_F_HW_CSUM;
+ if (DEFAULT_RX_CSUM_ENABLE)
+ dev->net->features |= NETIF_F_RXCSUM;
+
+ dev->net->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
smsc95xx_init_mac_address(dev);
@@ -1056,7 +1014,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->netdev_ops = &smsc95xx_netdev_ops;
dev->net->ethtool_ops = &smsc95xx_ethtool_ops;
dev->net->flags |= IFF_MULTICAST;
- dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD;
+ dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM;
return 0;
}
@@ -1080,8 +1038,6 @@ static void smsc95xx_rx_csum_offload(struct sk_buff *skb)
static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
while (skb->len > 0) {
u32 header, align_count;
struct sk_buff *ax_skb;
@@ -1123,7 +1079,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
/* last frame in this batch */
if (skb->len == size) {
- if (pdata->use_rx_csum)
+ if (dev->net->features & NETIF_F_RXCSUM)
smsc95xx_rx_csum_offload(skb);
skb_trim(skb, skb->len - 4); /* remove fcs */
skb->truesize = size + sizeof(struct sk_buff);
@@ -1141,7 +1097,7 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
ax_skb->data = packet;
skb_set_tail_pointer(ax_skb, size);
- if (pdata->use_rx_csum)
+ if (dev->net->features & NETIF_F_RXCSUM)
smsc95xx_rx_csum_offload(ax_skb);
skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */
ax_skb->truesize = size + sizeof(struct sk_buff);
@@ -1174,8 +1130,7 @@ static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb)
static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
struct sk_buff *skb, gfp_t flags)
{
- struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
- bool csum = pdata->use_tx_csum && (skb->ip_summed == CHECKSUM_PARTIAL);
+ bool csum = skb->ip_summed == CHECKSUM_PARTIAL;
int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;
u32 tx_cmd_a, tx_cmd_b;
@@ -1313,6 +1268,21 @@ static const struct usb_device_id products[] = {
USB_DEVICE(0x0424, 0x9909),
.driver_info = (unsigned long) &smsc95xx_info,
},
+ {
+ /* SMSC LAN9530 USB Ethernet Device */
+ USB_DEVICE(0x0424, 0x9530),
+ .driver_info = (unsigned long) &smsc95xx_info,
+ },
+ {
+ /* SMSC LAN9730 USB Ethernet Device */
+ USB_DEVICE(0x0424, 0x9730),
+ .driver_info = (unsigned long) &smsc95xx_info,
+ },
+ {
+ /* SMSC LAN89530 USB Ethernet Device */
+ USB_DEVICE(0x0424, 0x9E08),
+ .driver_info = (unsigned long) &smsc95xx_info,
+ },
{ }, /* END */
};
MODULE_DEVICE_TABLE(usb, products);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 95c41d56631c..069c1cf0fdf7 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -387,8 +387,12 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
{
if (dev->driver_info->rx_fixup &&
- !dev->driver_info->rx_fixup (dev, skb))
- goto error;
+ !dev->driver_info->rx_fixup (dev, skb)) {
+ /* With RX_ASSEMBLE, rx_fixup() must update counters */
+ if (!(dev->driver_info->flags & FLAG_RX_ASSEMBLE))
+ dev->net->stats.rx_errors++;
+ goto done;
+ }
// else network stack removes extra byte if we forced a short packet
if (skb->len) {
@@ -401,8 +405,8 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
}
netif_dbg(dev, rx_err, dev->net, "drop\n");
-error:
dev->net->stats.rx_errors++;
+done:
skb_queue_tail(&dev->done, skb);
}
@@ -1376,7 +1380,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
// else "eth%d" when there's reasonable doubt. userspace
// can rename the link if it knows better.
if ((dev->driver_info->flags & FLAG_ETHER) != 0 &&
- (net->dev_addr [0] & 0x02) == 0)
+ ((dev->driver_info->flags & FLAG_POINTTOPOINT) == 0 ||
+ (net->dev_addr [0] & 0x02) == 0))
strcpy (net->name, "eth%d");
/* WLAN devices should always be named "wlan%d" */
if ((dev->driver_info->flags & FLAG_WLAN) != 0)
diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c
index 3eb0b167b5b4..241756e0e86f 100644
--- a/drivers/net/usb/zaurus.c
+++ b/drivers/net/usb/zaurus.c
@@ -102,7 +102,7 @@ static int always_connected (struct usbnet *dev)
static const struct driver_info zaurus_sl5x00_info = {
.description = "Sharp Zaurus SL-5x00",
- .flags = FLAG_FRAMING_Z,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z,
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
@@ -112,7 +112,7 @@ static const struct driver_info zaurus_sl5x00_info = {
static const struct driver_info zaurus_pxa_info = {
.description = "Sharp Zaurus, PXA-2xx based",
- .flags = FLAG_FRAMING_Z,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z,
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
@@ -122,7 +122,7 @@ static const struct driver_info zaurus_pxa_info = {
static const struct driver_info olympus_mxl_info = {
.description = "Olympus R1000",
- .flags = FLAG_FRAMING_Z,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z,
.check_connect = always_connected,
.bind = zaurus_bind,
.unbind = usbnet_cdc_unbind,
@@ -258,7 +258,7 @@ bad_desc:
static const struct driver_info bogus_mdlm_info = {
.description = "pseudo-MDLM (BLAN) device",
- .flags = FLAG_FRAMING_Z,
+ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z,
.check_connect = always_connected,
.tx_fixup = zaurus_tx_fixup,
.bind = blan_mdlm_bind,