diff options
Diffstat (limited to 'ANDROID_3.4.5/drivers/net/usb/usbnet.c')
-rw-r--r-- | ANDROID_3.4.5/drivers/net/usb/usbnet.c | 1668 |
1 files changed, 0 insertions, 1668 deletions
diff --git a/ANDROID_3.4.5/drivers/net/usb/usbnet.c b/ANDROID_3.4.5/drivers/net/usb/usbnet.c deleted file mode 100644 index 20838aba..00000000 --- a/ANDROID_3.4.5/drivers/net/usb/usbnet.c +++ /dev/null @@ -1,1668 +0,0 @@ -/* - * USB Network driver infrastructure - * Copyright (C) 2000-2005 by David Brownell - * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.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 - */ - -/* - * This is a generic "USB networking" framework that works with several - * kinds of full and high speed networking devices: host-to-host cables, - * smart usb peripherals, and actual Ethernet adapters. - * - * These devices usually differ in terms of control protocols (if they - * even have one!) and sometimes they define new framing to wrap or batch - * Ethernet packets. Otherwise, they talk to USB pretty much the same, - * so interface (un)binding, endpoint I/O queues, fault handling, and other - * issues can usefully be addressed by this framework. - */ - -// #define DEBUG // error path messages, extra info -// #define VERBOSE // more; success messages - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/ctype.h> -#include <linux/ethtool.h> -#include <linux/workqueue.h> -#include <linux/mii.h> -#include <linux/usb.h> -#include <linux/usb/usbnet.h> -#include <linux/slab.h> -#include <linux/kernel.h> -#include <linux/pm_runtime.h> - -#define DRIVER_VERSION "22-Aug-2005" - - -/*-------------------------------------------------------------------------*/ - -/* - * Nineteen USB 1.1 max size bulk transactions per frame (ms), max. - * Several dozen bytes of IPv4 data can fit in two such transactions. - * One maximum size Ethernet packet takes twenty four of them. - * For high speed, each frame comfortably fits almost 36 max size - * Ethernet packets (so queues should be bigger). - * - * REVISIT qlens should be members of 'struct usbnet'; the goal is to - * let the USB host controller be busy for 5msec or more before an irq - * is required, under load. Jumbograms change the equation. - */ -#define RX_MAX_QUEUE_MEMORY (60 * 1518) -#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ - (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4) -#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ - (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4) - -// reawaken network queue this soon after stopping; else watchdog barks -#define TX_TIMEOUT_JIFFIES (5*HZ) - -// throttle rx/tx briefly after some faults, so khubd might disconnect() -// us (it polls at HZ/4 usually) before we report too many false errors. -#define THROTTLE_JIFFIES (HZ/8) - -// between wakeups -#define UNLINK_TIMEOUT_MS 3 - -/*-------------------------------------------------------------------------*/ - -// randomly generated ethernet address -static u8 node_id [ETH_ALEN]; - -static const char driver_name [] = "usbnet"; - -/* use ethtool to change the level for any given device */ -static int msg_level = -1; -module_param (msg_level, int, 0); -MODULE_PARM_DESC (msg_level, "Override default message level"); - -/*-------------------------------------------------------------------------*/ - - -//add by kevin -extern int wmt_getsyspara(const char *varname, char *varval, int * varlen); -static unsigned char char2hex(char c){ - unsigned char ret=0xff; - if(c>='0'&&c<='9') - ret= c-'0'; - if(c>='a'&&c<='f') - ret= c-'a'+10; - if(c>='A'&&c<='F') - ret= c-'A'+10; - return ret; -} - -void check_set_mac(unsigned char *buf){ - - char ethbuf[50]; - int len = sizeof(ethbuf); - memset((void*)ethbuf, 0, len); - - //wondermedia set bt mac addr - if(!wmt_getsyspara("ethaddr", ethbuf, &len)){ - - printk("wmt eth addr %s %d\n",ethbuf,strlen(ethbuf)); - //ethaddr=00:40:63:70:13:37 - if(strlen(ethbuf)==17){ - buf[0] = (char2hex(ethbuf[0])<<4)|char2hex(ethbuf[1]); - buf[1] = (char2hex(ethbuf[3])<<4)|char2hex(ethbuf[4]); - buf[2] = (char2hex(ethbuf[6])<<4)|char2hex(ethbuf[7]); - buf[3] = (char2hex(ethbuf[9])<<4)|char2hex(ethbuf[10]); - buf[4] = (char2hex(ethbuf[12])<<4)|char2hex(ethbuf[13]); - buf[5] = (char2hex(ethbuf[15])<<4)|char2hex(ethbuf[16]); - - } - //ethaddr=004063701337 - if(strlen(ethbuf)==12){ - buf[0] = (char2hex(ethbuf[0])<<4)|char2hex(ethbuf[1]); - buf[1] = (char2hex(ethbuf[2])<<4)|char2hex(ethbuf[3]); - buf[2] = (char2hex(ethbuf[4])<<4)|char2hex(ethbuf[5]); - buf[3] = (char2hex(ethbuf[6])<<4)|char2hex(ethbuf[7]); - buf[4] = (char2hex(ethbuf[8])<<4)|char2hex(ethbuf[9]); - buf[5] = (char2hex(ethbuf[10])<<4)|char2hex(ethbuf[11]); - } - } -} - - -/* handles CDC Ethernet and many other network "bulk data" interfaces */ -int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf) -{ - int tmp; - struct usb_host_interface *alt = NULL; - struct usb_host_endpoint *in = NULL, *out = NULL; - struct usb_host_endpoint *status = NULL; - - for (tmp = 0; tmp < intf->num_altsetting; tmp++) { - unsigned ep; - - in = out = status = NULL; - alt = intf->altsetting + tmp; - - /* take the first altsetting with in-bulk + out-bulk; - * remember any status endpoint, just in case; - * ignore other endpoints and altsettings. - */ - for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) { - struct usb_host_endpoint *e; - int intr = 0; - - e = alt->endpoint + ep; - switch (e->desc.bmAttributes) { - case USB_ENDPOINT_XFER_INT: - if (!usb_endpoint_dir_in(&e->desc)) - continue; - intr = 1; - /* FALLTHROUGH */ - case USB_ENDPOINT_XFER_BULK: - break; - default: - continue; - } - if (usb_endpoint_dir_in(&e->desc)) { - if (!intr && !in) - in = e; - else if (intr && !status) - status = e; - } else { - if (!out) - out = e; - } - } - if (in && out) - break; - } - if (!alt || !in || !out) - return -EINVAL; - - if (alt->desc.bAlternateSetting != 0 || - !(dev->driver_info->flags & FLAG_NO_SETINT)) { - tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber, - alt->desc.bAlternateSetting); - if (tmp < 0) - return tmp; - } - - dev->in = usb_rcvbulkpipe (dev->udev, - in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - dev->out = usb_sndbulkpipe (dev->udev, - out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - dev->status = status; - return 0; -} -EXPORT_SYMBOL_GPL(usbnet_get_endpoints); - -int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress) -{ - int tmp, i; - unsigned char buf [13]; - - tmp = usb_string(dev->udev, iMACAddress, buf, sizeof buf); - if (tmp != 12) { - dev_dbg(&dev->udev->dev, - "bad MAC string %d fetch, %d\n", iMACAddress, tmp); - if (tmp >= 0) - tmp = -EINVAL; - return tmp; - } - for (i = tmp = 0; i < 6; i++, tmp += 2) - dev->net->dev_addr [i] = - (hex_to_bin(buf[tmp]) << 4) + hex_to_bin(buf[tmp + 1]); - return 0; -} -EXPORT_SYMBOL_GPL(usbnet_get_ethernet_addr); - -static void intr_complete (struct urb *urb); - -static int init_status (struct usbnet *dev, struct usb_interface *intf) -{ - char *buf = NULL; - unsigned pipe = 0; - unsigned maxp; - unsigned period; - - if (!dev->driver_info->status) - return 0; - - pipe = usb_rcvintpipe (dev->udev, - dev->status->desc.bEndpointAddress - & USB_ENDPOINT_NUMBER_MASK); - maxp = usb_maxpacket (dev->udev, pipe, 0); - - /* avoid 1 msec chatter: min 8 msec poll rate */ - period = max ((int) dev->status->desc.bInterval, - (dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3); - - buf = kmalloc (maxp, GFP_KERNEL); - if (buf) { - dev->interrupt = usb_alloc_urb (0, GFP_KERNEL); - if (!dev->interrupt) { - kfree (buf); - return -ENOMEM; - } 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); - } - } - return 0; -} - -/* Passes this packet up the stack, updating its accounting. - * Some link protocols batch packets, so their rx_fixup paths - * can return clones as well as just modify the original skb. - */ -void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) -{ - int status; - - if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { - skb_queue_tail(&dev->rxq_pause, skb); - return; - } - - skb->protocol = eth_type_trans (skb, dev->net); - dev->net->stats.rx_packets++; - dev->net->stats.rx_bytes += skb->len; - - netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n", - skb->len + sizeof (struct ethhdr), skb->protocol); - memset (skb->cb, 0, sizeof (struct skb_data)); - - if (skb_defer_rx_timestamp(skb)) - return; - - status = netif_rx (skb); - if (status != NET_RX_SUCCESS) - netif_dbg(dev, rx_err, dev->net, - "netif_rx status %d\n", status); -} -EXPORT_SYMBOL_GPL(usbnet_skb_return); - - -/*------------------------------------------------------------------------- - * - * Network Device Driver (peer link to "Host Device", from USB host) - * - *-------------------------------------------------------------------------*/ - -int usbnet_change_mtu (struct net_device *net, int new_mtu) -{ - struct usbnet *dev = netdev_priv(net); - int ll_mtu = new_mtu + net->hard_header_len; - int old_hard_mtu = dev->hard_mtu; - int old_rx_urb_size = dev->rx_urb_size; - - if (new_mtu <= 0) - return -EINVAL; - // no second zero-length packet read wanted after mtu-sized packets - if ((ll_mtu % dev->maxpacket) == 0) - return -EDOM; - net->mtu = new_mtu; - - dev->hard_mtu = net->mtu + net->hard_header_len; - if (dev->rx_urb_size == old_hard_mtu) { - dev->rx_urb_size = dev->hard_mtu; - if (dev->rx_urb_size > old_rx_urb_size) - usbnet_unlink_rx_urbs(dev); - } - - return 0; -} -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 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); - __skb_queue_tail(&dev->done, skb); - 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 - * - * NOTE: annoying asymmetry: if it's active, schedule_work() fails, - * but tasklet_schedule() doesn't. hope the failure is rare. - */ -void usbnet_defer_kevent (struct usbnet *dev, int work) -{ - set_bit (work, &dev->flags); - if (!schedule_work (&dev->kevent)) - netdev_err(dev->net, "kevent %d may have been dropped\n", work); - else - netdev_dbg(dev->net, "kevent %d scheduled\n", work); -} -EXPORT_SYMBOL_GPL(usbnet_defer_kevent); - -/*-------------------------------------------------------------------------*/ - -static void rx_complete (struct urb *urb); - -static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) -{ - struct sk_buff *skb; - struct skb_data *entry; - int retval = 0; - unsigned long lockflags; - size_t size = dev->rx_urb_size; - - 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; - } - - entry = (struct skb_data *) skb->cb; - entry->urb = urb; - entry->dev = dev; - entry->length = 0; - - usb_fill_bulk_urb (urb, dev->udev, dev->in, - skb->data, size, rx_complete, skb); - - spin_lock_irqsave (&dev->rxq.lock, lockflags); - - if (netif_running (dev->net) && - netif_device_present (dev->net) && - !test_bit (EVENT_RX_HALT, &dev->flags) && - !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) { - switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) { - case -EPIPE: - usbnet_defer_kevent (dev, EVENT_RX_HALT); - break; - case -ENOMEM: - usbnet_defer_kevent (dev, EVENT_RX_MEMORY); - break; - case -ENODEV: - netif_dbg(dev, ifdown, dev->net, "device gone\n"); - netif_device_detach (dev->net); - break; - case -EHOSTUNREACH: - retval = -ENOLINK; - break; - default: - netif_dbg(dev, rx_err, dev->net, - "rx submit, %d\n", retval); - tasklet_schedule (&dev->bh); - break; - case 0: - __usbnet_queue_skb(&dev->rxq, skb, rx_start); - } - } else { - netif_dbg(dev, ifdown, dev->net, "rx: stopped\n"); - retval = -ENOLINK; - } - spin_unlock_irqrestore (&dev->rxq.lock, lockflags); - if (retval) { - dev_kfree_skb_any (skb); - usb_free_urb (urb); - } - return retval; -} - - -/*-------------------------------------------------------------------------*/ - -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)) { - /* 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) { - /* all data was already cloned from skb inside the driver */ - if (dev->driver_info->flags & FLAG_MULTI_PACKET) - dev_kfree_skb_any(skb); - else - usbnet_skb_return(dev, skb); - return; - } - - netif_dbg(dev, rx_err, dev->net, "drop\n"); - dev->net->stats.rx_errors++; -done: - skb_queue_tail(&dev->done, skb); -} - -/*-------------------------------------------------------------------------*/ - -static void rx_complete (struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *) urb->context; - 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); - state = rx_done; - entry->urb = NULL; - - switch (urb_status) { - /* success */ - case 0: - if (skb->len < dev->net->hard_header_len) { - state = rx_cleanup; - dev->net->stats.rx_errors++; - dev->net->stats.rx_length_errors++; - netif_dbg(dev, rx_err, dev->net, - "rx length %d\n", skb->len); - } - break; - - /* stalls need manual reset. this is rare ... except that - * when going through USB 2.0 TTs, unplug appears this way. - * we avoid the highspeed version of the ETIMEDOUT/EILSEQ - * storm, recovering as needed. - */ - case -EPIPE: - dev->net->stats.rx_errors++; - usbnet_defer_kevent (dev, EVENT_RX_HALT); - // FALLTHROUGH - - /* software-driven interface shutdown */ - case -ECONNRESET: /* async unlink */ - case -ESHUTDOWN: /* hardware gone */ - netif_dbg(dev, ifdown, dev->net, - "rx shutdown, code %d\n", urb_status); - goto block; - - /* we get controller i/o faults during khubd disconnect() delays. - * throttle down resubmits, to avoid log floods; just temporarily, - * so we still recover when the fault isn't a khubd delay. - */ - case -EPROTO: - case -ETIME: - case -EILSEQ: - dev->net->stats.rx_errors++; - if (!timer_pending (&dev->delay)) { - mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES); - netif_dbg(dev, link, dev->net, - "rx throttle %d\n", urb_status); - } -block: - state = rx_cleanup; - entry->urb = urb; - urb = NULL; - break; - - /* data overrun ... flush fifo? */ - case -EOVERFLOW: - dev->net->stats.rx_over_errors++; - // FALLTHROUGH - - default: - state = rx_cleanup; - dev->net->stats.rx_errors++; - netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status); - break; - } - - state = defer_bh(dev, skb, &dev->rxq, state); - - if (urb) { - if (netif_running (dev->net) && - !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); - } - netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n"); -} - -static void intr_complete (struct urb *urb) -{ - struct usbnet *dev = urb->context; - int status = urb->status; - - switch (status) { - /* success */ - case 0: - dev->driver_info->status(dev, urb); - break; - - /* software-driven interface shutdown */ - case -ENOENT: /* urb killed */ - case -ESHUTDOWN: /* hardware gone */ - netif_dbg(dev, ifdown, dev->net, - "intr shutdown, code %d\n", status); - return; - - /* NOTE: not throttling like RX/TX, since this endpoint - * already polls infrequently - */ - default: - netdev_dbg(dev->net, "intr status %d\n", status); - break; - } - - if (!netif_running (dev->net)) - return; - - memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status != 0) - netif_err(dev, timer, dev->net, - "intr resubmit --> %d\n", status); -} - -/*-------------------------------------------------------------------------*/ -void usbnet_pause_rx(struct usbnet *dev) -{ - set_bit(EVENT_RX_PAUSED, &dev->flags); - - netif_dbg(dev, rx_status, dev->net, "paused rx queue enabled\n"); -} -EXPORT_SYMBOL_GPL(usbnet_pause_rx); - -void usbnet_resume_rx(struct usbnet *dev) -{ - struct sk_buff *skb; - int num = 0; - - clear_bit(EVENT_RX_PAUSED, &dev->flags); - - while ((skb = skb_dequeue(&dev->rxq_pause)) != NULL) { - usbnet_skb_return(dev, skb); - num++; - } - - tasklet_schedule(&dev->bh); - - netif_dbg(dev, rx_status, dev->net, - "paused rx queue disabled, %d skbs requeued\n", num); -} -EXPORT_SYMBOL_GPL(usbnet_resume_rx); - -void usbnet_purge_paused_rxq(struct usbnet *dev) -{ - skb_queue_purge(&dev->rxq_pause); -} -EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq); - -/*-------------------------------------------------------------------------*/ - -// unlink pending rx/tx; completion handlers do all other cleanup - -static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) -{ - unsigned long flags; - struct sk_buff *skb; - int count = 0; - - spin_lock_irqsave (&q->lock, flags); - while (!skb_queue_empty(q)) { - struct skb_data *entry; - struct urb *urb; - int retval; - - 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); - if (retval != -EINPROGRESS && retval != 0) - 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; -} - -// Flush all pending rx urbs -// minidrivers may need to do this when the MTU changes - -void usbnet_unlink_rx_urbs(struct usbnet *dev) -{ - if (netif_running(dev->net)) { - (void) unlink_urbs (dev, &dev->rxq); - tasklet_schedule(&dev->bh); - } -} -EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs); - -/*-------------------------------------------------------------------------*/ - -// precondition: never called in_interrupt -static void usbnet_terminate_urbs(struct usbnet *dev) -{ - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup); - DECLARE_WAITQUEUE(wait, current); - int temp; - - /* ensure there are no more active urbs */ - add_wait_queue(&unlink_wakeup, &wait); - set_current_state(TASK_UNINTERRUPTIBLE); - dev->wait = &unlink_wakeup; - temp = unlink_urbs(dev, &dev->txq) + - unlink_urbs(dev, &dev->rxq); - - /* maybe wait for deletions to finish. */ - while (!skb_queue_empty(&dev->rxq) - && !skb_queue_empty(&dev->txq) - && !skb_queue_empty(&dev->done)) { - schedule_timeout(msecs_to_jiffies(UNLINK_TIMEOUT_MS)); - set_current_state(TASK_UNINTERRUPTIBLE); - netif_dbg(dev, ifdown, dev->net, - "waited for %d urb completions\n", temp); - } - set_current_state(TASK_RUNNING); - dev->wait = NULL; - remove_wait_queue(&unlink_wakeup, &wait); -} - -int usbnet_stop (struct net_device *net) -{ - struct usbnet *dev = netdev_priv(net); - struct driver_info *info = dev->driver_info; - int retval; - - clear_bit(EVENT_DEV_OPEN, &dev->flags); - netif_stop_queue (net); - - netif_info(dev, ifdown, dev->net, - "stop stats: rx/tx %lu/%lu, errs %lu/%lu\n", - net->stats.rx_packets, net->stats.tx_packets, - net->stats.rx_errors, net->stats.tx_errors); - - /* allow minidriver to stop correctly (wireless devices to turn off - * radio etc) */ - if (info->stop) { - retval = info->stop(dev); - if (retval < 0) - netif_info(dev, ifdown, dev->net, - "stop fail (%d) usbnet usb-%s-%s, %s\n", - retval, - dev->udev->bus->bus_name, dev->udev->devpath, - info->description); - } - - if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) - usbnet_terminate_urbs(dev); - - usb_kill_urb(dev->interrupt); - - usbnet_purge_paused_rxq(dev); - - /* deferred work (task, timer, softirq) must also stop. - * can't flush_scheduled_work() until we drop rtnl (later), - * else workers could deadlock; so make workers a NOP. - */ - dev->flags = 0; - del_timer_sync (&dev->delay); - tasklet_kill (&dev->bh); - if (info->manage_power) - info->manage_power(dev, 0); - else - usb_autopm_put_interface(dev->intf); - - return 0; -} -EXPORT_SYMBOL_GPL(usbnet_stop); - -/*-------------------------------------------------------------------------*/ - -// posts reads, and enables write queuing - -// precondition: never called in_interrupt - -int usbnet_open (struct net_device *net) -{ - struct usbnet *dev = netdev_priv(net); - int retval; - struct driver_info *info = dev->driver_info; - - if ((retval = usb_autopm_get_interface(dev->intf)) < 0) { - netif_info(dev, ifup, dev->net, - "resumption fail (%d) usbnet usb-%s-%s, %s\n", - retval, - dev->udev->bus->bus_name, - dev->udev->devpath, - info->description); - goto done_nopm; - } - - // put into "known safe" state - if (info->reset && (retval = info->reset (dev)) < 0) { - netif_info(dev, ifup, dev->net, - "open reset fail (%d) usbnet usb-%s-%s, %s\n", - retval, - dev->udev->bus->bus_name, - dev->udev->devpath, - info->description); - goto done; - } - - // insist peer be connected - if (info->check_connect && (retval = info->check_connect (dev)) < 0) { - netif_dbg(dev, ifup, dev->net, "can't open; %d\n", retval); - goto done; - } - - /* start any status interrupt transfer */ - if (dev->interrupt) { - retval = usb_submit_urb (dev->interrupt, GFP_KERNEL); - if (retval < 0) { - netif_err(dev, ifup, dev->net, - "intr submit %d\n", retval); - goto done; - } - } - - set_bit(EVENT_DEV_OPEN, &dev->flags); - netif_start_queue (net); - netif_info(dev, ifup, dev->net, - "open: enable queueing (rx %d, tx %d) mtu %d %s framing\n", - (int)RX_QLEN(dev), (int)TX_QLEN(dev), - dev->net->mtu, - (dev->driver_info->flags & FLAG_FRAMING_NC) ? "NetChip" : - (dev->driver_info->flags & FLAG_FRAMING_GL) ? "GeneSys" : - (dev->driver_info->flags & FLAG_FRAMING_Z) ? "Zaurus" : - (dev->driver_info->flags & FLAG_FRAMING_RN) ? "RNDIS" : - (dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" : - "simple"); - - // delay posting reads until we're fully open - tasklet_schedule (&dev->bh); - if (info->manage_power) { - retval = info->manage_power(dev, 1); - if (retval < 0) - goto done; - usb_autopm_put_interface(dev->intf); - } - return retval; - -done: - usb_autopm_put_interface(dev->intf); -done_nopm: - return retval; -} -EXPORT_SYMBOL_GPL(usbnet_open); - -/*-------------------------------------------------------------------------*/ - -/* ethtool methods; minidrivers may need to add some more, but - * they'll probably want to use this base set. - */ - -int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd) -{ - struct usbnet *dev = netdev_priv(net); - - if (!dev->mii.mdio_read) - return -EOPNOTSUPP; - - return mii_ethtool_gset(&dev->mii, cmd); -} -EXPORT_SYMBOL_GPL(usbnet_get_settings); - -int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd) -{ - struct usbnet *dev = netdev_priv(net); - int retval; - - if (!dev->mii.mdio_write) - return -EOPNOTSUPP; - - retval = mii_ethtool_sset(&dev->mii, cmd); - - /* link speed/duplex might have changed */ - if (dev->driver_info->link_reset) - dev->driver_info->link_reset(dev); - - return retval; - -} -EXPORT_SYMBOL_GPL(usbnet_set_settings); - -u32 usbnet_get_link (struct net_device *net) -{ - struct usbnet *dev = netdev_priv(net); - - /* If a check_connect is defined, return its result */ - if (dev->driver_info->check_connect) - return dev->driver_info->check_connect (dev) == 0; - - /* if the device has mii operations, use those */ - if (dev->mii.mdio_read) - return mii_link_ok(&dev->mii); - - /* Otherwise, dtrt for drivers calling netif_carrier_{on,off} */ - return ethtool_op_get_link(net); -} -EXPORT_SYMBOL_GPL(usbnet_get_link); - -int usbnet_nway_reset(struct net_device *net) -{ - struct usbnet *dev = netdev_priv(net); - - if (!dev->mii.mdio_write) - return -EOPNOTSUPP; - - return mii_nway_restart(&dev->mii); -} -EXPORT_SYMBOL_GPL(usbnet_nway_reset); - -void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info) -{ - struct usbnet *dev = netdev_priv(net); - - strncpy (info->driver, dev->driver_name, sizeof info->driver); - strncpy (info->version, DRIVER_VERSION, sizeof info->version); - strncpy (info->fw_version, dev->driver_info->description, - sizeof info->fw_version); - usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info); -} -EXPORT_SYMBOL_GPL(usbnet_get_drvinfo); - -u32 usbnet_get_msglevel (struct net_device *net) -{ - struct usbnet *dev = netdev_priv(net); - - return dev->msg_enable; -} -EXPORT_SYMBOL_GPL(usbnet_get_msglevel); - -void usbnet_set_msglevel (struct net_device *net, u32 level) -{ - struct usbnet *dev = netdev_priv(net); - - dev->msg_enable = level; -} -EXPORT_SYMBOL_GPL(usbnet_set_msglevel); - -/* drivers may override default ethtool_ops in their bind() routine */ -static const struct ethtool_ops usbnet_ethtool_ops = { - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, - .get_link = usbnet_get_link, - .nway_reset = usbnet_nway_reset, - .get_drvinfo = usbnet_get_drvinfo, - .get_msglevel = usbnet_get_msglevel, - .set_msglevel = usbnet_set_msglevel, -}; - -/*-------------------------------------------------------------------------*/ - -/* work that cannot be done in interrupt context uses keventd. - * - * NOTE: with 2.5 we could do more of this using completion callbacks, - * especially now that control transfers can be queued. - */ -static void -kevent (struct work_struct *work) -{ - struct usbnet *dev = - container_of(work, struct usbnet, kevent); - int status; - - /* usb_clear_halt() needs a thread context */ - if (test_bit (EVENT_TX_HALT, &dev->flags)) { - unlink_urbs (dev, &dev->txq); - status = usb_autopm_get_interface(dev->intf); - if (status < 0) - goto fail_pipe; - status = usb_clear_halt (dev->udev, dev->out); - usb_autopm_put_interface(dev->intf); - if (status < 0 && - status != -EPIPE && - status != -ESHUTDOWN) { - if (netif_msg_tx_err (dev)) -fail_pipe: - netdev_err(dev->net, "can't clear tx halt, status %d\n", - status); - } else { - clear_bit (EVENT_TX_HALT, &dev->flags); - if (status != -ESHUTDOWN) - netif_wake_queue (dev->net); - } - } - if (test_bit (EVENT_RX_HALT, &dev->flags)) { - unlink_urbs (dev, &dev->rxq); - status = usb_autopm_get_interface(dev->intf); - if (status < 0) - goto fail_halt; - status = usb_clear_halt (dev->udev, dev->in); - usb_autopm_put_interface(dev->intf); - if (status < 0 && - status != -EPIPE && - status != -ESHUTDOWN) { - if (netif_msg_rx_err (dev)) -fail_halt: - netdev_err(dev->net, "can't clear rx halt, status %d\n", - status); - } else { - clear_bit (EVENT_RX_HALT, &dev->flags); - tasklet_schedule (&dev->bh); - } - } - - /* tasklet could resubmit itself forever if memory is tight */ - if (test_bit (EVENT_RX_MEMORY, &dev->flags)) { - struct urb *urb = NULL; - int resched = 1; - - if (netif_running (dev->net)) - urb = usb_alloc_urb (0, GFP_KERNEL); - else - clear_bit (EVENT_RX_MEMORY, &dev->flags); - if (urb != NULL) { - clear_bit (EVENT_RX_MEMORY, &dev->flags); - status = usb_autopm_get_interface(dev->intf); - if (status < 0) { - usb_free_urb(urb); - goto fail_lowmem; - } - if (rx_submit (dev, urb, GFP_KERNEL) == -ENOLINK) - resched = 0; - usb_autopm_put_interface(dev->intf); -fail_lowmem: - if (resched) - tasklet_schedule (&dev->bh); - } - } - - if (test_bit (EVENT_LINK_RESET, &dev->flags)) { - struct driver_info *info = dev->driver_info; - int retval = 0; - - clear_bit (EVENT_LINK_RESET, &dev->flags); - status = usb_autopm_get_interface(dev->intf); - if (status < 0) - goto skip_reset; - if(info->link_reset && (retval = info->link_reset(dev)) < 0) { - usb_autopm_put_interface(dev->intf); -skip_reset: - netdev_info(dev->net, "link reset failed (%d) usbnet usb-%s-%s, %s\n", - retval, - dev->udev->bus->bus_name, - dev->udev->devpath, - info->description); - } else { - usb_autopm_put_interface(dev->intf); - } - } - - if (dev->flags) - netdev_dbg(dev->net, "kevent done, flags = 0x%lx\n", dev->flags); -} - -/*-------------------------------------------------------------------------*/ - -static void tx_complete (struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct skb_data *entry = (struct skb_data *) skb->cb; - struct usbnet *dev = entry->dev; - - if (urb->status == 0) { - if (!(dev->driver_info->flags & FLAG_MULTI_PACKET)) - dev->net->stats.tx_packets++; - dev->net->stats.tx_bytes += entry->length; - } else { - dev->net->stats.tx_errors++; - - switch (urb->status) { - case -EPIPE: - usbnet_defer_kevent (dev, EVENT_TX_HALT); - break; - - /* software-driven interface shutdown */ - case -ECONNRESET: // async unlink - case -ESHUTDOWN: // hardware gone - break; - - // like rx, tx gets controller i/o faults during khubd delays - // and so it uses the same throttling mechanism. - case -EPROTO: - case -ETIME: - case -EILSEQ: - usb_mark_last_busy(dev->udev); - if (!timer_pending (&dev->delay)) { - mod_timer (&dev->delay, - jiffies + THROTTLE_JIFFIES); - netif_dbg(dev, link, dev->net, - "tx throttle %d\n", urb->status); - } - netif_stop_queue (dev->net); - break; - default: - netif_dbg(dev, tx_err, dev->net, - "tx err %d\n", entry->urb->status); - break; - } - } - - usb_autopm_put_interface_async(dev->intf); - (void) defer_bh(dev, skb, &dev->txq, tx_done); -} - -/*-------------------------------------------------------------------------*/ - -void usbnet_tx_timeout (struct net_device *net) -{ - struct usbnet *dev = netdev_priv(net); - - unlink_urbs (dev, &dev->txq); - tasklet_schedule (&dev->bh); - - // FIXME: device recovery -- reset? -} -EXPORT_SYMBOL_GPL(usbnet_tx_timeout); - -/*-------------------------------------------------------------------------*/ - -netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, - struct net_device *net) -{ - struct usbnet *dev = netdev_priv(net); - int length; - struct urb *urb = NULL; - struct skb_data *entry; - struct driver_info *info = dev->driver_info; - unsigned long flags; - int retval; - - if (skb) - skb_tx_timestamp(skb); - - // some devices want funky USB-level framing, for - // win32 driver (usually) and/or hardware quirks - if (info->tx_fixup) { - skb = info->tx_fixup (dev, skb, GFP_ATOMIC); - if (!skb) { - if (netif_msg_tx_err(dev)) { - netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); - goto drop; - } else { - /* cdc_ncm collected packet; waits for more */ - goto not_drop; - } - } - } - length = skb->len; - - if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) { - netif_dbg(dev, tx_err, dev->net, "no urb\n"); - goto drop; - } - - entry = (struct skb_data *) skb->cb; - entry->urb = urb; - entry->dev = dev; - entry->length = length; - - usb_fill_bulk_urb (urb, dev->udev, dev->out, - skb->data, skb->len, tx_complete, skb); - - /* don't assume the hardware handles USB_ZERO_PACKET - * NOTE: strictly conforming cdc-ether devices should expect - * the ZLP here, but ignore the one-byte packet. - * NOTE2: CDC NCM specification is different from CDC ECM when - * handling ZLP/short packets, so cdc_ncm driver will make short - * packet itself if needed. - */ - if (length % dev->maxpacket == 0) { - if (!(info->flags & FLAG_SEND_ZLP)) { - if (!(info->flags & FLAG_MULTI_PACKET)) { - urb->transfer_buffer_length++; - if (skb_tailroom(skb)) { - skb->data[skb->len] = 0; - __skb_put(skb, 1); - } - } - } else - urb->transfer_flags |= URB_ZERO_PACKET; - } - - spin_lock_irqsave(&dev->txq.lock, flags); - retval = usb_autopm_get_interface_async(dev->intf); - if (retval < 0) { - spin_unlock_irqrestore(&dev->txq.lock, flags); - goto drop; - } - -#ifdef CONFIG_PM - /* if this triggers the device is still a sleep */ - if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { - /* transmission will be done in resume */ - usb_anchor_urb(urb, &dev->deferred); - /* no use to process more packets */ - netif_stop_queue(net); - spin_unlock_irqrestore(&dev->txq.lock, flags); - netdev_dbg(dev->net, "Delaying transmission for resumption\n"); - goto deferred; - } -#endif - - switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) { - case -EPIPE: - netif_stop_queue (net); - usbnet_defer_kevent (dev, EVENT_TX_HALT); - usb_autopm_put_interface_async(dev->intf); - break; - default: - usb_autopm_put_interface_async(dev->intf); - netif_dbg(dev, tx_err, dev->net, - "tx: submit urb err %d\n", retval); - break; - case 0: - net->trans_start = jiffies; - __usbnet_queue_skb(&dev->txq, skb, tx_start); - if (dev->txq.qlen >= TX_QLEN (dev)) - netif_stop_queue (net); - } - spin_unlock_irqrestore (&dev->txq.lock, flags); - - if (retval) { - netif_dbg(dev, tx_err, dev->net, "drop, code %d\n", retval); -drop: - dev->net->stats.tx_dropped++; -not_drop: - if (skb) - dev_kfree_skb_any (skb); - usb_free_urb (urb); - } else - netif_dbg(dev, tx_queued, dev->net, - "> tx, len %d, type 0x%x\n", length, skb->protocol); -#ifdef CONFIG_PM -deferred: -#endif - return NETDEV_TX_OK; -} -EXPORT_SYMBOL_GPL(usbnet_start_xmit); - -/*-------------------------------------------------------------------------*/ - -// tasklet (work deferred from completions, in_irq) or timer - -static void usbnet_bh (unsigned long param) -{ - struct usbnet *dev = (struct usbnet *) param; - struct sk_buff *skb; - struct skb_data *entry; - - while ((skb = skb_dequeue (&dev->done))) { - entry = (struct skb_data *) skb->cb; - switch (entry->state) { - case rx_done: - entry->state = rx_cleanup; - rx_process (dev, skb); - continue; - case tx_done: - case rx_cleanup: - usb_free_urb (entry->urb); - dev_kfree_skb (skb); - continue; - default: - netdev_dbg(dev->net, "bogus skb state %d\n", entry->state); - } - } - - // waiting for all pending urbs to complete? - if (dev->wait) { - if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { - wake_up (dev->wait); - } - - // or are we maybe short a few urbs? - } else if (netif_running (dev->net) && - netif_device_present (dev->net) && - !timer_pending (&dev->delay) && - !test_bit (EVENT_RX_HALT, &dev->flags)) { - int temp = dev->rxq.qlen; - int qlen = RX_QLEN (dev); - - if (temp < qlen) { - struct urb *urb; - int i; - - // don't refill the queue all at once - for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) { - urb = usb_alloc_urb (0, GFP_ATOMIC); - if (urb != NULL) { - if (rx_submit (dev, urb, GFP_ATOMIC) == - -ENOLINK) - return; - } - } - if (temp != dev->rxq.qlen) - netif_dbg(dev, link, dev->net, - "rxqlen %d --> %d\n", - temp, dev->rxq.qlen); - if (dev->rxq.qlen < qlen) - tasklet_schedule (&dev->bh); - } - if (dev->txq.qlen < TX_QLEN (dev)) - netif_wake_queue (dev->net); - } -} - - -/*------------------------------------------------------------------------- - * - * USB Device Driver support - * - *-------------------------------------------------------------------------*/ - -// precondition: never called in_interrupt - -void usbnet_disconnect (struct usb_interface *intf) -{ - struct usbnet *dev; - struct usb_device *xdev; - struct net_device *net; - - dev = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); - if (!dev) - return; - - xdev = interface_to_usbdev (intf); - - netif_info(dev, probe, dev->net, "unregister '%s' usb-%s-%s, %s\n", - intf->dev.driver->name, - xdev->bus->bus_name, xdev->devpath, - dev->driver_info->description); - - net = dev->net; - unregister_netdev (net); - - cancel_work_sync(&dev->kevent); - - if (dev->driver_info->unbind) - dev->driver_info->unbind (dev, intf); - - usb_kill_urb(dev->interrupt); - usb_free_urb(dev->interrupt); - - free_netdev(net); - usb_put_dev (xdev); -} -EXPORT_SYMBOL_GPL(usbnet_disconnect); - -static const struct net_device_ops usbnet_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 = usbnet_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -/*-------------------------------------------------------------------------*/ - -// precondition: never called in_interrupt - -static struct device_type wlan_type = { - .name = "wlan", -}; - -static struct device_type wwan_type = { - .name = "wwan", -}; - -int -usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) -{ - struct usbnet *dev; - struct net_device *net; - struct usb_host_interface *interface; - struct driver_info *info; - struct usb_device *xdev; - int status; - const char *name; - struct usb_driver *driver = to_usb_driver(udev->dev.driver); - - /* usbnet already took usb runtime pm, so have to enable the feature - * for usb interface, otherwise usb_autopm_get_interface may return - * failure if USB_SUSPEND(RUNTIME_PM) is enabled. - */ - if (!driver->supports_autosuspend) { - driver->supports_autosuspend = 1; - pm_runtime_enable(&udev->dev); - } - - name = udev->dev.driver->name; - info = (struct driver_info *) prod->driver_info; - if (!info) { - dev_dbg (&udev->dev, "blacklisted by %s\n", name); - return -ENODEV; - } - xdev = interface_to_usbdev (udev); - interface = udev->cur_altsetting; - - usb_get_dev (xdev); - - status = -ENOMEM; - -#if 1 - //huawei mu609,skip it's cdc ether device - if(xdev->descriptor.idVendor==0x12d1&&xdev->descriptor.idProduct==0x1573){ - printk("skip %x:%x cdc ether\n",xdev->descriptor.idVendor,xdev->descriptor.idProduct); - goto out; - } -#else -{ - char buf[256]; - int varlen = 256; - int disable = 0; - if( wmt_getsyspara("wmt.cdc.disable",buf,&varlen) == 0) - { - sscanf(buf,"%d",&disable); - if(disable>0) - goto out; - - } -} -#endif - // set up our own records - net = alloc_etherdev(sizeof(*dev)); - if (!net) - goto out; - - /* netdev_printk() needs this so do it as early as possible */ - SET_NETDEV_DEV(net, &udev->dev); - - dev = netdev_priv(net); - dev->udev = xdev; - dev->intf = udev; - dev->driver_info = info; - dev->driver_name = name; - dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV - | NETIF_MSG_PROBE | NETIF_MSG_LINK); - skb_queue_head_init (&dev->rxq); - skb_queue_head_init (&dev->txq); - skb_queue_head_init (&dev->done); - skb_queue_head_init(&dev->rxq_pause); - dev->bh.func = usbnet_bh; - dev->bh.data = (unsigned long) dev; - INIT_WORK (&dev->kevent, kevent); - init_usb_anchor(&dev->deferred); - dev->delay.function = usbnet_bh; - dev->delay.data = (unsigned long) dev; - init_timer (&dev->delay); - mutex_init (&dev->phy_mutex); - - dev->net = net; - strcpy (net->name, "usb%d"); - memcpy (net->dev_addr, node_id, sizeof node_id); - - /* rx and tx sides can use different message sizes; - * 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; - net->ethtool_ops = &usbnet_ethtool_ops; - - // allow device-specific bind/init procedures - // NOTE net->name still not usable ... - if (info->bind) { - status = info->bind (dev, udev); - if (status < 0) - goto out1; - - // heuristic: "usb%d" for links we know are two-host, - // 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 && - ((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) - strcpy(net->name, "wlan%d"); - /* WWAN devices should always be named "wwan%d" */ - if ((dev->driver_info->flags & FLAG_WWAN) != 0) - strcpy(net->name, "wwan%d"); - - /* maybe the remote can't receive an Ethernet MTU */ - if (net->mtu > (dev->hard_mtu - net->hard_header_len)) - net->mtu = dev->hard_mtu - net->hard_header_len; - } else if (!info->in || !info->out) - status = usbnet_get_endpoints (dev, udev); - else { - dev->in = usb_rcvbulkpipe (xdev, info->in); - dev->out = usb_sndbulkpipe (xdev, info->out); - if (!(info->flags & FLAG_NO_SETINT)) - status = usb_set_interface (xdev, - interface->desc.bInterfaceNumber, - interface->desc.bAlternateSetting); - else - status = 0; - - } - if (status >= 0 && dev->status) - status = init_status (dev, udev); - if (status < 0) - goto out3; - - if (!dev->rx_urb_size) - dev->rx_urb_size = dev->hard_mtu; - dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1); - - if ((dev->driver_info->flags & FLAG_WLAN) != 0) - SET_NETDEV_DEVTYPE(net, &wlan_type); - if ((dev->driver_info->flags & FLAG_WWAN) != 0) - SET_NETDEV_DEVTYPE(net, &wwan_type); - - status = register_netdev (net); - if (status) - goto out4; - netif_info(dev, probe, dev->net, - "register '%s' at usb-%s-%s, %s, %pM\n", - udev->dev.driver->name, - xdev->bus->bus_name, xdev->devpath, - dev->driver_info->description, - net->dev_addr); - - // ok, it's ready to go. - usb_set_intfdata (udev, dev); - - netif_device_attach (net); - - if (dev->driver_info->flags & FLAG_LINK_INTR) - netif_carrier_off(net); - - return 0; - -out4: - usb_free_urb(dev->interrupt); -out3: - if (info->unbind) - info->unbind (dev, udev); -out1: - free_netdev(net); -out: - usb_put_dev(xdev); - return status; -} -EXPORT_SYMBOL_GPL(usbnet_probe); - -/*-------------------------------------------------------------------------*/ - -/* - * suspend the whole driver as soon as the first interface is suspended - * resume only when the last interface is resumed - */ - -int usbnet_suspend (struct usb_interface *intf, pm_message_t message) -{ - struct usbnet *dev = usb_get_intfdata(intf); - - if (!dev->suspend_count++) { - spin_lock_irq(&dev->txq.lock); - /* don't autosuspend while transmitting */ - if (dev->txq.qlen && PMSG_IS_AUTO(message)) { - spin_unlock_irq(&dev->txq.lock); - return -EBUSY; - } else { - set_bit(EVENT_DEV_ASLEEP, &dev->flags); - spin_unlock_irq(&dev->txq.lock); - } - /* - * accelerate emptying of the rx and queues, to avoid - * having everything error out. - */ - netif_device_detach (dev->net); - usbnet_terminate_urbs(dev); - usb_kill_urb(dev->interrupt); - - /* - * reattach so runtime management can use and - * wake the device - */ - netif_device_attach (dev->net); - } - return 0; -} -EXPORT_SYMBOL_GPL(usbnet_suspend); - -int usbnet_resume (struct usb_interface *intf) -{ - struct usbnet *dev = usb_get_intfdata(intf); - struct sk_buff *skb; - struct urb *res; - int retval; - - if (!--dev->suspend_count) { - /* resume interrupt URBs */ - if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags)) - usb_submit_urb(dev->interrupt, GFP_NOIO); - - spin_lock_irq(&dev->txq.lock); - while ((res = usb_get_from_anchor(&dev->deferred))) { - - skb = (struct sk_buff *)res->context; - retval = usb_submit_urb(res, GFP_ATOMIC); - if (retval < 0) { - dev_kfree_skb_any(skb); - usb_free_urb(res); - usb_autopm_put_interface_async(dev->intf); - } else { - dev->net->trans_start = jiffies; - __skb_queue_tail(&dev->txq, skb); - } - } - - smp_mb(); - clear_bit(EVENT_DEV_ASLEEP, &dev->flags); - spin_unlock_irq(&dev->txq.lock); - - if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { - if (!(dev->txq.qlen >= TX_QLEN(dev))) - netif_tx_wake_all_queues(dev->net); - tasklet_schedule (&dev->bh); - } - } - return 0; -} -EXPORT_SYMBOL_GPL(usbnet_resume); - - -/*-------------------------------------------------------------------------*/ - -static int __init usbnet_init(void) -{ - /* Compiler should optimize this out. */ - BUILD_BUG_ON( - FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data)); - - random_ether_addr(node_id); - return 0; -} -module_init(usbnet_init); - -static void __exit usbnet_exit(void) -{ -} -module_exit(usbnet_exit); - -MODULE_AUTHOR("David Brownell"); -MODULE_DESCRIPTION("USB network driver framework"); -MODULE_LICENSE("GPL"); |