mirror of
https://github.com/torvalds/linux.git
synced 2024-12-23 11:21:33 +00:00
5a0e3ad6af
percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
1511 lines
40 KiB
C
1511 lines
40 KiB
C
/*
|
||
* 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>
|
||
|
||
#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");
|
||
|
||
/*-------------------------------------------------------------------------*/
|
||
|
||
/* 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 altsetttings.
|
||
*/
|
||
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);
|
||
|
||
static u8 nibble(unsigned char c)
|
||
{
|
||
if (likely(isdigit(c)))
|
||
return c - '0';
|
||
c = toupper(c);
|
||
if (likely(isxdigit(c)))
|
||
return 10 + c - 'A';
|
||
return 0;
|
||
}
|
||
|
||
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] =
|
||
(nibble(buf [tmp]) << 4) + nibble(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_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));
|
||
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);
|
||
|
||
/*-------------------------------------------------------------------------*/
|
||
|
||
/* 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)
|
||
{
|
||
unsigned long flags;
|
||
|
||
spin_lock_irqsave(&list->lock, flags);
|
||
__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);
|
||
}
|
||
|
||
/* 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 void 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;
|
||
|
||
if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
|
||
netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
|
||
usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
|
||
usb_free_urb (urb);
|
||
return;
|
||
}
|
||
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,
|
||
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;
|
||
default:
|
||
netif_dbg(dev, rx_err, dev->net,
|
||
"rx submit, %d\n", retval);
|
||
tasklet_schedule (&dev->bh);
|
||
break;
|
||
case 0:
|
||
__skb_queue_tail (&dev->rxq, skb);
|
||
}
|
||
} 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);
|
||
}
|
||
}
|
||
|
||
|
||
/*-------------------------------------------------------------------------*/
|
||
|
||
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;
|
||
// else network stack removes extra byte if we forced a short packet
|
||
|
||
if (skb->len)
|
||
usbnet_skb_return (dev, skb);
|
||
else {
|
||
netif_dbg(dev, rx_err, dev->net, "drop\n");
|
||
error:
|
||
dev->net->stats.rx_errors++;
|
||
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;
|
||
|
||
skb_put (skb, urb->actual_length);
|
||
entry->state = rx_done;
|
||
entry->urb = NULL;
|
||
|
||
switch (urb_status) {
|
||
/* success */
|
||
case 0:
|
||
if (skb->len < dev->net->hard_header_len) {
|
||
entry->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:
|
||
entry->state = rx_cleanup;
|
||
entry->urb = urb;
|
||
urb = NULL;
|
||
break;
|
||
|
||
/* data overrun ... flush fifo? */
|
||
case -EOVERFLOW:
|
||
dev->net->stats.rx_over_errors++;
|
||
// FALLTHROUGH
|
||
|
||
default:
|
||
entry->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);
|
||
|
||
if (urb) {
|
||
if (netif_running (dev->net) &&
|
||
!test_bit (EVENT_RX_HALT, &dev->flags)) {
|
||
rx_submit (dev, urb, GFP_ATOMIC);
|
||
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, *skbnext;
|
||
int count = 0;
|
||
|
||
spin_lock_irqsave (&q->lock, flags);
|
||
skb_queue_walk_safe(q, skb, skbnext) {
|
||
struct skb_data *entry;
|
||
struct urb *urb;
|
||
int retval;
|
||
|
||
entry = (struct skb_data *) skb->cb;
|
||
urb = entry->urb;
|
||
|
||
// 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++;
|
||
}
|
||
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(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;
|
||
|
||
netif_stop_queue (net);
|
||
|
||
netif_info(dev, ifdown, dev->net,
|
||
"stop stats: rx/tx %ld/%ld, errs %ld/%ld\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;
|
||
}
|
||
}
|
||
|
||
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;
|
||
|
||
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)
|
||
goto fail_lowmem;
|
||
rx_submit (dev, urb, GFP_KERNEL);
|
||
usb_autopm_put_interface(dev->intf);
|
||
fail_lowmem:
|
||
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) {
|
||
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);
|
||
urb->dev = NULL;
|
||
entry->state = tx_done;
|
||
defer_bh(dev, skb, &dev->txq);
|
||
}
|
||
|
||
/*-------------------------------------------------------------------------*/
|
||
|
||
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;
|
||
|
||
// 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) {
|
||
netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n");
|
||
goto 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->state = tx_start;
|
||
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.
|
||
*/
|
||
if (!(info->flags & FLAG_SEND_ZLP) && (length % dev->maxpacket) == 0) {
|
||
urb->transfer_buffer_length++;
|
||
if (skb_tailroom(skb)) {
|
||
skb->data[skb->len] = 0;
|
||
__skb_put(skb, 1);
|
||
}
|
||
}
|
||
|
||
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;
|
||
__skb_queue_tail (&dev->txq, skb);
|
||
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++;
|
||
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)
|
||
rx_submit (dev, urb, GFP_ATOMIC);
|
||
}
|
||
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);
|
||
|
||
/* we don't hold rtnl here ... */
|
||
flush_scheduled_work ();
|
||
|
||
if (dev->driver_info->unbind)
|
||
dev->driver_info->unbind (dev, intf);
|
||
|
||
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;
|
||
|
||
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;
|
||
|
||
// set up our own records
|
||
net = alloc_etherdev(sizeof(*dev));
|
||
if (!net) {
|
||
dbg ("can't kmalloc dev");
|
||
goto out;
|
||
}
|
||
|
||
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 &&
|
||
(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);
|
||
|
||
SET_NETDEV_DEV(net, &udev->dev);
|
||
|
||
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 out3;
|
||
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;
|
||
|
||
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 && (message.event & PM_EVENT_AUTO)) {
|
||
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) {
|
||
spin_lock_irq(&dev->txq.lock);
|
||
while ((res = usb_get_from_anchor(&dev->deferred))) {
|
||
|
||
printk(KERN_INFO"%s has delayed data\n", __func__);
|
||
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 (!(dev->txq.qlen >= TX_QLEN(dev)))
|
||
netif_start_queue(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 (sizeof (((struct sk_buff *)0)->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");
|