All pastes #452966 Raw Edit

Unnamed

public text v1 · immutable
·published 2007-04-22 17:29 UTC
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);