Commit 33505caf authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB usbnet driver update

    - generalizes/cleans keventd support to also handle
        * rx stalls (and usb 2.0 transaction translator unplug)
        * rx memory shortfalls (latent bug Oliver noticed)
        * cleanup on device disconnect (quiesce first)
    - merges Brad's patch to use the IEEE802 "locally assigned" bit
    - fixes a couple minor bugs on error paths (leak, bogus diagnostic)
    - updates some comments
parent 6067b10d
...@@ -33,19 +33,32 @@ ...@@ -33,19 +33,32 @@
* re-enable queues to get higher bandwidth utilization (without needing * re-enable queues to get higher bandwidth utilization (without needing
* to tweak MTU for larger packets). * to tweak MTU for larger packets).
* *
* Add support for more "network cable" chips; interop with their Win32 * - AN2720 ... not widely available, but reportedly works well
* drivers may be a good thing. Test the AnchorChip 2720 support..
* Figure out the initialization protocol used by the Prolific chips,
* for better robustness ... there's some powerup/reset handshake that's
* needed when only one end reboots.
* *
* Use interrupt on PL230x to detect peer connect/disconnect, and call * - Belkin/eTEK ... no known issues
* netif_carrier_{on,off} (?) appropriately. For Net1080, detect peer
* connect/disconnect with async control messages.
* *
* Find some way to report "peer connected" network hotplug events; it'll * - Both GeneSys and PL-230x use interrupt transfers for driver-to-driver
* likely mean updating the networking layer. (This has been discussed * handshaking; it'd be worth implementing those as "carrier detect".
* on the netdev list...) * Prefer generic hooks, not minidriver-specific hacks.
*
* - Linux devices ... the www.handhelds.org SA-1100 support works nicely,
* but the Sharp Zaurus uses an incompatible protocol (extra checksums).
* No reason not to merge the Zaurus protocol here too (got patch? :)
*
* - For Netchip, use keventd to poll via control requests to detect hardware
* level "carrier detect".
*
* - PL-230x ... the initialization protocol doesn't seem to match chip data
* sheets, sometimes it's not needed and sometimes it hangs. Prolific has
* not responded to repeated support/information requests.
*
* Interop with more Win32 drivers may be a good thing.
*
* Seems like reporting "peer connected" (carrier present) events may end
* up going through the netlink event system, not hotplug ... that may be
* awkward in terms of automatic configuration though.
*
* There are reports that bridging gives lower-than-usual throughput.
* *
* Craft smarter hotplug policy scripts ... ones that know how to arrange * Craft smarter hotplug policy scripts ... ones that know how to arrange
* bridging with "brctl", and can handle static and dynamic ("pump") setups. * bridging with "brctl", and can handle static and dynamic ("pump") setups.
...@@ -88,6 +101,9 @@ ...@@ -88,6 +101,9 @@
* Level of diagnostics is more configurable; they use device * Level of diagnostics is more configurable; they use device
* location (usb_device->devpath) instead of address (2.5). * location (usb_device->devpath) instead of address (2.5).
* For tx_fixup, memflags can't be NOIO. * For tx_fixup, memflags can't be NOIO.
* 07-may-2002 Generalize/cleanup keventd support, handling rx stalls (mostly
* for USB 2.0 TTs) and memory shortages (potential) too. (db)
* Use "locally assigned" IEEE802 address space. (Brad Hards)
* *
*-------------------------------------------------------------------------*/ *-------------------------------------------------------------------------*/
...@@ -113,6 +129,7 @@ ...@@ -113,6 +129,7 @@
#include <linux/usb.h> #include <linux/usb.h>
/* minidrivers _could_ be individually configured */
#define CONFIG_USB_AN2720 #define CONFIG_USB_AN2720
#define CONFIG_USB_BELKIN #define CONFIG_USB_BELKIN
#define CONFIG_USB_GENESYS #define CONFIG_USB_GENESYS
...@@ -121,7 +138,7 @@ ...@@ -121,7 +138,7 @@
#define CONFIG_USB_PL2301 #define CONFIG_USB_PL2301
#define DRIVER_VERSION "26-Apr-2002" #define DRIVER_VERSION "07-May-2002"
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -185,7 +202,12 @@ struct usbnet { ...@@ -185,7 +202,12 @@ struct usbnet {
struct sk_buff_head txq; struct sk_buff_head txq;
struct sk_buff_head done; struct sk_buff_head done;
struct tasklet_struct bh; struct tasklet_struct bh;
struct tq_struct ctrl_task;
struct tq_struct kevent;
unsigned long flags;
# define EVENT_TX_HALT 0
# define EVENT_RX_HALT 1
# define EVENT_RX_MEMORY 2
}; };
// device-specific info used by the driver // device-specific info used by the driver
...@@ -1238,6 +1260,21 @@ static void defer_bh (struct usbnet *dev, struct sk_buff *skb) ...@@ -1238,6 +1260,21 @@ static void defer_bh (struct usbnet *dev, struct sk_buff *skb)
spin_unlock_irqrestore (&dev->done.lock, flags); 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_task() fails,
* but tasklet_schedule() doesn't. hope the failure is rare.
*/
static void defer_kevent (struct usbnet *dev, int work)
{
set_bit (work, &dev->flags);
if (!schedule_task (&dev->kevent))
err ("%s: kevent %d may have been dropped",
dev->net.name, work);
else
dbg ("%s: kevent %d scheduled", dev->net.name, work);
}
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void rx_complete (struct urb *urb); static void rx_complete (struct urb *urb);
...@@ -1264,7 +1301,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags) ...@@ -1264,7 +1301,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
if ((skb = alloc_skb (size, flags)) == 0) { if ((skb = alloc_skb (size, flags)) == 0) {
dbg ("no rx skb"); dbg ("no rx skb");
tasklet_schedule (&dev->bh); defer_kevent (dev, EVENT_RX_MEMORY);
usb_free_urb (urb); usb_free_urb (urb);
return; return;
} }
...@@ -1290,11 +1327,20 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags) ...@@ -1290,11 +1327,20 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
spin_lock_irqsave (&dev->rxq.lock, lockflags); spin_lock_irqsave (&dev->rxq.lock, lockflags);
if (netif_running (&dev->net)) { if (netif_running (&dev->net)
if ((retval = usb_submit_urb (urb, GFP_ATOMIC)) != 0) { && !test_bit (EVENT_RX_HALT, &dev->flags)) {
switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){
case -EPIPE:
defer_kevent (dev, EVENT_RX_HALT);
break;
case -ENOMEM:
defer_kevent (dev, EVENT_RX_MEMORY);
break;
default:
dbg ("%s rx submit, %d", dev->net.name, retval); dbg ("%s rx submit, %d", dev->net.name, retval);
tasklet_schedule (&dev->bh); tasklet_schedule (&dev->bh);
} else { break;
case 0:
__skb_queue_tail (&dev->rxq, skb); __skb_queue_tail (&dev->rxq, skb);
} }
} else { } else {
...@@ -1368,12 +1414,20 @@ static void rx_complete (struct urb *urb) ...@@ -1368,12 +1414,20 @@ static void rx_complete (struct urb *urb)
} }
break; 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 ETIMEOUT/EILSEQ
// storm, recovering as needed.
case -EPIPE:
defer_kevent (dev, EVENT_RX_HALT);
// FALLTHROUGH
// software-driven interface shutdown // software-driven interface shutdown
case -ECONNRESET: // usb-ohci, usb-uhci case -ECONNRESET: // according to API spec
case -ECONNABORTED: // uhci ... for usb-uhci, INTR case -ECONNABORTED: // some (now fixed?) UHCI bugs
dbg ("%s shutdown, code %d", dev->net.name, urb_status); dbg ("%s rx shutdown, code %d", dev->net.name, urb_status);
entry->state = rx_cleanup; entry->state = rx_cleanup;
// do urb frees only in the tasklet // do urb frees only in the tasklet (UHCI has oopsed ...)
entry->urb = urb; entry->urb = urb;
urb = 0; urb = 0;
break; break;
...@@ -1384,8 +1438,9 @@ static void rx_complete (struct urb *urb) ...@@ -1384,8 +1438,9 @@ static void rx_complete (struct urb *urb)
// FALLTHROUGH // FALLTHROUGH
default: default:
// on unplug we'll get a burst of ETIMEDOUT/EILSEQ // on unplug we get ETIMEDOUT (ohci) or EILSEQ (uhci)
// till the khubd gets and handles its interrupt. // until khubd sees its interrupt and disconnects us.
// that can easily be hundreds of passes through here.
entry->state = rx_cleanup; entry->state = rx_cleanup;
dev->stats.rx_errors++; dev->stats.rx_errors++;
dbg ("%s rx: status %d", dev->net.name, urb_status); dbg ("%s rx: status %d", dev->net.name, urb_status);
...@@ -1395,10 +1450,12 @@ static void rx_complete (struct urb *urb) ...@@ -1395,10 +1450,12 @@ static void rx_complete (struct urb *urb)
defer_bh (dev, skb); defer_bh (dev, skb);
if (urb) { if (urb) {
if (netif_running (&dev->net)) { if (netif_running (&dev->net)
&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
rx_submit (dev, urb, GFP_ATOMIC); rx_submit (dev, urb, GFP_ATOMIC);
return; return;
} }
usb_free_urb (urb);
} }
#ifdef VERBOSE #ifdef VERBOSE
dbg ("no read resubmitted"); dbg ("no read resubmitted");
...@@ -1428,7 +1485,7 @@ static int unlink_urbs (struct sk_buff_head *q) ...@@ -1428,7 +1485,7 @@ static int unlink_urbs (struct sk_buff_head *q)
// during some PM-driven resume scenarios, // during some PM-driven resume scenarios,
// these (async) unlinks complete immediately // these (async) unlinks complete immediately
retval = usb_unlink_urb (urb); retval = usb_unlink_urb (urb);
if (retval < 0) if (retval != -EINPROGRESS && retval != 0)
dbg ("unlink urb err, %d", retval); dbg ("unlink urb err, %d", retval);
else else
count++; count++;
...@@ -1600,16 +1657,62 @@ static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd) ...@@ -1600,16 +1657,62 @@ static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* usb_clear_halt cannot be called in interrupt context */ /* work that cannot be done in interrupt context uses keventd.
*
* NOTE: "uhci" and "usb-uhci" may have trouble with this since they don't
* queue control transfers to individual devices, and other threads could
* trigger control requests concurrently. hope that's rare.
*/
static void static void
tx_clear_halt (void *data) kevent (void *data)
{ {
struct usbnet *dev = data; struct usbnet *dev = data;
int status;
usb_clear_halt (dev->udev, /* usb_clear_halt() needs a thread context */
if (test_bit (EVENT_TX_HALT, &dev->flags)) {
unlink_urbs (&dev->txq);
status = usb_clear_halt (dev->udev,
usb_sndbulkpipe (dev->udev, dev->driver_info->out)); usb_sndbulkpipe (dev->udev, dev->driver_info->out));
if (status < 0)
err ("%s: can't clear tx halt, status %d",
dev->net.name, status);
else {
clear_bit (EVENT_TX_HALT, &dev->flags);
netif_wake_queue (&dev->net); netif_wake_queue (&dev->net);
}
}
if (test_bit (EVENT_RX_HALT, &dev->flags)) {
unlink_urbs (&dev->rxq);
status = usb_clear_halt (dev->udev,
usb_rcvbulkpipe (dev->udev, dev->driver_info->in));
if (status < 0)
err ("%s: can't clear rx halt, status %d",
dev->net.name, 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 = 0;
if (netif_running (&dev->net))
urb = usb_alloc_urb (0, GFP_KERNEL);
else
clear_bit (EVENT_RX_MEMORY, &dev->flags);
if (urb != 0) {
clear_bit (EVENT_RX_MEMORY, &dev->flags);
rx_submit (dev, urb, GFP_KERNEL);
tasklet_schedule (&dev->bh);
}
}
if (dev->flags)
dbg ("%s: kevent done, flags = 0x%lx",
dev->net.name, dev->flags);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -1620,15 +1723,8 @@ static void tx_complete (struct urb *urb) ...@@ -1620,15 +1723,8 @@ static void tx_complete (struct urb *urb)
struct skb_data *entry = (struct skb_data *) skb->cb; struct skb_data *entry = (struct skb_data *) skb->cb;
struct usbnet *dev = entry->dev; struct usbnet *dev = entry->dev;
if (urb->status == -EPIPE) { if (urb->status == -EPIPE)
if (dev->ctrl_task.sync == 0) { defer_kevent (dev, EVENT_TX_HALT);
dev->ctrl_task.routine = tx_clear_halt;
dev->ctrl_task.data = dev;
schedule_task (&dev->ctrl_task);
} else {
dbg ("Cannot clear TX stall");
}
}
urb->dev = 0; urb->dev = 0;
entry->state = tx_done; entry->state = tx_done;
defer_bh (dev, skb); defer_bh (dev, skb);
...@@ -1725,10 +1821,15 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) ...@@ -1725,10 +1821,15 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
#endif /* CONFIG_USB_NET1080 */ #endif /* CONFIG_USB_NET1080 */
netif_stop_queue (net); netif_stop_queue (net);
if ((retval = usb_submit_urb (urb, GFP_ATOMIC)) != 0) { switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
case -EPIPE:
defer_kevent (dev, EVENT_TX_HALT);
break;
default:
netif_start_queue (net); netif_start_queue (net);
dbg ("%s tx: submit urb err %d", net->name, retval); dbg ("%s tx: submit urb err %d", net->name, retval);
} else { break;
case 0:
net->trans_start = jiffies; net->trans_start = jiffies;
__skb_queue_tail (&dev->txq, skb); __skb_queue_tail (&dev->txq, skb);
if (dev->txq.qlen < TX_QLEN) if (dev->txq.qlen < TX_QLEN)
...@@ -1799,7 +1900,8 @@ static void usbnet_bh (unsigned long param) ...@@ -1799,7 +1900,8 @@ static void usbnet_bh (unsigned long param)
} }
// or are we maybe short a few urbs? // or are we maybe short a few urbs?
} else if (netif_running (&dev->net)) { } else if (netif_running (&dev->net)
&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
int temp = dev->rxq.qlen; int temp = dev->rxq.qlen;
if (temp < RX_QLEN) { if (temp < RX_QLEN) {
...@@ -1845,6 +1947,9 @@ static void usbnet_disconnect (struct usb_device *udev, void *ptr) ...@@ -1845,6 +1947,9 @@ static void usbnet_disconnect (struct usb_device *udev, void *ptr)
list_del (&dev->dev_list); list_del (&dev->dev_list);
mutex_unlock (&usbnet_mutex); mutex_unlock (&usbnet_mutex);
// assuming we used keventd, it must quiesce too
flush_scheduled_tasks ();
kfree (dev); kfree (dev);
usb_dec_dev_use (udev); usb_dec_dev_use (udev);
} }
...@@ -1902,6 +2007,7 @@ usbnet_probe (struct usb_device *udev, unsigned ifnum, ...@@ -1902,6 +2007,7 @@ usbnet_probe (struct usb_device *udev, unsigned ifnum,
skb_queue_head_init (&dev->done); skb_queue_head_init (&dev->done);
dev->bh.func = usbnet_bh; dev->bh.func = usbnet_bh;
dev->bh.data = (unsigned long) dev; dev->bh.data = (unsigned long) dev;
INIT_TQUEUE (&dev->kevent, kevent, dev);
// set up network interface records // set up network interface records
net = &dev->net; net = &dev->net;
...@@ -2038,6 +2144,7 @@ static int __init usbnet_init (void) ...@@ -2038,6 +2144,7 @@ static int __init usbnet_init (void)
get_random_bytes (node_id, sizeof node_id); get_random_bytes (node_id, sizeof node_id);
node_id [0] &= 0xfe; // clear multicast bit node_id [0] &= 0xfe; // clear multicast bit
node_id [0] |= 0x02; // set local assignment bit (IEEE802)
if (usb_register (&usbnet_driver) < 0) if (usb_register (&usbnet_driver) < 0)
return -1; return -1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment