ChangeSet 1.1932, 2004/04/22 13:43:39-07:00, david-b@pacbell.net

[PATCH] USB: rndis gadget driver updates

Various build fixes:  64bit (Andrew Morton), static linking,
broken on big-endian, etc.

Tighten up the integration with the main "ether" driver, so
state transitions and host ethernet addresses are shared too.
Add missing spinlock calls around RNDIS command outcall,
fix GET_INTERFACE issue, host mustn't clobber netdev flags.

Minor code cleanups.


 drivers/usb/gadget/ether.c |   49 +++++++++-------
 drivers/usb/gadget/rndis.c |  135 +++++++++++++++++++++++++++++----------------
 drivers/usb/gadget/rndis.h |    7 +-
 3 files changed, 120 insertions(+), 71 deletions(-)


diff -Nru a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
--- a/drivers/usb/gadget/ether.c	Thu Apr 22 14:41:20 2004
+++ b/drivers/usb/gadget/ether.c	Thu Apr 22 14:41:20 2004
@@ -120,6 +120,7 @@
 	unsigned long		todo;
 #define	WORK_RX_MEMORY		0
 	int			rndis_config;
+	u8			host_mac [ETH_ALEN];
 };
 
 /* This version autoconfigures as much as possible at run-time.
@@ -159,9 +160,8 @@
 
 /* For hardware that can talk RNDIS and either of the above protocols,
  * use this ID ... the windows INF files will know it.  Unless it's
- * used with CDC Ethernet, Linux hosts will need updates to choose the
- * non-MSFT configuration, either in the kernel (2.4) or else from a
- * hotplug script (2.6).
+ * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
+ * the non-RNDIS configuration.
  */
 #define RNDIS_VENDOR_NUM	0x0525	/* NetChip */
 #define RNDIS_PRODUCT_NUM	0xa4a2	/* Ethernet/RNDIS Gadget */
@@ -1334,8 +1334,10 @@
 	struct eth_dev          *dev = ep->driver_data;
 	
 	/* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */
+	spin_lock(&dev->lock);
 	if (rndis_msg_parser (dev->rndis_config, (u8 *) req->buf))
 		ERROR(dev, "%s: rndis parse error\n", __FUNCTION__ );
+	spin_unlock(&dev->lock);
 }
 
 #endif	/* RNDIS */
@@ -1486,14 +1488,14 @@
 				|| !dev->config
 				|| ctrl->wIndex > 1)
 			break;
-		if (!dev->cdc && ctrl->wIndex != 0)
+		if (!(dev->cdc || dev->rndis) && ctrl->wIndex != 0)
 			break;
 
-		/* if carrier is on, data interface is active. */
-		*(u8 *)req->buf =
-			((ctrl->wIndex == 1) && netif_carrier_ok (dev->net))
-				? 1
-				: 0,
+		/* for CDC, iff carrier is on, data interface is active. */
+		if (dev->rndis || ctrl->wIndex != 1)
+			*(u8 *)req->buf = 0;
+		else
+			*(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0;
 		value = min (ctrl->wLength, (u16) 1);
 		break;
 
@@ -1552,6 +1554,7 @@
 				memcpy (req->buf, buf, value);
 				req->complete = rndis_response_complete;
 			}
+			/* else stalls ... spec says to avoid that */
 		}
 		break;
 #endif	/* RNDIS */
@@ -1590,6 +1593,8 @@
 	eth_reset_config (dev);
 	spin_unlock_irqrestore (&dev->lock, flags);
 
+	/* FIXME RNDIS should enter RNDIS_UNINITIALIZED */
+
 	/* next we may get setup() calls to enumerate new connections;
 	 * or an unbind() during shutdown (including removing module).
 	 */
@@ -2376,19 +2381,19 @@
 	 */
 	random_ether_addr(net->dev_addr);
 
-#ifdef	DEV_CONFIG_CDC
 	/* ... another address for the host, on the other end of the
 	 * link, gets exported through CDC (see CDC spec table 41)
+	 * and RNDIS.
 	 */
-	if (cdc) {
-		u8		node_id [ETH_ALEN];
-
-		random_ether_addr(node_id);
+	if (cdc || rndis) {
+		random_ether_addr(dev->host_mac);
+#ifdef	DEV_CONFIG_CDC
 		snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
-			node_id [0], node_id [1], node_id [2],
-			node_id [3], node_id [4], node_id [5]);
-	}
+			dev->host_mac [0], dev->host_mac [1],
+			dev->host_mac [2], dev->host_mac [3],
+			dev->host_mac [4], dev->host_mac [5]);
 #endif
+	}
 
 	if (rndis) {
 		status = rndis_init();
@@ -2448,10 +2453,11 @@
 		net->dev_addr [2], net->dev_addr [3],
 		net->dev_addr [4], net->dev_addr [5]);
 
-#ifdef	DEV_CONFIG_CDC
-	if (cdc)
-		INFO (dev, "CDC host enet %s\n", ethaddr);
-#endif
+	if (cdc || rndis)
+		INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+			dev->host_mac [0], dev->host_mac [1],
+			dev->host_mac [2], dev->host_mac [3],
+			dev->host_mac [4], dev->host_mac [5]);
 
 #ifdef	CONFIG_USB_ETH_RNDIS
 	if (rndis) {
@@ -2468,6 +2474,7 @@
 		}
 		
 		/* these set up a lot of the OIDs that RNDIS needs */
+		rndis_set_host_mac (dev->rndis_config, dev->host_mac);
 		if (rndis_set_param_dev (dev->rndis_config, dev->net,
 					 &dev->stats))
 			goto fail0;
diff -Nru a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
--- a/drivers/usb/gadget/rndis.c	Thu Apr 22 14:41:20 2004
+++ b/drivers/usb/gadget/rndis.c	Thu Apr 22 14:41:20 2004
@@ -37,6 +37,16 @@
 #include "rndis.h"
 
 
+/* The driver for your USB chip needs to support ep0 OUT to work with
+ * RNDIS, plus the same three descriptors as CDC Ethernet.
+ *
+ * Windows hosts need an INF file like Documentation/usb/linux.inf
+ */
+
+#ifndef	__LITTLE_ENDIAN
+#warning this code is missing all cpu_to_leXX() calls ...
+#endif
+
 #if 0
 #define DEBUG if (rndis_debug) printk 
 static int rndis_debug = 0;
@@ -89,8 +99,12 @@
 
 static void currentFilter2devFlags (u32 currentFilter, struct net_device *dev)
 {
+	/* FIXME the filter is supposed to control what gets
+	 * forwarded from gadget to host; but dev->flags controls
+	 * reporting from host to gadget ...
+	 */
+#if 0
 	if (!dev) return;
-	
 	if (currentFilter & NDIS_PACKET_TYPE_MULTICAST)
 	    dev->flags |= IFF_MULTICAST;
 	if (currentFilter & NDIS_PACKET_TYPE_BROADCAST)
@@ -99,8 +113,13 @@
 	    dev->flags |= IFF_ALLMULTI;
 	if (currentFilter & NDIS_PACKET_TYPE_PROMISCUOUS)
 	    dev->flags |= IFF_PROMISC;
+#endif
 }
 
+/* FIXME OMITTED OIDs, that RNDIS-on-USB "must" support, include
+ *  - power management (OID_PNP_CAPABILITIES, ...)
+ *  - network wakeup (OID_PNP_ENABLE_WAKE_UP, ...)
+ */
 
 /* NDIS Functions */
 static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
@@ -114,8 +133,6 @@
 
 	if (!resp) return -ENOMEM;
 	
-	if (!resp) return -ENOMEM;
-	
 	switch (OID) {
 	/* mandatory */
 	case OID_GEN_SUPPORTED_LIST:
@@ -178,7 +195,8 @@
 	case OID_GEN_LINK_SPEED:
 		DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
 		length = 4;
-		if (rndis_per_dev_params [configNr].media_state)
+		if (rndis_per_dev_params [configNr].media_state
+			== NDIS_MEDIA_STATE_DISCONNECTED)
 		    *((u32 *) resp + 6) = 0;
 		else
 		    *((u32 *) resp + 6) = rndis_per_dev_params [configNr].speed;
@@ -611,15 +629,10 @@
 	case OID_802_3_PERMANENT_ADDRESS:
 		DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].dev) {
-			length = 6;
+			length = ETH_ALEN;
 			memcpy ((u8 *) resp + 24,
-				rndis_per_dev_params [configNr].dev->dev_addr,
+				rndis_per_dev_params [configNr].host_mac,
 				length);
-			/* 
-			 * we need a MAC address and hope that 
-			 * (our MAC + 1) is not in use
-			 */
-			*((u8 *) resp + 29) += 1;
 			retval = 0;
 		} else {
 			*((u32 *) resp + 6) = 0;
@@ -631,15 +644,10 @@
 	case OID_802_3_CURRENT_ADDRESS:
 		DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].dev) {
-			length = 6;
+			length = ETH_ALEN;
 			memcpy ((u8 *) resp + 24,
-				rndis_per_dev_params [configNr].dev->dev_addr,
+				rndis_per_dev_params [configNr].host_mac,
 				length);
-			/* 
-			 * we need a MAC address and hope that 
-			 * (our MAC + 1) is not in use
-			 */
-			*((u8 *) resp + 29) += 1;
 			retval = 0;
 		}
 		break;
@@ -746,22 +754,38 @@
 	rndis_set_cmplt_type		*resp;
 	int 				i, retval = -ENOTSUPP;
 	struct rndis_config_parameter	*param;
-	
-	if (!r) return -ENOMEM;
+	struct rndis_params		*params;
+	u8 *cp;
+
+	if (!r)
+		return -ENOMEM;
 	resp = (rndis_set_cmplt_type *) r->buf;
-	
-	if (!resp) return -ENOMEM;
-	
+	if (!resp)
+		return -ENOMEM;
+
+	cp = (u8 *)resp;
+
 	switch (OID) {
 	case OID_GEN_CURRENT_PACKET_FILTER:
 		DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
-		currentFilter2devFlags ((u32) ((u8 *) resp + 28), 
-					rndis_per_dev_params [configNr].dev);
+		params = &rndis_per_dev_params [configNr];
+		currentFilter2devFlags(cp[28], params->dev);
 		retval = 0;
-		if ((u32) ((u8 *) resp + 28))
-		    rndis_per_dev_params [configNr].state = RNDIS_INITIALIZED;
-		else
-		    rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
+
+		/* this call has a significant side effect:  it's
+		 * what makes the packet flow start and stop, like
+		 * activating the CDC Ethernet altsetting.
+		 */
+		if (cp[28]) {
+			params->state = RNDIS_DATA_INITIALIZED;
+			netif_carrier_on(params->dev);
+			if (netif_running(params->dev))
+				netif_wake_queue (params->dev);
+		} else {
+			params->state = RNDIS_INITIALIZED;
+			netif_carrier_off (params->dev);
+			netif_stop_queue (params->dev);
+		}
 		break;
 		
 	case OID_802_3_MULTICAST_LIST:
@@ -937,10 +961,9 @@
 {
 	rndis_keepalive_cmplt_type	*resp;
 	rndis_resp_t			*r;
-	
-	/* respond only in RNDIS_INITIALIZED state */
-	if (rndis_per_dev_params [configNr].state != RNDIS_INITIALIZED) 
-	    return 0;
+
+	/* host "should" check only in RNDIS_DATA_INITIALIZED state */
+
 	r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
 	resp = (rndis_keepalive_cmplt_type *) r->buf;
 	if (!resp) return -ENOMEM;
@@ -1004,35 +1027,48 @@
 					  RNDIS_STATUS_MEDIA_DISCONNECT);
 }
 
+void rndis_set_host_mac (int configNr, const u8 *addr)
+{
+	rndis_per_dev_params [configNr].host_mac = addr;
+}
+
 /* 
  * Message Parser 
  */
 int rndis_msg_parser (u8 configNr, u8 *buf)
 {
 	u32 MsgType, MsgLength, *tmp;
+	struct rndis_params		*params;
 	
-	if (!buf) return -ENOMEM;
+	if (!buf)
+		return -ENOMEM;
 	
 	tmp = (u32 *) buf; 
 	MsgType = *tmp;
 	MsgLength = *(tmp + 1);
 	
-	if (configNr >= RNDIS_MAX_CONFIGS) return -ENOTSUPP;
+	if (configNr >= RNDIS_MAX_CONFIGS)
+		return -ENOTSUPP;
+	params = &rndis_per_dev_params [configNr];
 	
+	/* For USB: responses may take up to 10 seconds */
 	switch (MsgType)
 	{
-	case REMOTE_NDIS_INIZIALIZE_MSG:
-		DEBUG(KERN_INFO "%s: REMOTE_NDIS_INIZIALIZE_MSG\n", 
+	case REMOTE_NDIS_INITIALIZE_MSG:
+		DEBUG(KERN_INFO "%s: REMOTE_NDIS_INITIALIZE_MSG\n", 
 			__FUNCTION__ );
-		rndis_per_dev_params [configNr].state = RNDIS_INITIALIZED;
+		params->state = RNDIS_INITIALIZED;
 		return  rndis_init_response (configNr,
 					     (rndis_init_msg_type *) buf);
-		break;
 		
 	case REMOTE_NDIS_HALT_MSG:
 		DEBUG(KERN_INFO "%s: REMOTE_NDIS_HALT_MSG\n",
 			__FUNCTION__ );
-		rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
+		params->state = RNDIS_UNINITIALIZED;
+		if (params->dev) {
+			netif_carrier_off (params->dev);
+			netif_stop_queue (params->dev);
+		}
 		return 0;
 		
 	case REMOTE_NDIS_QUERY_MSG:
@@ -1040,29 +1076,26 @@
 			__FUNCTION__ );
 		return rndis_query_response (configNr, 
 					     (rndis_query_msg_type *) buf);
-		break;
 		
 	case REMOTE_NDIS_SET_MSG:
 		DEBUG(KERN_INFO "%s: REMOTE_NDIS_SET_MSG\n", 
 			__FUNCTION__ );
 		return rndis_set_response (configNr, 
 					   (rndis_set_msg_type *) buf);
-		break;
 		
 	case REMOTE_NDIS_RESET_MSG:
 		DEBUG(KERN_INFO "%s: REMOTE_NDIS_RESET_MSG\n", 
 			__FUNCTION__ );
 		return rndis_reset_response (configNr,
 					     (rndis_reset_msg_type *) buf);
-		break;
 
 	case REMOTE_NDIS_KEEPALIVE_MSG:
+		/* For USB: host does this every 5 seconds */
 		DEBUG(KERN_INFO "%s: REMOTE_NDIS_KEEPALIVE_MSG\n", 
 			__FUNCTION__ );
 		return rndis_keepalive_response (configNr,
 						 (rndis_keepalive_msg_type *) 
 						 buf);
-		break;
 		
 	default: 
 		printk (KERN_ERR "%s: unknown RNDIS Message Type 0x%08X\n", 
@@ -1240,9 +1273,15 @@
 			 "vendor ID : 0x%08X\n"
 			 "vendor    : %s\n", 
 			 param->confignr, (param->used) ? "y" : "n", 
-			 (param->state)
-				? "RNDIS_INITIALIZED"
-				: "RNDIS_UNINITIALIZED",
+			 ({ char *s = "?";
+			 switch (param->state) {
+			 case RNDIS_UNINITIALIZED:
+				s = "RNDIS_UNINITIALIZED"; break;
+			 case RNDIS_INITIALIZED:
+				s = "RNDIS_INITIALIZED"; break;
+			 case RNDIS_DATA_INITIALIZED:
+				s = "RNDIS_DATA_INITIALIZED"; break;
+			}; s; }),
 			 param->medium, 
 			 (param->media_state) ? 0 : param->speed*100, 
 			 (param->media_state) ? "disconnected" : "connected",
@@ -1353,7 +1392,7 @@
 	return 0;
 }
 
-void __exit rndis_exit (void)
+void rndis_exit (void)
 {
 	u8 i;
 	char name [4];
diff -Nru a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
--- a/drivers/usb/gadget/rndis.h	Thu Apr 22 14:41:20 2004
+++ b/drivers/usb/gadget/rndis.h	Thu Apr 22 14:41:20 2004
@@ -38,7 +38,7 @@
  */
 
 /* Message Set for Connectionless (802.3) Devices */
-#define REMOTE_NDIS_INIZIALIZE_MSG	0x00000002U	/* Initialize device */
+#define REMOTE_NDIS_INITIALIZE_MSG	0x00000002U	/* Initialize device */
 #define REMOTE_NDIS_HALT_MSG		0x00000003U
 #define REMOTE_NDIS_QUERY_MSG		0x00000004U
 #define REMOTE_NDIS_SET_MSG		0x00000005U
@@ -280,6 +280,7 @@
 	u32			medium;
 	u32			speed;
 	u32			media_state;
+	const u8		*host_mac;
 	struct net_device 	*dev;
 	struct net_device_stats *stats;
 	u32			vendorID;
@@ -301,11 +302,13 @@
 int  rndis_rm_hdr (u8 *buf, u32 *length);
 u8   *rndis_get_next_response (int configNr, u32 *length);
 void rndis_free_response (int configNr, u8 *buf);
+
 int  rndis_signal_connect (int configNr);
 int  rndis_signal_disconnect (int configNr);
 int  rndis_state (int configNr);
+extern void rndis_set_host_mac (int configNr, const u8 *addr);
 
 int __init rndis_init (void);
-void __exit rndis_exit (void);
+void rndis_exit (void);
 
 #endif  /* _LINUX_RNDIS_H */