ChangeSet 1.1673.8.48, 2004/03/30 08:56:45-08:00, david-b@pacbell.net [PATCH] USB Gadget: RNDIS/Ethernet Gadget Driver (2/2) This updates the existing Ethernet gadget driver to support an additional RNDIS configuration on all current USB controllers that can support one. It also includes a bit more work to address the complex autoconfiguration of this driver. Needs testing on PXA. Patch (mostly) contributed by Robert Schwebel, and developed with support from Auerswald GmbH. drivers/usb/gadget/ether.c | 994 +++++++++++++++++++++++++++++++++++++-------- 1 files changed, 818 insertions(+), 176 deletions(-) diff -Nru a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c --- a/drivers/usb/gadget/ether.c Wed Apr 14 14:36:08 2004 +++ b/drivers/usb/gadget/ether.c Wed Apr 14 14:36:08 2004 @@ -1,7 +1,8 @@ /* * ether.c -- Ethernet gadget driver, with CDC and non-CDC options * - * Copyright (C) 2003 David Brownell + * Copyright (C) 2003-2004 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger * * 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 @@ -72,6 +73,7 @@ * implement a "minimalist" vendor-agnostic CDC core: same framing, but * link-level setup only requires activating the configuration. * Linux supports it, but other host operating systems may not. + * (This is a subset of CDC Ethernet.) * * A third option is also in use. Rather than CDC Ethernet, or something * simpler, Microsoft pushes their own approach: RNDIS. The published @@ -87,6 +89,13 @@ #define RX_EXTRA 20 /* guard against rx overflows */ +#ifdef CONFIG_USB_ETH_RNDIS +#include "rndis.h" +#else +#define rndis_init() 0 +#define rndis_exit() do{}while(0) +#endif + /*-------------------------------------------------------------------------*/ struct eth_dev { @@ -107,8 +116,10 @@ struct work_struct work; unsigned zlp:1; unsigned cdc:1; + unsigned rndis:1; unsigned long todo; #define WORK_RX_MEMORY 0 + int rndis_config; }; /* This version autoconfigures as much as possible at run-time. @@ -122,11 +133,13 @@ /*-------------------------------------------------------------------------*/ -/* Thanks to NetChip Technologies for donating this product ID. - * - * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! +/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ + +/* Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only CDC Ethernet configurations. + */ #define CDC_VENDOR_NUM 0x0525 /* NetChip */ #define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ @@ -135,27 +148,61 @@ * with pxa250. We're protocol-compatible, if the host-side drivers * use the endpoint descriptors. bcdDevice (version) is nonzero, so * drivers that need to hard-wire endpoint numbers have a hook. + * + * The protocol is a minimal subset of CDC Ether, which works on any bulk + * hardware that's not deeply broken ... even on hardware that can't talk + * RNDIS (like SA-1100, with no interrupt endpoint, or anything that + * doesn't handle control-OUT). */ #define SIMPLE_VENDOR_NUM 0x049f #define SIMPLE_PRODUCT_NUM 0x505a -/*-------------------------------------------------------------------------*/ +/* 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). + */ +#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ +#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ -#define DEV_CONFIG_VALUE 1 /* some hardware cares */ -/* Include CDC code/data only if we could run on CDC-capable hardware. - * - * FIXME this driver should know how to choose between CDC and non-CDC - * configurations as it initializes, and while handling SET_CONFIGURATION - * and SET_INTERFACE requests. It doesn't yet. +/* Some systems will want different product identifers published in the + * device descriptor, either numbers or strings or both. These string + * parameters are in UTF-8 (superset of ASCII's 7 bit characters). */ +static ushort __initdata idVendor; +module_param(idVendor, ushort, S_IRUGO); +MODULE_PARM_DESC(idVendor, "USB Vendor ID"); + +static ushort __initdata idProduct; +module_param(idProduct, ushort, S_IRUGO); +MODULE_PARM_DESC(idProduct, "USB Product ID"); + +static ushort __initdata bcdDevice; +module_param(bcdDevice, ushort, S_IRUGO); +MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); + +static char *__initdata iManufacturer; +module_param(iManufacturer, charp, S_IRUGO); +MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); + +static char *__initdata iProduct; +module_param(iProduct, charp, S_IRUGO); +MODULE_PARM_DESC(iProduct, "USB Product string"); + + +/*-------------------------------------------------------------------------*/ + +/* Include CDC support if we could run on CDC-capable hardware. */ + #ifdef CONFIG_USB_GADGET_NET2280 #define DEV_CONFIG_CDC #endif #ifdef CONFIG_USB_GADGET_DUMMY_HCD -// #define DEV_CONFIG_CDC +#define DEV_CONFIG_CDC #endif #ifdef CONFIG_USB_GADGET_GOKU @@ -170,6 +217,24 @@ #define DEV_CONFIG_CDC #endif + +/* For CDC-incapable hardware, choose the simple cdc subset. + * Anything that talks bulk (without notable bugs) can do this. + */ +#ifdef CONFIG_USB_GADGET_PXA +#define DEV_CONFIG_SUBSET +#endif + +#ifdef CONFIG_USB_GADGET_SH +#define DEV_CONFIG_SUBSET +#endif + +#ifdef CONFIG_USB_GADGET_SA1100 +/* use non-CDC for backwards compatibility */ +#define DEV_CONFIG_SUBSET +#endif + + /*-------------------------------------------------------------------------*/ #define DEFAULT_QLEN 2 /* double buffering by default */ @@ -187,8 +252,12 @@ /* also defer IRQs on highspeed TX */ #define TX_DELAY qmult +#define BITRATE(g) ((g->speed == USB_SPEED_HIGH) ? 4800000 : 120000) + #else /* full speed (low speed doesn't do bulk) */ #define qlen(gadget) DEFAULT_QLEN + +#define BITRATE(g) (12000) #endif @@ -229,8 +298,13 @@ /* * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. Notice how most of the cdc descriptors - * aren't needed in the "minimalist" mode. + * descriptors are built on demand. For now we do either full CDC, or + * our simple subset, with RNDIS as an optional second configuration. + * + * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But + * the class descriptors match a modem (they're ignored; it's really just + * Ethernet functionality), they don't need the NOP altsetting, and the + * status transfer endpoint isn't optional. */ #define STRING_MANUFACTURER 1 @@ -238,12 +312,24 @@ #define STRING_ETHADDR 3 #define STRING_DATA 4 #define STRING_CONTROL 5 +#define STRING_RNDIS_CONTROL 6 +#define STRING_CDC 7 +#define STRING_SUBSET 8 +#define STRING_RNDIS 9 #define USB_BUFSIZ 256 /* holds our biggest descriptor */ /* - * This device advertises one configuration. + * This device advertises one configuration, eth_config, unless RNDIS + * is enabled (rndis_config) on hardware supporting at least two configs. + * + * NOTE: Controllers like superh_udc should probably be able to use + * an RNDIS-only configuration. */ + +#define DEV_CONFIG_VALUE 1 /* cdc or subset */ +#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ + static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, @@ -270,22 +356,37 @@ /* compute wTotalLength on the fly */ .bNumInterfaces = 2, .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = STRING_PRODUCT, + .iConfiguration = STRING_CDC, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = 1, }; -#ifdef DEV_CONFIG_CDC +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_config_descriptor +rndis_config = { + .bLength = sizeof rndis_config, + .bDescriptorType = USB_DT_CONFIG, + + /* compute wTotalLength on the fly */ + .bNumInterfaces = 2, + .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, + .iConfiguration = STRING_RNDIS, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, +}; +#endif /* - * Compared to the "minimalist" non-CDC model, the CDC model adds + * Compared to the simple CDC subset, the full CDC Ethernet model adds * three class descriptors, two interface descriptors, optional status * endpoint. Both have a "data" interface and two bulk endpoints. * There are also differences in how control requests are handled. + * + * RNDIS shares a lot with CDC-Ethernet, since it's a variant of + * the CDC-ACM (modem) spec. */ -/* master comm interface optionally has a status notification endpoint */ - +#ifdef DEV_CONFIG_CDC static struct usb_interface_descriptor control_intf = { .bLength = sizeof control_intf, @@ -299,6 +400,24 @@ .bInterfaceProtocol = 0, .iInterface = STRING_CONTROL, }; +#endif + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_interface_descriptor +rndis_control_intf = { + .bLength = sizeof rndis_control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = 2, /* abstract control model */ + .bInterfaceProtocol = 0xff, /* vendor specific */ + .iInterface = STRING_RNDIS_CONTROL, +}; +#endif + +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) /* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ struct header_desc { @@ -311,13 +430,13 @@ static const struct header_desc header_desc = { .bLength = sizeof header_desc, - .bDescriptorType = 0x24, + .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = 0, .bcdCDC = __constant_cpu_to_le16 (0x0110), }; -/* "Union Functional Descriptor" from CDC spec 5.2.3.X */ +/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ struct union_desc { u8 bLength; u8 bDescriptorType; @@ -330,13 +449,58 @@ static const struct union_desc union_desc = { .bLength = sizeof union_desc, - .bDescriptorType = 0x24, + .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = 6, .bMasterInterface0 = 0, /* index of control interface */ .bSlaveInterface0 = 1, /* index of DATA interface */ }; +#endif /* CDC || RNDIS */ + +#ifdef CONFIG_USB_ETH_RNDIS + +/* "Call Management Descriptor" from CDC spec 5.2.3.3 */ +struct call_mgmt_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubType; + + u8 bmCapabilities; + u8 bDataInterface; +} __attribute__ ((packed)); + +static const struct call_mgmt_descriptor call_mgmt_descriptor = { + .bLength = sizeof call_mgmt_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = 0x01, + + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + + +/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.4 */ +struct acm_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubType; + + u8 bmCapabilities; +} __attribute__ ((packed)); + +static struct acm_descriptor acm_descriptor = { + .bLength = sizeof acm_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = 0x02, + + .bmCapabilities = 0X00, +}; + +#endif + +#ifdef DEV_CONFIG_CDC + /* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ struct ether_desc { u8 bLength; @@ -352,7 +516,7 @@ static const struct ether_desc ether_desc = { .bLength = sizeof ether_desc, - .bDescriptorType = 0x24, + .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = 0x0f, /* this descriptor actually adds value, surprise! */ @@ -363,16 +527,28 @@ .bNumberPowerFilters = 0, }; -/* include the status endpoint if we can, even though it's optional. +#endif + +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) + +/* include the status endpoint if we can, even where it's optional. + * use small wMaxPacketSize, since many "interrupt" endpoints have + * very small fifos and it's no big deal if CDC_NOTIFY_SPEED_CHANGE + * takes two packets. also default to a big transfer interval, to + * waste less bandwidth. * - * some drivers (like current Linux cdc-ether!) "need" it to exist even + * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even * if they ignore the connect/disconnect notifications that real aether * can provide. more advanced cdc configurations might want to support * encapsulated commands (vendor-specific, using control-OUT). + * + * RNDIS requires the status endpoint, since it uses that encapsulation + * mechanism for its funky RPC scheme. */ -#define LOG2_STATUS_INTERVAL_MSEC 6 -#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 8 /* 8 byte header + data */ + static struct usb_endpoint_descriptor fs_status_desc = { .bLength = USB_DT_ENDPOINT_SIZE, @@ -383,6 +559,9 @@ .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, }; +#endif + +#ifdef DEV_CONFIG_CDC /* the default data interface has no endpoints ... */ @@ -397,10 +576,9 @@ .bInterfaceClass = USB_CLASS_CDC_DATA, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, - .iInterface = STRING_DATA, }; -/* ... but the "real" data interface has two full speed bulk endpoints */ +/* ... but the "real" data interface has two bulk endpoints */ static const struct usb_interface_descriptor data_intf = { @@ -415,16 +593,39 @@ .bInterfaceProtocol = 0, .iInterface = STRING_DATA, }; -#else + +#endif + +#ifdef CONFIG_USB_ETH_RNDIS + +/* RNDIS doesn't activate by changing to the "real" altsetting */ + +static const struct usb_interface_descriptor +rndis_data_intf = { + .bLength = sizeof rndis_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = STRING_DATA, +}; + +#endif + +#ifdef DEV_CONFIG_SUBSET /* - * "Minimalist" non-CDC option is a simple vendor-neutral model that most + * "Simple" CDC-subset option is a simple vendor-neutral model that most * full speed controllers can handle: one interface, two bulk endpoints. */ static const struct usb_interface_descriptor -data_intf = { - .bLength = sizeof data_intf, +subset_data_intf = { + .bLength = sizeof subset_data_intf, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, @@ -436,7 +637,7 @@ .iInterface = STRING_DATA, }; -#endif /* DEV_CONFIG_CDC */ +#endif /* SUBSET */ static struct usb_endpoint_descriptor @@ -457,7 +658,7 @@ .bmAttributes = USB_ENDPOINT_XFER_BULK, }; -static const struct usb_descriptor_header *fs_function [] = { +static const struct usb_descriptor_header *fs_eth_function [10] = { #ifdef DEV_CONFIG_CDC /* "cdc" mode descriptors */ (struct usb_descriptor_header *) &control_intf, @@ -466,15 +667,44 @@ (struct usb_descriptor_header *) ðer_desc, /* NOTE: status endpoint may need to be removed */ (struct usb_descriptor_header *) &fs_status_desc, + /* data interface, with altsetting */ (struct usb_descriptor_header *) &data_nop_intf, -#endif /* DEV_CONFIG_CDC */ - /* minimalist core */ (struct usb_descriptor_header *) &data_intf, (struct usb_descriptor_header *) &fs_source_desc, (struct usb_descriptor_header *) &fs_sink_desc, 0, +#endif /* DEV_CONFIG_CDC */ }; +static inline void __init fs_subset_descriptors(void) +{ +#ifdef DEV_CONFIG_SUBSET + fs_eth_function[0] = (struct usb_descriptor_header *) &subset_data_intf; + fs_eth_function[1] = (struct usb_descriptor_header *) &fs_source_desc; + fs_eth_function[2] = (struct usb_descriptor_header *) &fs_sink_desc; + fs_eth_function[3] = 0; +#else + fs_eth_function[0] = 0; +#endif +} + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_descriptor_header *fs_rndis_function [] = { + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) &fs_status_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &fs_source_desc, + (struct usb_descriptor_header *) &fs_sink_desc, + 0, +}; +#endif + #ifdef CONFIG_USB_GADGET_DUALSPEED /* @@ -482,7 +712,7 @@ * descriptors, unless they only run at full speed. */ -#ifdef DEV_CONFIG_CDC +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) static struct usb_endpoint_descriptor hs_status_desc = { .bLength = USB_DT_ENDPOINT_SIZE, @@ -490,7 +720,7 @@ .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), - .bInterval = LOG2_STATUS_INTERVAL_MSEC + 3, + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, }; #endif /* DEV_CONFIG_CDC */ @@ -523,7 +753,7 @@ .bNumConfigurations = 1, }; -static const struct usb_descriptor_header *hs_function [] = { +static const struct usb_descriptor_header *hs_eth_function [10] = { #ifdef DEV_CONFIG_CDC /* "cdc" mode descriptors */ (struct usb_descriptor_header *) &control_intf, @@ -532,15 +762,44 @@ (struct usb_descriptor_header *) ðer_desc, /* NOTE: status endpoint may need to be removed */ (struct usb_descriptor_header *) &hs_status_desc, + /* data interface, with altsetting */ (struct usb_descriptor_header *) &data_nop_intf, -#endif /* DEV_CONFIG_CDC */ - /* minimalist core */ (struct usb_descriptor_header *) &data_intf, (struct usb_descriptor_header *) &hs_source_desc, (struct usb_descriptor_header *) &hs_sink_desc, 0, +#endif /* DEV_CONFIG_CDC */ }; +static inline void __init hs_subset_descriptors(void) +{ +#ifdef DEV_CONFIG_SUBSET + hs_eth_function[0] = (struct usb_descriptor_header *) &subset_data_intf; + hs_eth_function[1] = (struct usb_descriptor_header *) &fs_source_desc; + hs_eth_function[2] = (struct usb_descriptor_header *) &fs_sink_desc; + hs_eth_function[3] = 0; +#else + hs_eth_function[0] = 0; +#endif +} + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_descriptor_header *hs_rndis_function [] = { + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) &hs_status_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + 0, +}; +#endif + /* maxpacket and other transfer characteristics vary by speed. */ #define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs)) @@ -550,6 +809,10 @@ /* if there's no high speed support, maxpacket doesn't change. */ #define ep_desc(g,hs,fs) fs +static inline void __init hs_subset_descriptors(void) +{ +} + #endif /* !CONFIG_USB_GADGET_DUALSPEED */ /*-------------------------------------------------------------------------*/ @@ -557,6 +820,7 @@ /* descriptors that are built on-demand */ static char manufacturer [40]; +static char product_desc [40] = DRIVER_DESC; #ifdef DEV_CONFIG_CDC /* address that the host will use ... usually assigned at random */ @@ -566,12 +830,20 @@ /* static strings, in iso 8859/1 */ static struct usb_string strings [] = { { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, driver_desc, }, + { STRING_PRODUCT, product_desc, }, + { STRING_DATA, "Ethernet Data", }, #ifdef DEV_CONFIG_CDC + { STRING_CDC, "CDC Ethernet", }, { STRING_ETHADDR, ethaddr, }, { STRING_CONTROL, "CDC Communications Control", }, #endif - { STRING_DATA, "Ethernet Data", }, +#ifdef DEV_CONFIG_SUBSET + { STRING_SUBSET, "CDC Ethernet Subset", }, +#endif +#ifdef CONFIG_USB_ETH_RNDIS + { STRING_RNDIS, "RNDIS", }, + { STRING_RNDIS_CONTROL, "RNDIS Communications Control", }, +#endif { } /* end of list */ }; @@ -588,20 +860,34 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) { int len; - const struct usb_descriptor_header **function = fs_function; #ifdef CONFIG_USB_GADGET_DUALSPEED int hs = (speed == USB_SPEED_HIGH); if (type == USB_DT_OTHER_SPEED_CONFIG) hs = !hs; - if (hs) - function = hs_function; +#define which_config(t) (hs ? & t ## _config : & t ## _config) +#define which_fn(t) (hs ? & hs_ ## t ## _function : & fs_ ## t ## _function) +#else +#define which_config(t) (& t ## _config) +#define which_fn(t) (& fs_ ## t ## _function) #endif - /* a single configuration must always be index 0 */ - if (index > 0) + if (index >= device_desc.bNumConfigurations) return -EINVAL; - len = usb_gadget_config_buf (ð_config, buf, USB_BUFSIZ, function); + +#ifdef CONFIG_USB_ETH_RNDIS + /* list the RNDIS config first, to make Microsoft's drivers + * happy. DOCSIS 1.0 needs this too. + */ + if (device_desc.bNumConfigurations == 2 && index == 0) + len = usb_gadget_config_buf (which_config (rndis), buf, + USB_BUFSIZ, (const struct usb_descriptor_header **) + which_fn (rndis)); + else +#endif + len = usb_gadget_config_buf (which_config (eth), buf, + USB_BUFSIZ, (const struct usb_descriptor_header **) + which_fn (eth)); if (len < 0) return len; ((struct usb_config_descriptor *) buf)->bDescriptorType = type; @@ -613,6 +899,91 @@ static void eth_start (struct eth_dev *dev, int gfp_flags); static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags); +#ifdef DEV_CONFIG_CDC +static inline int ether_alt_ep_setup (struct eth_dev *dev, struct usb_ep *ep) +{ + const struct usb_endpoint_descriptor *d; + + /* With CDC, the host isn't allowed to use these two data + * endpoints in the default altsetting for the interface. + * so we don't activate them yet. Reset from SET_INTERFACE. + * + * Strictly speaking RNDIS should work the same: activation is + * a side effect of setting a packet filter. Deactivation is + * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. + */ + + /* one endpoint writes data back IN to the host */ + if (strcmp (ep->name, EP_IN_NAME) == 0) { + d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); + ep->driver_data = dev; + dev->in_ep = ep; + dev->in = d; + + /* one endpoint just reads OUT packets */ + } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { + d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); + ep->driver_data = dev; + dev->out_ep = ep; + dev->out = d; + + /* optional status/notification endpoint */ + } else if (EP_STATUS_NAME && + strcmp (ep->name, EP_STATUS_NAME) == 0) { + int result; + + d = ep_desc (dev->gadget, &hs_status_desc, &fs_status_desc); + result = usb_ep_enable (ep, d); + if (result < 0) + return result; + + ep->driver_data = dev; + dev->status_ep = ep; + dev->status = d; + } + return 0; +} +#endif + +#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) +static inline int ether_ep_setup (struct eth_dev *dev, struct usb_ep *ep) +{ + int result; + const struct usb_endpoint_descriptor *d; + + /* CDC subset is simpler: if the device is there, + * it's live with rx and tx endpoints. + * + * Do this as a shortcut for RNDIS too. + */ + + /* one endpoint writes data back IN to the host */ + if (strcmp (ep->name, EP_IN_NAME) == 0) { + d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); + result = usb_ep_enable (ep, d); + if (result < 0) + return result; + + ep->driver_data = dev; + dev->in_ep = ep; + dev->in = d; + + /* one endpoint just reads OUT packets */ + } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { + d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); + result = usb_ep_enable (ep, d); + if (result < 0) + return result; + + ep->driver_data = dev; + dev->out_ep = ep; + dev->out = d; + } + + return 0; +} +#endif + static int set_ether_config (struct eth_dev *dev, int gfp_flags) { @@ -621,37 +992,17 @@ struct usb_gadget *gadget = dev->gadget; gadget_for_each_ep (ep, gadget) { - const struct usb_endpoint_descriptor *d; - #ifdef DEV_CONFIG_CDC - /* With CDC, the host isn't allowed to use these two data - * endpoints in the default altsetting for the interface. - * so we don't activate them yet. - * - * RNDIS is the same, but activation is a side effect of - * an RPC setting a packet filter (no SET_INTERFACE). - */ + if (!dev->rndis && dev->cdc) { + result = ether_alt_ep_setup (dev, ep); + if (result == 0) + continue; + } +#endif - /* one endpoint writes data back IN to the host */ - if (strcmp (ep->name, EP_IN_NAME) == 0) { - d = ep_desc (gadget, &hs_source_desc, &fs_source_desc); - ep->driver_data = dev; - dev->in_ep = ep; - dev->in = d; - continue; - - /* one endpoint just reads OUT packets */ - } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { - d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc); - ep->driver_data = dev; - dev->out_ep = ep; - dev->out = d; - continue; - } - - /* optional status/notification endpoint */ - else if (EP_STATUS_NAME && - strcmp (ep->name, EP_STATUS_NAME) == 0) { +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis && strcmp (ep->name, EP_STATUS_NAME) == 0) { + const struct usb_endpoint_descriptor *d; d = ep_desc (gadget, &hs_status_desc, &fs_status_desc); result = usb_ep_enable (ep, d); if (result == 0) { @@ -660,42 +1011,17 @@ dev->status = d; continue; } - } - -#else /* !CONFIG_CDC_ETHER */ - - /* non-CDC is simpler: if the device is there, - * it's live with rx and tx endpoints. - */ - /* one endpoint writes data back IN to the host */ - if (strcmp (ep->name, EP_IN_NAME) == 0) { - d = ep_desc (gadget, &hs_source_desc, &fs_source_desc); - result = usb_ep_enable (ep, d); - if (result == 0) { - ep->driver_data = dev; - dev->in_ep = ep; - dev->in = d; - continue; - } + } else +#endif - /* one endpoint just reads OUT packets */ - } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { - d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc); - result = usb_ep_enable (ep, d); - if (result == 0) { - ep->driver_data = dev; - dev->out_ep = ep; - dev->out = d; + { +#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) + result = ether_ep_setup (dev, ep); + if (result == 0) continue; - } +#endif } -#endif /* !CONFIG_CDC_ETHER */ - - /* ignore any other endpoints */ - else - continue; - /* stop on error */ ERROR (dev, "can't enable %s, result %d\n", ep->name, result); break; @@ -706,23 +1032,41 @@ if (result == 0) result = alloc_requests (dev, qlen (gadget), gfp_flags); -#ifndef DEV_CONFIG_CDC - if (result == 0) { + /* on error, disable any endpoints */ + if (result < 0) { +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) + if (dev->status_ep) + (void) usb_ep_disable (dev->status_ep); +#endif + dev->status_ep = 0; + dev->status = 0; +#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) + if (dev->rndis || !dev->cdc) { + if (dev->in_ep) + (void) usb_ep_disable (dev->in_ep); + if (dev->out_ep) + (void) usb_ep_disable (dev->out_ep); + } +#endif + dev->in_ep = 0; + dev->in = 0; + dev->out_ep = 0; + dev->out = 0; + } else + + /* activate non-CDC configs right away + * this isn't strictly according to the RNDIS spec + */ +#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) + if (dev->rndis || !dev->cdc) { netif_carrier_on (dev->net); if (netif_running (dev->net)) { spin_unlock (&dev->lock); eth_start (dev, GFP_ATOMIC); spin_lock (&dev->lock); } - } else { - (void) usb_ep_disable (dev->in_ep); - dev->in_ep = 0; - dev->in = 0; - (void) usb_ep_disable (dev->out_ep); - dev->out_ep = 0; - dev->out = 0; } -#endif /* !CONFIG_CDC_ETHER */ +#endif if (result == 0) DEBUG (dev, "qlen %d\n", qlen (gadget)); @@ -797,9 +1141,15 @@ switch (number) { case DEV_CONFIG_VALUE: + dev->rndis = 0; + result = set_ether_config (dev, gfp_flags); + break; +#ifdef CONFIG_USB_ETH_RNDIS + case DEV_RNDIS_CONFIG_VALUE: + dev->rndis = 1; result = set_ether_config (dev, gfp_flags); break; - // OR: RNDIS_CONFIG_VALUE ... +#endif default: result = -EINVAL; /* FALL THROUGH */ @@ -821,21 +1171,29 @@ } dev->config = number; - INFO (dev, "%s speed config #%d: %s\n", speed, number, - driver_desc); + INFO (dev, "%s speed config #%d: %s, using %s\n", + speed, number, driver_desc, + dev->rndis + ? "RNDIS" + : (dev->cdc + ? "CDC Ethernet" + : "CDC Ethernet Subset")); } return result; } /*-------------------------------------------------------------------------*/ -#ifdef DEV_CONFIG_CDC - -/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications */ +/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications + * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS + * and RNDIS also defines its own bit-incompatible notifications + */ #define CDC_NOTIFY_NETWORK_CONNECTION 0x00 /* required; 6.3.1 */ #define CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 /* optional; 6.3.2 */ #define CDC_NOTIFY_SPEED_CHANGE 0x2a /* required; 6.3.8 */ +#ifdef DEV_CONFIG_CDC + struct cdc_notification { u8 bmRequestType; u8 bNotificationType; @@ -950,19 +1308,46 @@ /* see section 3.8.2 table 10 of the CDC spec for more ethernet * requests, mostly for filters (multicast, pm) and statistics + * section 3.6.2.1 table 4 has ACM requests; RNDIS requires the + * encapsulated command mechanism. */ -#define CDC_SEND_ENCAPSULATED_REQUEST 0x00 /* optional */ +#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 /* optional */ #define CDC_GET_ENCAPSULATED_RESPONSE 0x01 /* optional */ #define CDC_SET_ETHERNET_PACKET_FILTER 0x43 /* required */ +#ifdef CONFIG_USB_ETH_RNDIS + +static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + + if (req->status || req->actual != req->length) + DEBUG (dev, "rndis response complete --> %d, %d/%d\n", + req->status, req->actual, req->length); + + /* done sending after CDC_GET_ENCAPSULATED_RESPONSE */ + rndis_free_response (dev->rndis_config, req->buf); +} + +static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + + /* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */ + if (rndis_msg_parser (dev->rndis_config, (u8 *) req->buf)) + ERROR(dev, "%s: rndis parse error\n", __FUNCTION__ ); +} + +#endif /* RNDIS */ + /* * The setup() callback implements all the ep0 functionality that's not * handled lower down. CDC has a number of less-common features: * * - two interfaces: control, and ethernet data - * - data interface has two altsettings: default, and active + * - Ethernet data interface has two altsettings: default, and active * - class-specific descriptors for the control interface - * - a mandatory class-specific control request + * - class-specific control requests */ static int eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) @@ -1119,6 +1504,7 @@ */ if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) || !dev->cdc + || dev->rndis || ctrl->wLength != 0 || ctrl->wIndex > 1) break; @@ -1131,6 +1517,45 @@ break; #endif /* DEV_CONFIG_CDC */ +#ifdef CONFIG_USB_ETH_RNDIS + /* RNDIS uses the CDC command encapsulation mechanism to implement + * an RPC scheme, with much getting/setting of attributes by OID. + */ + case CDC_SEND_ENCAPSULATED_COMMAND: + if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) + || !dev->rndis + || ctrl->wLength > USB_BUFSIZ + || ctrl->wValue + || rndis_control_intf.bInterfaceNumber + != ctrl->wIndex) + break; + /* read the request, then process it */ + value = ctrl->wLength; + req->complete = rndis_command_complete; + /* later, rndis_control_ack () sends a notification */ + break; + + case CDC_GET_ENCAPSULATED_RESPONSE: + if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) + == ctrl->bRequestType + && dev->rndis + // && ctrl->wLength >= 0x0400 + && !ctrl->wValue + && rndis_control_intf.bInterfaceNumber + == ctrl->wIndex) { + u8 *buf; + + /* return the result */ + buf = rndis_get_next_response (dev->rndis_config, + &value); + if (buf) { + memcpy (req->buf, buf, value); + req->complete = rndis_response_complete; + } + } + break; +#endif /* RNDIS */ + default: VDEBUG (dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", @@ -1178,6 +1603,8 @@ { struct eth_dev *dev = (struct eth_dev *) net->priv; + // FIXME if rndis, don't change while link's live + if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) return -ERANGE; /* no zero-length packet read wanted after mtu-sized packets */ @@ -1260,7 +1687,27 @@ int retval = -ENOMEM; size_t size; + /* Padding up to RX_EXTRA handles minor disagreements with host. + * Normally we use the USB "terminate on short read" convention; + * so allow up to (N*maxpacket)-1, since that memory is normally + * already allocated. Major loss of synch means -EOVERFLOW; any + * obviously corrupted packets will automatically be discarded. + * + * RNDIS uses internal framing, and explicitly allows senders to + * pad to end-of-packet. That's potentially nice for speed, + * but means receivers can't recover synch on their own. + */ size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA); + size += dev->out_ep->maxpacket - 1; +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis) + size += sizeof (struct rndis_packet_msg_type); +#endif + size -= size % dev->out_ep->maxpacket; +#ifdef CONFIG_USB_ETH_RNDIS + if (!dev->rndis) +#endif + size--; if ((skb = alloc_skb (size, gfp_flags)) == 0) { DEBUG (dev, "no rx skb\n"); @@ -1297,6 +1744,11 @@ /* normal completion */ case 0: skb_put (skb, req->actual); +#ifdef CONFIG_USB_ETH_RNDIS + /* we know MaxPacketsPerTransfer == 1 here */ + if (dev->rndis) + rndis_rm_hdr (req->buf, &(skb->len)); +#endif if (ETH_HLEN > skb->len || skb->len > ETH_FRAME_LEN) { dev->stats.rx_errors++; dev->stats.rx_length_errors++; @@ -1494,7 +1946,23 @@ /* no buffer copies needed, unless the network stack did it * or the hardware can't use skb buffers. + * or there's not enough space for any RNDIS headers we need */ +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis) { + struct sk_buff *skb_rndis; + + skb_rndis = skb_realloc_headroom (skb, + sizeof (struct rndis_packet_msg_type)); + if (!skb_rndis) + goto drop; + + dev_kfree_skb_any (skb); + skb = skb_rndis; + rndis_add_hdr (skb); + length = skb->len; + } +#endif req->buf = skb->data; req->context = skb; req->complete = tx_complete; @@ -1527,6 +1995,9 @@ } if (retval) { +#ifdef CONFIG_USB_ETH_RNDIS +drop: +#endif dev->stats.tx_dropped++; dev_kfree_skb_any (skb); spin_lock_irqsave (&dev->lock, flags); @@ -1538,6 +2009,67 @@ return 0; } +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_ETH_RNDIS + +static void rndis_send_media_state (struct eth_dev *dev, int connect) +{ + if (!dev) + return; + + if (connect) { + if (rndis_signal_connect (dev->rndis_config)) + return; + } else { + if (rndis_signal_disconnect (dev->rndis_config)) + return; + } +} + +static int rndis_control_ack (struct net_device *net) +{ + struct eth_dev *dev = (struct eth_dev *) net->priv; + u32 length; + struct usb_request *resp; + + /* RNDIS completion function */ + + /* Allocate memory for notification ie. ACK */ + resp = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC); + if (!resp) { + DEBUG (dev, "status ENOMEM\n"); + return -ENOMEM; + } + + resp->buf = usb_ep_alloc_buffer (dev->status_ep, 8, + &resp->dma, GFP_ATOMIC); + if (!resp->buf) { + DEBUG (dev, "status buf ENOMEM\n"); + usb_ep_free_request (dev->status_ep, resp); + return -ENOMEM; + } + + /* Send RNDIS RESPONSE_AVAILABLE notification; + * CDC_NOTIFY_RESPONSE_AVAILABLE should work too + */ + resp->length = 8; + resp->complete = rndis_response_complete; + + *((u32 *) resp->buf) = __constant_cpu_to_le32 (1); + *((u32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); + + length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC); + if (length < 0) { + resp->status = 0; + rndis_response_complete (dev->status_ep, resp); + } + + return 0; +} + +#endif /* RNDIS */ + static void eth_start (struct eth_dev *dev, int gfp_flags) { DEBUG (dev, "%s\n", __FUNCTION__); @@ -1548,6 +2080,14 @@ /* and open the tx floodgates */ atomic_set (&dev->tx_qlen, 0); netif_wake_queue (dev->net); +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis) { + rndis_set_param_medium (dev->rndis_config, + NDIS_MEDIUM_802_3, + BITRATE(dev->gadget)); + rndis_send_media_state (dev, 1); + } +#endif } static int eth_open (struct net_device *net) @@ -1587,6 +2127,14 @@ usb_ep_enable (dev->status_ep, dev->status); } } + +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis) { + rndis_set_param_medium (dev->rndis_config, + NDIS_MEDIUM_802_3, 0); + rndis_send_media_state (dev, 0); + } +#endif return 0; } @@ -1599,6 +2147,10 @@ struct eth_dev *dev = get_gadget_data (gadget); DEBUG (dev, "unbind\n"); +#ifdef CONFIG_USB_ETH_RNDIS + rndis_deregister (dev->rndis_config); + rndis_exit (); +#endif /* we've already been disconnected ... no i/o is active */ if (dev->req) { @@ -1622,10 +2174,18 @@ { struct eth_dev *dev; struct net_device *net; - u8 cdc = 1, zlp = 1; + u8 cdc = 1, zlp = 1, rndis = 1; struct usb_ep *ep; int status = -ENOMEM; + /* these flags are only ever cleared; compiler take note */ +#ifndef DEV_CONFIG_CDC + cdc = 0; +#endif +#ifndef CONFIG_USB_ETH_RNDIS + rndis = 0; +#endif + /* Because most host side USB stacks handle CDC Ethernet, that * standard protocol is _strongly_ preferred for interop purposes. * (By everyone except Microsoft.) @@ -1642,6 +2202,7 @@ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204); /* sh doesn't support multiple interfaces or configs */ cdc = 0; + rndis = 0; } else if (gadget_is_sa1100 (gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205); /* hardware can't write zlps */ @@ -1666,16 +2227,51 @@ gadget->name); return -ENODEV; } + snprintf (manufacturer, sizeof manufacturer, + UTS_SYSNAME " " UTS_RELEASE "/%s", + gadget->name); -#ifndef DEV_CONFIG_CDC - /* in case someone doesn't add their hardware correctly */ - if (cdc) { - dev_err (&gadget->dev, - "CDC Ethernet support for '%s' is missing\n", - gadget->name); - return -ENODEV; + /* CDC subset ... recognized by Linux since 2.4.10, but Windows + * drivers aren't widely available. + */ + if (!cdc) { + device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; + device_desc.idVendor = + __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); } -#endif + + /* If there's an RNDIS configuration, that's what Windows wants to + * be using ... so use these product IDs here and in the "linux.inf" + * needed to install MSFT drivers. Current Linux kernels will use + * the second configuration if it's CDC Ethernet, and need some help + * to choose the right configuration otherwise. + */ + if (rndis) { + device_desc.idVendor = + __constant_cpu_to_le16(RNDIS_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); + snprintf (product_desc, sizeof product_desc, + "RNDIS/%s", driver_desc); + } + + /* support optional vendor/distro customization */ + if (idVendor) { + if (!idProduct) { + dev_err (&gadget->dev, "idVendor needs idProduct!\n"); + return -ENODEV; + } + device_desc.idVendor = cpu_to_le16(idVendor); + device_desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + device_desc.bcdDevice = cpu_to_le16(bcdDevice); + } + if (iManufacturer) + strlcpy (manufacturer, iManufacturer, sizeof manufacturer); + if (iProduct) + strlcpy (product_desc, iProduct, sizeof product_desc); /* all we really need is bulk IN/OUT */ usb_ep_autoconfig_reset (gadget); @@ -1696,25 +2292,43 @@ EP_OUT_NAME = ep->name; ep->driver_data = ep; /* claim */ -#ifdef DEV_CONFIG_CDC /* CDC Ethernet control interface doesn't require a status endpoint. * Since some hosts expect one, try to allocate one anyway. */ - if (cdc) { + if (cdc || rndis) { ep = usb_ep_autoconfig (gadget, &fs_status_desc); if (ep) { EP_STATUS_NAME = ep->name; ep->driver_data = ep; /* claim */ - } else { + } else if (rndis) { + dev_err (&gadget->dev, + "can't run RNDIS on %s\n", + gadget->name); + return -ENODEV; +#ifdef DEV_CONFIG_CDC + } else if (cdc) { control_intf.bNumEndpoints = 0; /* FIXME remove endpoint from descriptor list */ - } - } else #endif + } + } + + /* one config: cdc, else minimal subset */ + if (!cdc) { eth_config.bNumInterfaces = 1; + eth_config.iConfiguration = STRING_SUBSET; + fs_subset_descriptors(); + hs_subset_descriptors(); + } + + /* For now RNDIS is always a second config */ + if (rndis) + device_desc.bNumConfigurations = 2; #ifdef CONFIG_USB_GADGET_DUALSPEED - if (!cdc) + if (rndis) + dev_qualifier.bNumConfigurations = 2; + else if (!cdc) dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC; /* assumes ep0 uses the same value for both speeds ... */ @@ -1723,13 +2337,12 @@ /* and that all endpoints are dual-speed */ hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; -#ifdef DEV_CONFIG_CDC +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) if (EP_STATUS_NAME) hs_status_desc.bEndpointAddress = fs_status_desc.bEndpointAddress; #endif - -#endif +#endif /* DUALSPEED */ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; usb_gadget_set_selfpowered (gadget); @@ -1777,6 +2390,15 @@ } #endif + if (rndis) { + status = rndis_init(); + if (status < 0) { + dev_err (&gadget->dev, "can't init RNDIS, %d\n", + status); + goto fail; + } + } + net->change_mtu = eth_change_mtu; net->get_stats = eth_get_stats; net->hard_start_xmit = eth_start_xmit; @@ -1812,37 +2434,57 @@ // SET_NETDEV_DEV (dev->net, &gadget->dev); status = register_netdev (dev->net); - if (status == 0) { + if (status < 0) + goto fail1; + + INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); + INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name, + EP_OUT_NAME, EP_IN_NAME, + EP_STATUS_NAME ? " STATUS " : "", + EP_STATUS_NAME ? EP_STATUS_NAME : "" + ); + INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + net->dev_addr [0], net->dev_addr [1], + net->dev_addr [2], net->dev_addr [3], + net->dev_addr [4], net->dev_addr [5]); - INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); - INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name, - EP_OUT_NAME, EP_IN_NAME, - EP_STATUS_NAME ? " STATUS " : "", - EP_STATUS_NAME ? EP_STATUS_NAME : "" - ); - INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", - net->dev_addr [0], net->dev_addr [1], - net->dev_addr [2], net->dev_addr [3], - net->dev_addr [4], net->dev_addr [5]); - - if (!cdc) { - device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; - device_desc.idVendor = - __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); - device_desc.idProduct = - __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); - } #ifdef DEV_CONFIG_CDC - else - INFO (dev, "CDC host enet %s\n", ethaddr); + if (cdc) + INFO (dev, "CDC host enet %s\n", ethaddr); #endif - snprintf (manufacturer, sizeof manufacturer, - UTS_SYSNAME " " UTS_RELEASE "/%s", - gadget->name); - - return status; +#ifdef CONFIG_USB_ETH_RNDIS + if (rndis) { + u32 vendorID = 0; + + /* FIXME RNDIS vendor id == "vendor NIC code" == ? */ + + dev->rndis_config = rndis_register (rndis_control_ack); + if (dev->rndis_config < 0) { +fail0: + unregister_netdev (dev->net); + status = -ENODEV; + goto fail; + } + + /* these set up a lot of the OIDs that RNDIS needs */ + if (rndis_set_param_dev (dev->rndis_config, dev->net, + &dev->stats)) + goto fail0; + if (rndis_set_param_vendor (dev->rndis_config, vendorID, + manufacturer)) + goto fail0; + if (rndis_set_param_medium (dev->rndis_config, + NDIS_MEDIUM_802_3, + 0)) + goto fail0; + INFO (dev, "RNDIS ready\n"); } +#endif + + return status; + +fail1: dev_dbg(&gadget->dev, "register_netdev failed, %d\n", status); fail: eth_unbind (gadget); @@ -1873,7 +2515,7 @@ }; MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell"); +MODULE_AUTHOR ("David Brownell, Benedikt Spanger"); MODULE_LICENSE ("GPL");