diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2012-11-12 15:28:39 +0100 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2012-11-12 15:28:39 +0100 |
commit | f987e832a9e79d2ce8009a5ea9c7b677624b3b30 (patch) | |
tree | 0dd09a5e6b4c60ee0a9916907dfc2cda83f3e496 /drivers/net/usb | |
parent | f737b7f46a72c099cf8ac88baff02fbf61b1a47c (diff) | |
parent | fc993d9bc48f772133d8cd156c67c296477db070 (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.c | 8 | ||||
-rw-r--r-- | drivers/net/usb/raw_ip_net.c | 185 | ||||
-rw-r--r-- | drivers/net/usb/usbnet.c | 74 |
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); } } |