--- 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 - * Copyright (C) 2006 Ole Andre Vadla Ravnaas * * 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 #include +#include #include #include #include @@ -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);