ChangeSet 1.1732, 2004/05/17 15:32:03-07:00, david-b@pacbell.net

[PATCH] USB: RNDIS (and CDC) filter flag handling

This should fix the problem David Meggy found, where RNDIS was setting
the OID_GEN_CURRENT_PACKET_FILTER state incorrectly.  It's the same
issue Andrew Morton noticed a while back, for that matter, but with
more than just a "now compiles on 64 bit" fix.

Basically the code needs to interpret 32 bits provided in the request
from the (Windows) host, rather than 8 bits of other memory that's got
some irrelevant value.

The fix is just to save the 32 bits.  I did the same thing with the
CDC Ethernet filter, which should eventually be used the same way:  to
limit what packets get sent to the host.  Also defined a couple more
of the CDC requests.


 drivers/usb/gadget/ether.c |   29 +++++++++++++++++++---
 drivers/usb/gadget/rndis.c |   57 ++++++++++-----------------------------------
 drivers/usb/gadget/rndis.h |    1 
 3 files changed, 39 insertions(+), 48 deletions(-)


diff -Nru a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
--- a/drivers/usb/gadget/ether.c	Mon May 17 16:37:33 2004
+++ b/drivers/usb/gadget/ether.c	Mon May 17 16:37:33 2004
@@ -117,6 +117,7 @@
 	unsigned		zlp:1;
 	unsigned		cdc:1;
 	unsigned		rndis:1;
+	u16			cdc_filter;
 	unsigned long		todo;
 #define	WORK_RX_MEMORY		0
 	int			rndis_config;
@@ -1139,6 +1140,9 @@
 	}
 	eth_reset_config (dev);
 
+	/* default:  pass all packets, no multicast filtering */
+	dev->cdc_filter = 0x000f;
+
 	switch (number) {
 	case DEV_CONFIG_VALUE:
 		dev->rndis = 0;
@@ -1311,9 +1315,20 @@
  * section 3.6.2.1 table 4 has ACM requests; RNDIS requires the
  * encapsulated command mechanism.
  */
-#define CDC_SEND_ENCAPSULATED_COMMAND	0x00	/* optional */
-#define CDC_GET_ENCAPSULATED_RESPONSE	0x01	/* optional */
-#define CDC_SET_ETHERNET_PACKET_FILTER	0x43	/* required */
+#define CDC_SEND_ENCAPSULATED_COMMAND		0x00	/* optional */
+#define CDC_GET_ENCAPSULATED_RESPONSE		0x01	/* optional */
+#define CDC_SET_ETHERNET_MULTICAST_FILTERS	0x40	/* optional */
+#define CDC_SET_ETHERNET_PM_PATTERN_FILTER	0x41	/* optional */
+#define CDC_GET_ETHERNET_PM_PATTERN_FILTER	0x42	/* optional */
+#define CDC_SET_ETHERNET_PACKET_FILTER		0x43	/* required */
+#define CDC_GET_ETHERNET_STATISTIC		0x44	/* optional */
+
+/* table 62; bits in cdc_filter */
+#define	CDC_PACKET_TYPE_PROMISCUOUS		(1 << 0)
+#define	CDC_PACKET_TYPE_ALL_MULTICAST		(1 << 1) /* no filter */
+#define	CDC_PACKET_TYPE_DIRECTED		(1 << 2)
+#define	CDC_PACKET_TYPE_BROADCAST		(1 << 3)
+#define	CDC_PACKET_TYPE_MULTICAST		(1 << 4) /* filtered */
 
 #ifdef CONFIG_USB_ETH_RNDIS
 
@@ -1513,8 +1528,9 @@
 		DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue);
 		/* NOTE: table 62 has 5 filter bits to reduce traffic,
 		 * and we "must" support multicast and promiscuous.
-		 * this NOP implements a bad filter...
+		 * this NOP implements a bad filter (always promisc)
 		 */
+		dev->cdc_filter = ctrl->wValue;
 		value = 0;
 		break;
 #endif /* DEV_CONFIG_CDC */
@@ -1941,6 +1957,11 @@
 	int			retval;
 	struct usb_request	*req = 0;
 	unsigned long		flags;
+
+	/* FIXME check dev->cdc_filter to decide whether to send this,
+	 * instead of acting as if CDC_PACKET_TYPE_PROMISCUOUS were
+	 * always set.  RNDIS has the same kind of outgoing filter.
+	 */
 
 	spin_lock_irqsave (&dev->lock, flags);
 	req = container_of (dev->tx_reqs.next, struct usb_request, list);
diff -Nru a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
--- a/drivers/usb/gadget/rndis.c	Mon May 17 16:37:33 2004
+++ b/drivers/usb/gadget/rndis.c	Mon May 17 16:37:33 2004
@@ -78,44 +78,6 @@
 
 static rndis_resp_t *rndis_add_response (int configNr, u32 length);
 
-/* helper functions */
-static u32 devFlags2currentFilter (struct net_device *dev)
-{
-	u32 filter = 0;
-	
-	if (!dev) return 0;
-	
-	if (dev->flags & IFF_MULTICAST) 
-	    filter |= NDIS_PACKET_TYPE_MULTICAST;
-	if (dev->flags & IFF_BROADCAST)
-	    filter |= NDIS_PACKET_TYPE_BROADCAST;
-	if (dev->flags & IFF_ALLMULTI)
-	    filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
-	if (dev->flags & IFF_PROMISC)
-	    filter |= NDIS_PACKET_TYPE_PROMISCUOUS;
-	
-	return filter;
-}
-
-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)
-	    dev->flags |= IFF_BROADCAST;
-	if (currentFilter & NDIS_PACKET_TYPE_ALL_MULTICAST)
-	    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, ...)
@@ -252,13 +214,12 @@
 			rndis_per_dev_params [configNr].vendorDescr, length);
 		retval = 0;
 		break;
-		
+
 	/* mandatory */
 	case OID_GEN_CURRENT_PACKET_FILTER:
 		DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
 		length = 4;
-		*((u32 *) resp + 6) = devFlags2currentFilter (
-					rndis_per_dev_params [configNr].dev);
+		*((u32 *) resp + 6) = rndis_per_dev_params[configNr].filter;
 		retval = 0;
 		break;
 		
@@ -767,16 +728,24 @@
 
 	switch (OID) {
 	case OID_GEN_CURRENT_PACKET_FILTER:
-		DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
 		params = &rndis_per_dev_params [configNr];
-		currentFilter2devFlags(cp[28], params->dev);
 		retval = 0;
 
+		/* FIXME use this NDIS_PACKET_TYPE_* bitflags to
+		 * filter packets in hard_start_xmit()
+		 * NDIS_PACKET_TYPE_x == CDC_PACKET_TYPE_x for x in:
+		 *	PROMISCUOUS, DIRECTED,
+		 *	MULTICAST, ALL_MULTICAST, BROADCAST
+		 */
+		params->filter = *(u32 *)buf;
+		DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
+			__FUNCTION__, params->filter);
+
 		/* 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]) {
+		if (params->filter) {
 			params->state = RNDIS_DATA_INITIALIZED;
 			netif_carrier_on(params->dev);
 			if (netif_running(params->dev))
diff -Nru a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
--- a/drivers/usb/gadget/rndis.h	Mon May 17 16:37:33 2004
+++ b/drivers/usb/gadget/rndis.h	Mon May 17 16:37:33 2004
@@ -277,6 +277,7 @@
 	u8			confignr;
 	int			used;
 	enum rndis_state	state;
+	u32			filter;
 	u32			medium;
 	u32			speed;
 	u32			media_state;
