ChangeSet 1.1500.8.26, 2004/02/05 16:34:54-08:00, david-b@pacbell.net

[PATCH] USB: usbnet updates (new devices)

[USB] usbnet updates:  new devices, cleanups

New devices:  Aten UC210T, Zaurus SL-6000

Cleanup, factoring out shared CDC glue for Ethernet, Zaurus,
and eventually RNDIS.


 drivers/usb/net/Kconfig  |    5 
 drivers/usb/net/usbnet.c |  273 +++++++++++++++++++++++++++++------------------
 2 files changed, 175 insertions(+), 103 deletions(-)


diff -Nru a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig
--- a/drivers/usb/net/Kconfig	Mon Feb  9 14:37:47 2004
+++ b/drivers/usb/net/Kconfig	Mon Feb  9 14:37:47 2004
@@ -254,13 +254,14 @@
 	  10/100 Ethernet devices.
 
  	  This driver should work with at least the following devices:
+	    * Aten UC210T
 	    * ASIX AX88172
 	    * D-Link DUB-E100
 	    * Hawking UF200
 	    * Linksys USB200M
 	    * Netgear FA120
-	    * Intellinet
-	    * ST Lab USB Ethernet
+	    * Intellinet USB 2.0 Ethernet
+	    * ST Lab USB 2.0 Ethernet
 	    * TrendNet TU2-ET100
 
 	  This driver creates an interface named "ethX", where X depends on
diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c	Mon Feb  9 14:37:47 2004
+++ b/drivers/usb/net/usbnet.c	Mon Feb  9 14:37:47 2004
@@ -222,6 +222,7 @@
 #define FLAG_FRAMING_NC	0x0001		/* guard against device dropouts */ 
 #define FLAG_FRAMING_GL	0x0002		/* genelink batches packets */
 #define FLAG_FRAMING_Z	0x0004		/* zaurus adds a trailer */
+#define FLAG_FRAMING_RN	0x0008		/* RNDIS batches, plus huge header */
 
 #define FLAG_NO_SETINT	0x0010		/* device can't set_interface() */
 #define FLAG_ETHER	0x0020		/* maybe use "eth%d" names */
@@ -300,7 +301,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-static struct ethtool_ops usbnet_ethtool_ops;
 static void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
 static u32 usbnet_get_link (struct net_device *);
 static u32 usbnet_get_msglevel (struct net_device *);
@@ -364,6 +364,25 @@
 	return 0;
 }
 
+static void skb_return (struct usbnet *dev, struct sk_buff *skb)
+{
+	int	status;
+
+	skb->dev = dev->net;
+	skb->protocol = eth_type_trans (skb, dev->net);
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += skb->len;
+
+#ifdef	VERBOSE
+	devdbg (dev, "< rx, len %d, type 0x%x",
+		skb->len + sizeof (struct ethhdr), skb->protocol);
+#endif
+	memset (skb->cb, 0, sizeof (struct skb_data));
+	status = netif_rx (skb);
+	if (status != NET_RX_SUCCESS)
+		devdbg (dev, "netif_rx status %d", status);
+}
+
 
 #ifdef	CONFIG_USB_AN2720
 #define	HAVE_HARDWARE
@@ -818,23 +837,30 @@
 
 
 
-#if	defined (CONFIG_USB_CDCETHER) || defined (CONFIG_USB_ZAURUS)
-
 /*-------------------------------------------------------------------------
  *
- * Communications Device Class, Ethernet Control model
- * 
- * Takes two interfaces.  The DATA interface is inactive till an altsetting
- * is selected.  Configuration data includes class descriptors.
- *
- * Zaurus uses nonstandard framing, and doesn't uniquify its Ethernet
- * addresses, but is otherwise CDC Ether.
- *
- * This should interop with whatever the 2.4 "CDCEther.c" driver
- * (by Brad Hards) talked with.
+ * Communications Device Class declarations.
+ * Used by CDC Ethernet, and some CDC variants
  *
  *-------------------------------------------------------------------------*/
 
+#ifdef	CONFIG_USB_CDCETHER
+#define NEED_GENERIC_CDC
+#endif
+
+#ifdef	CONFIG_USB_ZAURUS
+/* Ethernet variant uses funky framing, broken ethernet addressing */
+#define NEED_GENERIC_CDC
+#endif
+
+#ifdef	CONFIG_USB_RNDIS
+/* ACM variant uses even funkier framing, complex control RPC scheme */
+#define NEED_GENERIC_CDC
+#endif
+
+
+#ifdef	NEED_GENERIC_CDC
+
 /* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
 struct header_desc {
 	u8	bLength;
@@ -876,43 +902,21 @@
 	struct usb_interface	*data;
 };
 
-#include <linux/ctype.h>
-
-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;
-}
-
-static inline int get_ethernet_addr (struct usbnet *dev, struct ether_desc *e)
-{
-	int 		tmp, i;
-	unsigned char	buf [13];
-
-	tmp = usb_string (dev->udev, e->iMACAddress, buf, sizeof buf);
-	if (tmp < 0)
-		return tmp;
-	else if (tmp != 12)
-		return -EINVAL;
-	for (i = tmp = 0; i < 6; i++, tmp += 2)
-		dev->net->dev_addr [i] =
-			 (nibble (buf [tmp]) << 4) + nibble (buf [tmp + 1]);
-	return 0;
-}
-
 static struct usb_driver usbnet_driver;
 
-static int cdc_bind (struct usbnet *dev, struct usb_interface *intf)
+/*
+ * probes control interface, claims data interface, collects the bulk
+ * endpoints, activates data interface (if needed), maybe sets MTU.
+ * all pure cdc, except for certain firmware workarounds.
+ */
+static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf)
 {
 	u8				*buf = intf->altsetting->extra;
 	int				len = intf->altsetting->extralen;
 	struct usb_interface_descriptor	*d;
 	struct cdc_state		*info = (void *) &dev->data;
 	int				status;
+	int				rndis;
 
 	if (sizeof dev->data < sizeof *info)
 		return -EDOM;
@@ -931,14 +935,23 @@
 				"CDC descriptors on config\n");
 	}
 
+	/* this assumes that if there's a non-RNDIS vendor variant
+	 * of cdc-acm, it'll fail RNDIS requests cleanly.
+	 */
+	rndis = (intf->altsetting->desc.bInterfaceProtocol == 0xff);
+
 	memset (info, 0, sizeof *info);
 	info->control = intf;
 	while (len > 3) {
 		if (buf [1] != USB_DT_CS_INTERFACE)
 			goto next_desc;
 
-		/* bDescriptorSubType identifies three "must have" descriptors;
-		 * save them for later.
+		/* use bDescriptorSubType to identify the CDC descriptors.
+		 * We expect devices with CDC header and union descriptors.
+		 * For CDC Ethernet we need the ethernet descriptor.
+		 * For RNDIS, ignore two (pointless) CDC modem descriptors
+		 * in favor of a complicated OID-based RPC scheme doing what
+		 * CDC Ethernet achieves with a simple descriptor.
 		 */
 		switch (buf [2]) {
 		case 0x00:		/* Header, mostly useless */
@@ -1001,8 +1014,6 @@
 					d->bInterfaceClass);
 				goto bad_desc;
 			}
-			if (usb_interface_claimed (info->data))
-				return -EBUSY;
 			break;
 		case 0x0F:		/* Ethernet Networking */
 			if (info->ether) {
@@ -1015,13 +1026,20 @@
 					info->u->bLength);
 				goto bad_desc;
 			}
+			dev->net->mtu = cpu_to_le16p (
+						&info->ether->wMaxSegmentSize)
+					- ETH_HLEN;
+			/* because of Zaurus, we may be ignoring the host
+			 * side link address we were given.
+			 */
 			break;
 		}
 next_desc:
 		len -= buf [0];	/* bLength */
 		buf += buf [0];
 	}
-	if (!info->header || !info ->u || !info->ether) {
+
+	if (!info->header || !info->u || (!rndis && !info->ether)) {
 		dev_dbg (&intf->dev, "missing cdc %s%s%sdescriptor\n",
 			info->header ? "" : "header ",
 			info->u ? "" : "union ",
@@ -1029,18 +1047,6 @@
 		goto bad_desc;
 	}
 
-#ifdef CONFIG_USB_ZAURUS
-	/* Zaurus ethernet addresses aren't unique ... */
-	if ((dev->driver_info->flags & FLAG_FRAMING_Z) != 0)
-		/* ignore */ ;
-	else
-#endif
-	{
-		status = get_ethernet_addr (dev, info->ether);
-		if (status < 0)
-			return status;
-	}
-
 	/* claim data interface and set it up ... with side effects.
 	 * network traffic can't flow until an altsetting is enabled.
 	 */
@@ -1049,16 +1055,11 @@
 		return status;
 	status = get_endpoints (dev, info->data);
 	if (status < 0) {
+		/* ensure immediate exit from usbnet_disconnect */
+		usb_set_intfdata(info->data, NULL);
 		usb_driver_release_interface (&usbnet_driver, info->data);
 		return status;
 	}
-
-	/* FIXME cdc-ether has some multicast code too, though it complains
-	 * in routine cases.  info->ether describes the multicast support.
-	 */
-
-	dev->net->mtu = cpu_to_le16p (&info->ether->wMaxSegmentSize)
-		- ETH_HLEN;
 	return 0;
 
 bad_desc:
@@ -1072,24 +1073,89 @@
 
 	/* disconnect master --> disconnect slave */
 	if (intf == info->control && info->data) {
+		/* ensure immediate exit from usbnet_disconnect */
+		usb_set_intfdata(info->data, NULL);
 		usb_driver_release_interface (&usbnet_driver, info->data);
 		info->data = 0;
 	}
 
 	/* and vice versa (just in case) */
 	else if (intf == info->data && info->control) {
+		/* ensure immediate exit from usbnet_disconnect */
+		usb_set_intfdata(info->control, NULL);
 		usb_driver_release_interface (&usbnet_driver, info->control);
 		info->control = 0;
 	}
-
 }
 
-#endif	/* CONFIG_USB_ZAURUS || CONFIG_USB_CDCETHER */
-
+#endif	/* NEED_GENERIC_CDC */
 
+
 #ifdef	CONFIG_USB_CDCETHER
 #define	HAVE_HARDWARE
 
+/*-------------------------------------------------------------------------
+ *
+ * Communications Device Class, Ethernet Control model
+ * 
+ * Takes two interfaces.  The DATA interface is inactive till an altsetting
+ * is selected.  Configuration data includes class descriptors.
+ *
+ * This should interop with whatever the 2.4 "CDCEther.c" driver
+ * (by Brad Hards) talked with.
+ *
+ *-------------------------------------------------------------------------*/
+
+#include <linux/ctype.h>
+
+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;
+}
+
+static inline int
+get_ethernet_addr (struct usbnet *dev, struct ether_desc *e)
+{
+	int 		tmp, i;
+	unsigned char	buf [13];
+
+	tmp = usb_string (dev->udev, e->iMACAddress, buf, sizeof buf);
+	if (tmp < 0)
+		return tmp;
+	else if (tmp != 12)
+		return -EINVAL;
+	for (i = tmp = 0; i < 6; i++, tmp += 2)
+		dev->net->dev_addr [i] =
+			 (nibble (buf [tmp]) << 4) + nibble (buf [tmp + 1]);
+	return 0;
+}
+
+static int cdc_bind (struct usbnet *dev, struct usb_interface *intf)
+{
+	int				status;
+	struct cdc_state		*info = (void *) &dev->data;
+
+	status = generic_cdc_bind (dev, intf);
+	if (status < 0)
+		return status;
+
+	status = get_ethernet_addr (dev, info->ether);
+	if (status < 0) {
+		usb_driver_release_interface (&usbnet_driver, info->data);
+		return status;
+	}
+
+	/* FIXME cdc-ether has some multicast code too, though it complains
+	 * in routine cases.  info->ether describes the multicast support.
+	 */
+	return 0;
+}
+
 static const struct driver_info	cdc_info = {
 	.description =	"CDC Ethernet Device",
 	.flags =	FLAG_ETHER,
@@ -1337,7 +1403,6 @@
 	struct gl_header	*header;
 	struct gl_packet	*packet;
 	struct sk_buff		*gl_skb;
-	int			status;
 	u32			size;
 
 	header = (struct gl_header *) skb->data;
@@ -1373,17 +1438,7 @@
 
 			// copy the packet data to the new skb
 			memcpy(skb_put(gl_skb, size), packet->packet_data, size);
-			gl_skb->dev = dev->net;
-
-			// determine the packet's protocol ID
-			gl_skb->protocol = eth_type_trans (gl_skb, dev->net);
-
-			// update the status
-			dev->stats.rx_packets++;
-			dev->stats.rx_bytes += size;
-
-			// notify os of the received packet
-			status = netif_rx (gl_skb);
+			skb_return (dev, skb);
 		}
 
 		// advance to the next packet
@@ -2141,7 +2196,7 @@
 	.description =	"Sharp Zaurus SL-5x00",
 	.flags =	FLAG_FRAMING_Z,
 	.check_connect = always_connected,
-	.bind =		cdc_bind,
+	.bind =		generic_cdc_bind,
 	.unbind =	cdc_unbind,
 	.tx_fixup = 	zaurus_tx_fixup,
 };
@@ -2256,6 +2311,11 @@
 		size = 6 + (sizeof (struct ethhdr) + dev->net->mtu);
 	else
 #endif
+#ifdef CONFIG_USB_RNDIS
+	if (dev->driver_info->flags & FLAG_FRAMING_RN)
+		size = RNDIS_MAX_TRANSFER;
+	else
+#endif
 		size = (sizeof (struct ethhdr) + dev->net->mtu);
 
 	if ((skb = alloc_skb (size, flags)) == 0) {
@@ -2319,23 +2379,9 @@
 		goto error;
 	// else network stack removes extra byte if we forced a short packet
 
-	if (skb->len) {
-		int	status;
-
-		skb->dev = dev->net;
-		skb->protocol = eth_type_trans (skb, dev->net);
-		dev->stats.rx_packets++;
-		dev->stats.rx_bytes += skb->len;
-
-#ifdef	VERBOSE
-		devdbg (dev, "< rx, len %d, type 0x%x",
-			skb->len + sizeof (struct ethhdr), skb->protocol);
-#endif
-		memset (skb->cb, 0, sizeof (struct skb_data));
-		status = netif_rx (skb);
-		if (status != NET_RX_SUCCESS)
-			devdbg (dev, "netif_rx status %d", status);
-	} else {
+	if (skb->len)
+		skb_return (dev, skb);
+	else {
 		devdbg (dev, "drop");
 error:
 		dev->stats.rx_errors++;
@@ -2544,6 +2590,8 @@
 			framing = "GeneSys";
 		else if (dev->driver_info->flags & FLAG_FRAMING_Z)
 			framing = "Zaurus";
+		else if (dev->driver_info->flags & FLAG_FRAMING_RN)
+			framing = "RNDIS";
 		else
 			framing = "simple";
 
@@ -2941,6 +2989,8 @@
 
 /*-------------------------------------------------------------------------*/
 
+static struct ethtool_ops usbnet_ethtool_ops;
+
 // precondition: never called in_interrupt
 
 int
@@ -3127,7 +3177,11 @@
 	// Hawking UF200, TrendNet TU2-ET100
 	USB_DEVICE (0x07b8, 0x420a),
 	.driver_info =  (unsigned long) &hawking_uf200_info,
-}, 
+}, {
+	// ATEN UC210T
+	USB_DEVICE (0x0557, 0x2009),
+	.driver_info =  (unsigned long) &ax8817x_info,
+},
 #endif
 
 #ifdef	CONFIG_USB_EPSON2888
@@ -3167,6 +3221,14 @@
 },
 #endif
 
+#ifdef	CONFIG_USB_RNDIS
+{
+	/* RNDIS is MSFT's un-official variant of CDC ACM */
+	USB_INTERFACE_INFO (USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+	.driver_info = (unsigned long) &rndis_info,
+},
+#endif
+
 #ifdef	CONFIG_USB_ARMLINUX
 /*
  * SA-1100 using standard ARM Linux kernels, or compatible.
@@ -3238,7 +3300,16 @@
 	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
 		 | USB_DEVICE_ID_MATCH_DEVICE,
 	.idVendor               = 0x04DD,
-	.idProduct              = 0x9031,	/* C-750 */
+	.idProduct              = 0x9031,	/* C-750 C-760 */
+	.bInterfaceClass        = 0x02,
+	.bInterfaceSubClass     = 0x0a,
+	.bInterfaceProtocol     = 0x00,
+	.driver_info =  (unsigned long) &zaurus_pxa_info,
+}, {
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+		 | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor               = 0x04DD,
+	.idProduct              = 0x9032,	/* SL-6000 */
 	.bInterfaceClass        = 0x02,
 	.bInterfaceSubClass     = 0x0a,
 	.bInterfaceProtocol     = 0x00,
