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 */