summaryrefslogtreecommitdiff
path: root/drivers/net/usb
diff options
context:
space:
mode:
authorMarcel Ziswiler <marcel.ziswiler@toradex.com>2012-11-12 15:28:39 +0100
committerMarcel Ziswiler <marcel.ziswiler@toradex.com>2012-11-12 15:28:39 +0100
commitf987e832a9e79d2ce8009a5ea9c7b677624b3b30 (patch)
tree0dd09a5e6b4c60ee0a9916907dfc2cda83f3e496 /drivers/net/usb
parentf737b7f46a72c099cf8ac88baff02fbf61b1a47c (diff)
parentfc993d9bc48f772133d8cd156c67c296477db070 (diff)
Merge branch 'l4t/l4t-r16-r2' into colibri
Conflicts: arch/arm/mach-tegra/tegra3_usb_phy.c arch/arm/mach-tegra/usb_phy.c drivers/usb/gadget/tegra_udc.c drivers/usb/otg/Makefile drivers/video/tegra/fb.c sound/soc/tegra/tegra_pcm.c
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/cdc_ether.c8
-rw-r--r--drivers/net/usb/raw_ip_net.c185
-rw-r--r--drivers/net/usb/usbnet.c74
3 files changed, 154 insertions, 113 deletions
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index c31b1185f492..14bda2ecabde 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -604,6 +604,14 @@ static const struct usb_device_id products [] = {
USB_DEVICE(0x0489,0xE03A),
.driver_info = (unsigned long)&rmnet_info,
},
+
+/* ZM5250 */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ USB_DEVICE(0x19D2,0x1554),
+ .driver_info = (unsigned long)&rmnet_info,
+},
/*
* WHITELIST!!!
*
diff --git a/drivers/net/usb/raw_ip_net.c b/drivers/net/usb/raw_ip_net.c
index 1b3b89b3c274..dc9b5e61fd9e 100644
--- a/drivers/net/usb/raw_ip_net.c
+++ b/drivers/net/usb/raw_ip_net.c
@@ -3,7 +3,7 @@
*
* USB network driver for RAW-IP modems.
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation. All rights reserved.
*
* 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
@@ -42,25 +42,24 @@
#endif /* USB_NET_BUFSIZ */
/* maximum interface number supported */
-#define MAX_INTFS 3
+#define MAX_INTFS 5
MODULE_LICENSE("GPL");
-int g_i;
+static int g_i;
-int max_intfs = MAX_INTFS;
-unsigned long usb_net_raw_ip_vid = 0x1519;
-unsigned long usb_net_raw_ip_pid = 0x0020;
-unsigned long usb_net_raw_ip_intf[MAX_INTFS] = { 0x03, 0x05, 0x07 };
+/* To support more rmnet interfaces, increase the default max_intfs or
+ * pass kernel module parameter.
+ * e.g. insmod raw_ip_net.ko max_intfs=5
+ */
+static int max_intfs = 2; /* default number of interfaces */
+
+static unsigned long usb_net_raw_ip_intf[MAX_INTFS] = { 3, 5, 9, 11, 13};
unsigned long usb_net_raw_ip_rx_debug;
unsigned long usb_net_raw_ip_tx_debug;
module_param(max_intfs, int, 0644);
MODULE_PARM_DESC(max_intfs, "usb net (raw-ip) - max. interfaces supported");
-module_param(usb_net_raw_ip_vid, ulong, 0644);
-MODULE_PARM_DESC(usb_net_raw_ip_vid, "usb net (raw-ip) - USB VID");
-module_param(usb_net_raw_ip_pid, ulong, 0644);
-MODULE_PARM_DESC(usb_net_raw_ip_pid, "usb net (raw-ip) - USB PID");
module_param(usb_net_raw_ip_rx_debug, ulong, 0644);
MODULE_PARM_DESC(usb_net_raw_ip_rx_debug, "usb net (raw-ip) - rx debug");
module_param(usb_net_raw_ip_tx_debug, ulong, 0644);
@@ -99,11 +98,10 @@ struct baseband_usb {
int susp_count;
};
-static struct baseband_usb *baseband_usb_net[MAX_INTFS] = { 0, 0, 0};
+static struct baseband_usb *baseband_usb_net[MAX_INTFS] = { 0, 0, 0, 0, 0};
-static struct net_device *usb_net_raw_ip_dev[MAX_INTFS] = { 0, 0, 0};
+static struct net_device *usb_net_raw_ip_dev[MAX_INTFS] = { 0, 0, 0, 0, 0};
-static unsigned int g_usb_interface_index[MAX_INTFS];
static struct usb_interface *g_usb_interface[MAX_INTFS];
static int usb_net_raw_ip_rx_urb_submit(struct baseband_usb *usb);
@@ -117,7 +115,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb);
static int baseband_usb_driver_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- int i = g_i;
+ int i = g_i, j;
pr_debug("%s(%d) { intf %p id %p\n", __func__, __LINE__, intf, id);
@@ -138,16 +136,18 @@ static int baseband_usb_driver_probe(struct usb_interface *intf,
pr_debug("intf->cur_altsetting->desc.iInterface %02x\n",
intf->cur_altsetting->desc.iInterface);
- if (g_usb_interface_index[i] !=
- intf->cur_altsetting->desc.bInterfaceNumber) {
- pr_debug("%s(%d) } -ENODEV\n", __func__, __LINE__);
- return -ENODEV;
- } else {
- g_usb_interface[i] = intf;
+ /* register interfaces that are assigned to raw-ip */
+ for (j = 0; j < max_intfs; j++) {
+ if (usb_net_raw_ip_intf[j] ==
+ intf->cur_altsetting->desc.bInterfaceNumber) {
+ pr_info("%s: raw_ip using interface %d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+ g_usb_interface[j] = intf;
+ return 0;
+ }
}
-
pr_debug("%s(%d) }\n", __func__, __LINE__);
- return 0;
+ return -ENODEV;
}
static void baseband_usb_driver_disconnect(struct usb_interface *intf)
@@ -350,49 +350,27 @@ static int baseband_usb_driver_reset_resume(struct usb_interface *intf)
}
#endif /* CONFIG_PM */
-static struct usb_device_id baseband_usb_driver_id_table[MAX_INTFS][2];
-
-static char baseband_usb_driver_name[MAX_INTFS][32];
+static struct usb_device_id baseband_usb_driver_id_table[] = {
+ /* xmm modem vid, pid */
+ { USB_DEVICE(0x1519, 0x0020), },
+ { },
+};
-static struct usb_driver baseband_usb_driver[MAX_INTFS] = {
- {
- .name = baseband_usb_driver_name[0],
- .probe = baseband_usb_driver_probe,
- .disconnect = baseband_usb_driver_disconnect,
- .id_table = baseband_usb_driver_id_table[0],
-#ifdef CONFIG_PM
- .suspend = baseband_usb_driver_suspend,
- .resume = baseband_usb_driver_resume,
- .reset_resume = baseband_usb_driver_reset_resume,
- .supports_autosuspend = 1,
-#endif
- },
- {
- .name = baseband_usb_driver_name[1],
+static struct usb_driver baseband_usb_driver = {
+ .name = "bb_raw_ip_net",
.probe = baseband_usb_driver_probe,
.disconnect = baseband_usb_driver_disconnect,
- .id_table = baseband_usb_driver_id_table[1],
+ .id_table = baseband_usb_driver_id_table,
#ifdef CONFIG_PM
.suspend = baseband_usb_driver_suspend,
.resume = baseband_usb_driver_resume,
.reset_resume = baseband_usb_driver_reset_resume,
.supports_autosuspend = 1,
#endif
- },
- {
- .name = baseband_usb_driver_name[2],
- .probe = baseband_usb_driver_probe,
- .disconnect = baseband_usb_driver_disconnect,
- .id_table = baseband_usb_driver_id_table[2],
-#ifdef CONFIG_PM
- .suspend = baseband_usb_driver_suspend,
- .resume = baseband_usb_driver_resume,
- .reset_resume = baseband_usb_driver_reset_resume,
- .supports_autosuspend = 1,
-#endif
- },
};
+MODULE_DEVICE_TABLE(usb, baseband_usb_driver_id_table);
+
static void find_usb_pipe(struct baseband_usb *usb)
{
struct usb_device *usbdev = usb->usb.device;
@@ -434,13 +412,10 @@ static void find_usb_pipe(struct baseband_usb *usb)
void baseband_usb_close(struct baseband_usb *usb);
-struct baseband_usb *baseband_usb_open(int index,
- unsigned int vid,
- unsigned int pid,
- unsigned int intf)
+struct baseband_usb *baseband_usb_open(int index, unsigned int intf)
{
struct baseband_usb *usb;
- int err;
+ int i;
pr_debug("baseband_usb_open {\n");
@@ -453,37 +428,25 @@ struct baseband_usb *baseband_usb_open(int index,
/* create semaphores */
sema_init(&usb->sem, 1);
- /* open usb driver */
- sprintf(baseband_usb_driver_name[index],
- "baseband_usb_%x_%x_%x",
- vid, pid, intf);
- baseband_usb_driver_id_table[index][0].match_flags =
- USB_DEVICE_ID_MATCH_DEVICE;
- baseband_usb_driver_id_table[index][0].idVendor = vid;
- baseband_usb_driver_id_table[index][0].idProduct = pid;
- g_usb_interface_index[index] = intf;
- g_usb_interface[index] = (struct usb_interface *) 0;
- err = usb_register(&baseband_usb_driver[index]);
- if (err < 0) {
- pr_err("cannot open usb driver - err %d\n", err);
- kfree(usb);
- return (struct baseband_usb *) 0;
- }
+ /* open usb interface */
usb->baseband_index = index;
- usb->usb.driver = &baseband_usb_driver[index];
+ usb->usb.driver = &baseband_usb_driver;
if (!g_usb_interface[index]) {
- pr_err("cannot open usb driver - !g_usb_interface[%d]\n",
- index);
- usb_deregister(usb->usb.driver);
- kfree(usb);
- return (struct baseband_usb *) 0;
+ /* wait for usb probe */
+ for (i = 0; i < 50; i++)
+ if (!g_usb_interface[index])
+ msleep(20);
+ if (!g_usb_interface[index]) {
+ pr_err("can't open usb: !g_usb_interface[%d]\n", index);
+ kfree(usb);
+ return NULL;
+ }
}
usb->usb.device = interface_to_usbdev(g_usb_interface[index]);
usb->usb.interface = g_usb_interface[index];
find_usb_pipe(usb);
usb->usb.rx_urb = (struct urb *) 0;
usb->usb.tx_urb = (struct urb *) 0;
- g_usb_interface_index[index] = ~0U;
g_usb_interface[index] = (struct usb_interface *) 0;
pr_debug("usb->usb.driver->name %s\n", usb->usb.driver->name);
pr_debug("usb->usb.device %p\n", usb->usb.device);
@@ -509,12 +472,7 @@ void baseband_usb_close(struct baseband_usb *usb)
return;
/* close usb driver */
- if (usb->usb.driver) {
- pr_debug("close usb driver {\n");
- usb_deregister(usb->usb.driver);
- usb->usb.driver = (struct usb_driver *) 0;
- pr_debug("close usb driver }\n");
- }
+ usb->usb.driver = (struct usb_driver *) 0;
/* destroy semaphores */
memset(&usb->sem, 0, sizeof(usb->sem));
@@ -746,10 +704,28 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb)
skb_reserve(skb, NET_IP_ALIGN);
dst = skb_put(skb, 14);
memcpy(dst, ethernet_header, 14);
+ if ((((unsigned char *) urb->transfer_buffer)[0]
+ & 0xf0) == 0x60) {
+ /* ipv6 ether type */
+ dst[12] = 0x86;
+ dst[13] = 0xdd;
+ }
dst = skb_put(skb, urb->actual_length);
memcpy(dst, urb->transfer_buffer, urb->actual_length);
skb->protocol = eth_type_trans(skb,
usb_net_raw_ip_dev[i]);
+ pr_debug("%s: ntohs(skb->protocol) %04x (%s)\n",
+ __func__, ntohs(skb->protocol),
+ (ntohs(skb->protocol) == 0x0800)
+ ? "IPv4"
+ : (ntohs(skb->protocol) == 0x86dd)
+ ? "IPv6"
+ : "unknown");
+ pr_debug("%s: %02x %02x %02x %02x\n", __func__,
+ ((unsigned char *)urb->transfer_buffer)[0],
+ ((unsigned char *)urb->transfer_buffer)[1],
+ ((unsigned char *)urb->transfer_buffer)[2],
+ ((unsigned char *)urb->transfer_buffer)[3]);
/* pass skb to network stack */
if (netif_rx(skb) < 0) {
pr_err("usb_net_raw_ip_rx_urb_comp_work - "
@@ -892,6 +868,18 @@ static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb,
usb_net_raw_ip_tx_urb_comp,
usb);
urb->transfer_flags = URB_ZERO_PACKET;
+ pr_debug("%s: ntohs(skb->protocol) %04x (%s)\n",
+ __func__, ntohs(skb->protocol),
+ (ntohs(skb->protocol) == 0x0800)
+ ? "IPv4"
+ : (ntohs(skb->protocol) == 0x86dd)
+ ? "IPv6"
+ : "unknown");
+ pr_debug("%s: %02x %02x %02x %02x\n", __func__,
+ ((unsigned char *)urb->transfer_buffer)[0],
+ ((unsigned char *)urb->transfer_buffer)[1],
+ ((unsigned char *)urb->transfer_buffer)[2],
+ ((unsigned char *)urb->transfer_buffer)[3]);
/* queue tx urb work */
usb_anchor_urb(urb, &usb->usb.tx_urb_deferred);
@@ -923,8 +911,8 @@ static void usb_net_raw_ip_tx_urb_work(struct work_struct *work)
/* check if usb interface disconnected */
if (!usb->usb.interface) {
- pr_err("%s: not submitting tx urb %p -interface disconnected\n",
- __func__, urb);
+ pr_err("%s: not submitting tx urb -interface disconnected\n",
+ __func__);
return;
}
@@ -1044,12 +1032,18 @@ static int usb_net_raw_ip_init(void)
pr_debug("usb_net_raw_ip_init {\n");
+ err = usb_register(&baseband_usb_driver);
+ if (err < 0) {
+ pr_err("cannot open usb driver - err %d\n", err);
+ return err;
+ }
+
/* create multiple raw-ip network devices */
for (i = 0; i < max_intfs; i++) {
/* open baseband usb */
g_i = i;
- baseband_usb_net[i] = baseband_usb_open(i, usb_net_raw_ip_vid,
- usb_net_raw_ip_pid, usb_net_raw_ip_intf[i]);
+ baseband_usb_net[i] = baseband_usb_open(i,
+ usb_net_raw_ip_intf[i]);
if (!baseband_usb_net[i]) {
pr_err("cannot open baseband usb net\n");
err = -1;
@@ -1138,6 +1132,7 @@ error_exit:
baseband_usb_net[i] = (struct baseband_usb *) 0;
}
}
+ usb_deregister(&baseband_usb_driver);
return err;
}
@@ -1183,6 +1178,10 @@ static void usb_net_raw_ip_exit(void)
}
}
+ pr_debug("close usb driver {\n");
+ usb_deregister(&baseband_usb_driver);
+ pr_debug("close usb driver }\n");
+
pr_debug("usb_net_raw_ip_exit }\n");
}
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 82965e20e076..930a4c1959d2 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -210,6 +210,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf)
} else {
usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
buf, maxp, intr_complete, dev, period);
+ dev->interrupt->transfer_flags |= URB_FREE_BUFFER;
dev_dbg(&intf->dev,
"status ep%din, %d bytes period %d\n",
usb_pipeendpoint(pipe), maxp, period);
@@ -277,17 +278,32 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu)
}
EXPORT_SYMBOL_GPL(usbnet_change_mtu);
+/* The caller must hold list->lock */
+static void __usbnet_queue_skb(struct sk_buff_head *list,
+ struct sk_buff *newsk, enum skb_state state)
+{
+ struct skb_data *entry = (struct skb_data *) newsk->cb;
+
+ __skb_queue_tail(list, newsk);
+ entry->state = state;
+}
+
/*-------------------------------------------------------------------------*/
/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
* completion callbacks. 2.5 should have fixed those bugs...
*/
-static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
+static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
+ struct sk_buff_head *list, enum skb_state state)
{
unsigned long flags;
+ enum skb_state old_state;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
spin_lock_irqsave(&list->lock, flags);
+ old_state = entry->state;
+ entry->state = state;
__skb_unlink(skb, list);
spin_unlock(&list->lock);
spin_lock(&dev->done.lock);
@@ -295,6 +311,7 @@ static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_hea
if (dev->done.qlen == 1)
tasklet_schedule(&dev->bh);
spin_unlock_irqrestore(&dev->done.lock, flags);
+ return old_state;
}
/* some work can't be done in tasklets, so we use keventd
@@ -324,18 +341,17 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
unsigned long lockflags;
size_t size = dev->rx_urb_size;
- if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
+ skb = __netdev_alloc_skb_ip_align(dev->net, size, flags);
+ if (!skb) {
netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
usb_free_urb (urb);
return -ENOMEM;
}
- skb_reserve (skb, NET_IP_ALIGN);
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
entry->dev = dev;
- entry->state = rx_start;
entry->length = 0;
usb_fill_bulk_urb (urb, dev->udev, dev->in,
@@ -367,7 +383,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
tasklet_schedule (&dev->bh);
break;
case 0:
- __skb_queue_tail (&dev->rxq, skb);
+ __usbnet_queue_skb(&dev->rxq, skb, rx_start);
}
} else {
netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
@@ -418,16 +434,17 @@ static void rx_complete (struct urb *urb)
struct skb_data *entry = (struct skb_data *) skb->cb;
struct usbnet *dev = entry->dev;
int urb_status = urb->status;
+ enum skb_state state;
skb_put (skb, urb->actual_length);
- entry->state = rx_done;
+ state = rx_done;
entry->urb = NULL;
switch (urb_status) {
/* success */
case 0:
if (skb->len < dev->net->hard_header_len) {
- entry->state = rx_cleanup;
+ state = rx_cleanup;
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
netif_dbg(dev, rx_err, dev->net,
@@ -466,7 +483,7 @@ static void rx_complete (struct urb *urb)
"rx throttle %d\n", urb_status);
}
block:
- entry->state = rx_cleanup;
+ state = rx_cleanup;
entry->urb = urb;
urb = NULL;
break;
@@ -477,18 +494,20 @@ block:
// FALLTHROUGH
default:
- entry->state = rx_cleanup;
+ state = rx_cleanup;
dev->net->stats.rx_errors++;
netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status);
break;
}
- defer_bh(dev, skb, &dev->rxq);
+ state = defer_bh(dev, skb, &dev->rxq, state);
if (urb) {
if (netif_running (dev->net) &&
- !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ !test_bit (EVENT_RX_HALT, &dev->flags) &&
+ state != unlink_start) {
rx_submit (dev, urb, GFP_ATOMIC);
+ usb_mark_last_busy(dev->udev);
return;
}
usb_free_urb (urb);
@@ -573,18 +592,34 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq);
static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{
unsigned long flags;
- struct sk_buff *skb, *skbnext;
+ struct sk_buff *skb;
int count = 0;
spin_lock_irqsave (&q->lock, flags);
- skb_queue_walk_safe(q, skb, skbnext) {
+ while (!skb_queue_empty(q)) {
struct skb_data *entry;
struct urb *urb;
int retval;
- entry = (struct skb_data *) skb->cb;
+ skb_queue_walk(q, skb) {
+ entry = (struct skb_data *) skb->cb;
+ if (entry->state != unlink_start)
+ goto found;
+ }
+ break;
+found:
+ entry->state = unlink_start;
urb = entry->urb;
+ /*
+ * Get reference count of the URB to avoid it to be
+ * freed during usb_unlink_urb, which may trigger
+ * use-after-free problem inside usb_unlink_urb since
+ * usb_unlink_urb is always racing with .complete
+ * handler(include defer_bh).
+ */
+ usb_get_urb(urb);
+ spin_unlock_irqrestore(&q->lock, flags);
// during some PM-driven resume scenarios,
// these (async) unlinks complete immediately
retval = usb_unlink_urb (urb);
@@ -592,6 +627,8 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
else
count++;
+ usb_put_urb(urb);
+ spin_lock_irqsave(&q->lock, flags);
}
spin_unlock_irqrestore (&q->lock, flags);
return count;
@@ -1022,9 +1059,7 @@ static void tx_complete (struct urb *urb)
}
usb_autopm_put_interface_async(dev->intf);
- urb->dev = NULL;
- entry->state = tx_done;
- defer_bh(dev, skb, &dev->txq);
+ (void) defer_bh(dev, skb, &dev->txq, tx_done);
}
/*-------------------------------------------------------------------------*/
@@ -1077,7 +1112,6 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
entry->dev = dev;
- entry->state = tx_start;
entry->length = length;
usb_fill_bulk_urb (urb, dev->udev, dev->out,
@@ -1136,7 +1170,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
break;
case 0:
net->trans_start = jiffies;
- __skb_queue_tail (&dev->txq, skb);
+ __usbnet_queue_skb(&dev->txq, skb, tx_start);
if (dev->txq.qlen >= TX_QLEN (dev))
netif_stop_queue (net);
}
@@ -1531,7 +1565,7 @@ int usbnet_resume (struct usb_interface *intf)
if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {
if (!(dev->txq.qlen >= TX_QLEN(dev)))
- netif_start_queue(dev->net);
+ netif_tx_wake_all_queues(dev->net);
tasklet_schedule (&dev->bh);
}
}