rendered paste body--- old-usbnet.c 2007-04-22 17:51:33.000000000 +0100
+++ usbnet.c 2007-04-22 17:51:41.000000000 +0100
@@ -2,7 +2,6 @@
* USB Network driver infrastructure
* Copyright (C) 2000-2005 by David Brownell
* Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
- * Copyright (C) 2006 Ole Andre Vadla Ravnaas <oleavr@gmail.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
@@ -31,11 +30,11 @@
* issues can usefully be addressed by this framework.
*/
-#define DEBUG // error path messages, extra info
-#define VERBOSE // more; success messages
+// #define DEBUG // error path messages, extra info
+// #define VERBOSE // more; success messages
-#include <linux/config.h>
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/netdevice.h>
@@ -63,8 +62,11 @@
* let the USB host controller be busy for 5msec or more before an irq
* is required, under load. Jumbograms change the equation.
*/
-#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
-#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+#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)
@@ -147,12 +149,6 @@
if (tmp < 0)
return tmp;
}
-
- dev_dbg(&intf->dev, "bulk IN: 0x%x (0x%x), bulk OUT: 0x%x (0x%x)\n",
- in->desc.bEndpointAddress,
- in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK,
- out->desc.bEndpointAddress,
- out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
dev->in = usb_rcvbulkpipe (dev->udev,
in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
@@ -163,10 +159,9 @@
}
EXPORT_SYMBOL_GPL(usbnet_get_endpoints);
-static void intr_complete (struct urb *urb, struct pt_regs *regs);
+static void intr_complete (struct urb *urb);
-int usbnet_init_status (struct usbnet *dev, struct usb_interface *intf,
- gfp_t flags)
+static int init_status (struct usbnet *dev, struct usb_interface *intf)
{
char *buf = NULL;
unsigned pipe = 0;
@@ -185,9 +180,17 @@
period = max ((int) dev->status->desc.bInterval,
(dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
- buf = kmalloc (maxp, flags);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ buf = kmalloc (maxp, GFP_KERNEL);
+#else
+ buf = kmalloc (maxp, SLAB_KERNEL);
+#endif
if (buf) {
- dev->interrupt = usb_alloc_urb (0, flags);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ dev->interrupt = usb_alloc_urb (0, GFP_KERNEL);
+#else
+ dev->interrupt = usb_alloc_urb (0, SLAB_KERNEL);
+#endif
if (!dev->interrupt) {
kfree (buf);
return -ENOMEM;
@@ -201,16 +204,6 @@
}
return 0;
}
-EXPORT_SYMBOL_GPL(usbnet_init_status);
-
-void usbnet_free_status (struct usbnet *dev)
-{
- if (dev->interrupt) {
- usb_kill_urb(dev->interrupt);
- dev->interrupt = NULL;
- }
-}
-EXPORT_SYMBOL_GPL(usbnet_free_status);
/* Passes this packet up the stack, updating its accounting.
* Some link protocols batch packets, so their rx_fixup paths
@@ -235,7 +228,6 @@
}
EXPORT_SYMBOL_GPL(usbnet_skb_return);
-
/*-------------------------------------------------------------------------
*
* Network Device Driver (peer link to "Host Device", from USB host)
@@ -246,13 +238,23 @@
{
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 || ll_mtu > dev->hard_mtu)
+ 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;
}
@@ -301,7 +303,7 @@
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb, struct pt_regs *regs);
+static void rx_complete (struct urb *urb);
static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
{
@@ -369,70 +371,27 @@
/*-------------------------------------------------------------------------*/
-static struct sk_buff *skb_merge(struct sk_buff *skb1,
- struct sk_buff *skb2)
-{
- struct sk_buff *new_skb;
-
- new_skb = skb_copy_expand(skb1, 0, skb2->len, GFP_ATOMIC);
- if (skb2->len) {
- memcpy(skb_put(new_skb, skb2->len),
- skb2->data, skb2->len);
- }
-
- dev_kfree_skb(skb1);
- dev_kfree_skb(skb2);
-
- return new_skb;
-}
-
static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
{
- int retval;
- if (dev->driver_info->rx_fixup) {
- if (dev->pending_rx) {
- skb = skb_merge(dev->pending_rx, skb);
- dev->pending_rx = skb;
- }
-
- retval = dev->driver_info->rx_fixup (dev, skb);
-
- if (retval != -1) {
- dev->pending_rx = NULL;
- }
-
- if (retval == 0)
- goto error;
- else if (retval == -1) {
- /* queue it -- happens when the last packet is partial */
-
- if (dev->pending_rx == NULL) {
- dev->pending_rx = skb;
- } else {
- skb = skb_merge(dev->pending_rx, skb);
- dev->pending_rx = skb;
- }
-
- return;
- }
- }
-
+ 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
- dev_kfree_skb(skb);
-
- return;
+ else {
+ if (netif_msg_rx_err (dev))
+ devdbg (dev, "drop");
error:
- dev->stats.rx_errors++;
- skb_queue_tail (&dev->done, skb);
+ dev->stats.rx_errors++;
+ skb_queue_tail (&dev->done, skb);
+ }
}
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb, struct pt_regs *regs)
+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;
@@ -474,9 +433,9 @@
// 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: // ehci
- case -ETIMEDOUT: // ohci
- case -EILSEQ: // uhci
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
dev->stats.rx_errors++;
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
@@ -516,36 +475,39 @@
devdbg (dev, "no read resubmitted");
}
-static void intr_complete (struct urb *urb, struct pt_regs *regs)
+static void intr_complete (struct urb *urb)
{
struct usbnet *dev = urb->context;
int status = urb->status;
- int retval;
switch (status) {
/* success */
case 0:
- dev->driver_info->status (dev, urb);
+ dev->driver_info->status(dev, urb);
break;
/* software-driven interface shutdown */
case -ENOENT: // urb killed
case -ESHUTDOWN: // hardware gone
- deverr (dev, "intr shutdown, code %d", status);
+ if (netif_msg_ifdown (dev))
+ devdbg (dev, "intr shutdown, code %d", status);
return;
/* NOTE: not throttling like RX/TX, since this endpoint
* already polls infrequently
*/
default:
- deverr (dev, "intr status %d", status);
+ devdbg (dev, "intr status %d", status);
break;
}
- memset (urb->transfer_buffer, 0, urb->transfer_buffer_length);
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (unlikely (retval != 0))
- deverr (dev, "failed to submit interrupt urb: %d", retval);
+ 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_msg_timer (dev))
+ deverr(dev, "intr resubmit --> %d", status);
}
/*-------------------------------------------------------------------------*/
@@ -558,11 +520,6 @@
struct sk_buff *skb, *skbnext;
int count = 0;
- if (dev->pending_rx) {
- dev_kfree_skb(dev->pending_rx);
- dev->pending_rx = NULL;
- }
-
spin_lock_irqsave (&q->lock, flags);
for (skb = q->next; skb != (struct sk_buff *) q; skb = skbnext) {
struct skb_data *entry;
@@ -585,6 +542,17 @@
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);
/*-------------------------------------------------------------------------*/
@@ -594,7 +562,11 @@
{
struct usbnet *dev = netdev_priv(net);
int temp;
- DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup);
+#else
+ DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
+#endif
DECLARE_WAITQUEUE (wait, current);
netif_stop_queue (net);
@@ -621,7 +593,7 @@
dev->wait = NULL;
remove_wait_queue (&unlink_wakeup, &wait);
- usbnet_free_status(dev);
+ usb_kill_urb(dev->interrupt);
/* deferred work (task, timer, softirq) must also stop.
* can't flush_scheduled_work() until we drop rtnl (later),
@@ -664,6 +636,16 @@
goto done;
}
+ /* start any status interrupt transfer */
+ if (dev->interrupt) {
+ retval = usb_submit_urb (dev->interrupt, GFP_KERNEL);
+ if (retval < 0) {
+ if (netif_msg_ifup (dev))
+ deverr (dev, "intr submit %d", retval);
+ goto done;
+ }
+ }
+
netif_start_queue (net);
if (netif_msg_ifup (dev)) {
char *framing;
@@ -683,7 +665,7 @@
devinfo (dev, "open: enable queueing "
"(rx %d, tx %d) mtu %d %s framing",
- RX_QLEN (dev), TX_QLEN (dev), dev->net->mtu,
+ (int)RX_QLEN (dev), (int)TX_QLEN (dev), dev->net->mtu,
framing);
}
@@ -699,20 +681,40 @@
* they'll probably want to use this base set.
*/
-void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
+#if defined(CONFIG_MII) || defined(CONFIG_MII_MODULE)
+#define HAVE_MII
+
+int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd)
{
struct usbnet *dev = netdev_priv(net);
- /* REVISIT don't always return "usbnet" */
- strncpy (info->driver, 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);
+ if (!dev->mii.mdio_read)
+ return -EOPNOTSUPP;
+
+ return mii_ethtool_gset(&dev->mii, cmd);
}
-EXPORT_SYMBOL_GPL(usbnet_get_drvinfo);
+EXPORT_SYMBOL_GPL(usbnet_get_settings);
-static u32 usbnet_get_link (struct net_device *net)
+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);
@@ -720,9 +722,40 @@
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, say we're up (to avoid breaking scripts) */
return 1;
}
+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);
+
+#endif /* HAVE_MII */
+
+void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ /* REVISIT don't always return "usbnet" */
+ strncpy (info->driver, 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)
{
@@ -742,8 +775,13 @@
/* drivers may override default ethtool_ops in their bind() routine */
static struct ethtool_ops usbnet_ethtool_ops = {
- .get_drvinfo = usbnet_get_drvinfo,
+#ifdef HAVE_MII
+ .get_settings = usbnet_get_settings,
+ .set_settings = usbnet_set_settings,
.get_link = usbnet_get_link,
+ .nway_reset = usbnet_nway_reset,
+#endif
+ .get_drvinfo = usbnet_get_drvinfo,
.get_msglevel = usbnet_get_msglevel,
.set_msglevel = usbnet_set_msglevel,
};
@@ -827,7 +865,7 @@
/*-------------------------------------------------------------------------*/
-static void tx_complete (struct urb *urb, struct pt_regs *regs)
+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;
@@ -851,9 +889,9 @@
// like rx, tx gets controller i/o faults during khubd delays
// and so it uses the same throttling mechanism.
- case -EPROTO: // ehci
- case -ETIMEDOUT: // ohci
- case -EILSEQ: // uhci
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay,
jiffies + THROTTLE_JIFFIES);
@@ -1116,15 +1154,19 @@
dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
| NETIF_MSG_PROBE | NETIF_MSG_LINK);
skb_queue_head_init (&dev->rxq);
- dev->pending_rx = NULL;
skb_queue_head_init (&dev->txq);
skb_queue_head_init (&dev->done);
dev->bh.func = usbnet_bh;
dev->bh.data = (unsigned long) dev;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK (&dev->kevent, kevent);
+#else
INIT_WORK (&dev->kevent, kevent, dev);
+#endif
dev->delay.function = usbnet_bh;
dev->delay.data = (unsigned long) dev;
init_timer (&dev->delay);
+ mutex_init (&dev->phy_mutex);
SET_MODULE_OWNER (net);
dev->net = net;
@@ -1155,13 +1197,13 @@
// 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
+ // heuristic: "rndis%d" for links with RNDIS framing,
+ // "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_FRAMING_RN) != 0)
+ strcpy (net->name, "rndis%d");
+ else if ((dev->driver_info->flags & FLAG_ETHER) != 0
&& (net->dev_addr [0] & 0x02) == 0)
strcpy (net->name, "eth%d");
@@ -1181,6 +1223,10 @@
status = 0;
}
+ if (status == 0 && dev->status)
+ status = init_status (dev, udev);
+ if (status < 0)
+ goto out1;
if (!dev->rx_urb_size)
dev->rx_urb_size = dev->hard_mtu;
@@ -1255,7 +1301,7 @@
static int __init usbnet_init(void)
{
/* compiler should optimize this out */
- BUG_ON (sizeof (((struct sk_buff *)0)->cb)
+ BUILD_BUG_ON (sizeof (((struct sk_buff *)0)->cb)
< sizeof (struct skb_data));
random_ether_addr(node_id);