diff options
Diffstat (limited to 'drivers/usb/gadget/ether.c')
-rw-r--r-- | drivers/usb/gadget/ether.c | 640 |
1 files changed, 580 insertions, 60 deletions
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index c070f63..f909267 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -27,9 +27,11 @@ #include <linux/usb/cdc.h> #include <linux/usb/gadget.h> #include <net.h> +#include <malloc.h> #include <linux/ctype.h> #include "gadget_chips.h" +#include "rndis.h" #define USB_NET_NAME "usb_ether" @@ -95,7 +97,13 @@ static const char driver_desc[] = DRIVER_DESC; #define RX_EXTRA 20 /* guard against rx overflows */ -/* CDC support the same host-chosen outgoing packet filters. */ +#ifndef CONFIG_USB_ETH_RNDIS +#define rndis_uninit(x) do {} while (0) +#define rndis_deregister(c) do {} while (0) +#define rndis_exit() do {} while (0) +#endif + +/* CDC and RNDIS support the same host-chosen outgoing packet filters. */ #define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ |USB_CDC_PACKET_TYPE_PROMISCUOUS \ @@ -108,7 +116,7 @@ static const char driver_desc[] = DRIVER_DESC; struct eth_dev { struct usb_gadget *gadget; struct usb_request *req; /* for control responses */ - struct usb_request *stat_req; /* for cdc status */ + struct usb_request *stat_req; /* for cdc & rndis status */ u8 config; struct usb_ep *in_ep, *out_ep, *status_ep; @@ -123,12 +131,14 @@ struct eth_dev { unsigned zlp:1; unsigned cdc:1; + unsigned rndis:1; unsigned suspended:1; unsigned network_started:1; u16 cdc_filter; unsigned long todo; int mtu; #define WORK_RX_MEMORY 0 + int rndis_config; u8 host_mac[ETH_ALEN]; }; @@ -158,8 +168,18 @@ static inline int is_cdc(struct eth_dev *dev) #endif } -#define subset_active(dev) (!is_cdc(dev)) -#define cdc_active(dev) (is_cdc(dev)) +/* "secondary" RNDIS config may sometimes be activated */ +static inline int rndis_active(struct eth_dev *dev) +{ +#ifdef CONFIG_USB_ETH_RNDIS + return dev->rndis; +#else + return 0; +#endif +} + +#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev)) +#define cdc_active(dev) (is_cdc(dev) && !rndis_active(dev)) #define DEFAULT_QLEN 2 /* double buffering by default */ @@ -225,8 +245,17 @@ static inline int BITRATE(struct usb_gadget *g) * 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 +#define SIMPLE_VENDOR_NUM 0x049f /* Compaq Computer Corp. */ +#define SIMPLE_PRODUCT_NUM 0x505a /* Linux-USB "CDC Subset" Device */ + +/* + * 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 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 */ /* * Some systems will want different product identifers published in the @@ -234,17 +263,28 @@ static inline int BITRATE(struct usb_gadget *g) * parameters are in UTF-8 (superset of ASCII's 7 bit characters). */ -static ushort bcdDevice; +/* + * Emulating them in eth_bind: + * static ushort idVendor; + * static ushort idProduct; + */ + #if defined(CONFIG_USBNET_MANUFACTURER) static char *iManufacturer = CONFIG_USBNET_MANUFACTURER; #else static char *iManufacturer = "U-boot"; #endif + +/* These probably need to be configurable. */ +static ushort bcdDevice; static char *iProduct; static char *iSerialNumber; + static char dev_addr[18]; + static char host_addr[18]; + /*-------------------------------------------------------------------------*/ /* @@ -256,7 +296,12 @@ static char host_addr[18]; /* * DESCRIPTORS ... most are static, but strings and (full) configuration * descriptors are built on demand. For now we do either full CDC, or - * our simple subset. + * 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 @@ -264,22 +309,28 @@ static char host_addr[18]; #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 STRING_SERIALNUMBER 10 -/* holds our biggest descriptor */ +/* holds our biggest descriptor (or RNDIS response) */ #define USB_BUFSIZ 256 /* - * This device advertises one configuration, eth_config, - * on hardware supporting at least two configs. + * 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. * * FIXME define some higher-powered configurations to make it easier * to recharge batteries ... */ #define DEV_CONFIG_VALUE 1 /* cdc or subset */ +#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ static struct usb_device_descriptor device_desc = { @@ -320,11 +371,38 @@ eth_config = { .bMaxPower = 1, }; +#ifdef CONFIG_USB_ETH_RNDIS +static 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 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. Unfortunately MSFT's RNDIS driver is buggy; it + * may hang or oops. Since bugfixes (or accurate specs, letting Linux + * work around those bugs) are unlikely to ever come from MSFT, you may + * wish to avoid using RNDIS. + * + * MCCI offers an alternative to RNDIS if you need to connect to Windows + * but have hardware that can't support CDC Ethernet. We add descriptors + * to present the CDC Subset as a (nonconformant) CDC MDLM variant called + * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can + * get those drivers from MCCI, or bundled with various products. */ #ifdef DEV_CONFIG_CDC @@ -343,6 +421,21 @@ control_intf = { }; #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 = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, + .iInterface = STRING_RNDIS_CONTROL, +}; +#endif + static const struct usb_cdc_header_desc header_desc = { .bLength = sizeof header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -351,7 +444,7 @@ static const struct usb_cdc_header_desc header_desc = { .bcdCDC = __constant_cpu_to_le16(0x0110), }; -#if defined(DEV_CONFIG_CDC) +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) static const struct usb_cdc_union_desc union_desc = { .bLength = sizeof union_desc, @@ -362,7 +455,28 @@ static const struct usb_cdc_union_desc union_desc = { .bSlaveInterface0 = 1, /* index of DATA interface */ }; -#endif /* CDC */ +#endif /* CDC || RNDIS */ + +#ifdef CONFIG_USB_ETH_RNDIS + +static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { + .bLength = sizeof call_mgmt_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, + + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + +static const struct usb_cdc_acm_descriptor acm_descriptor = { + .bLength = sizeof acm_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_ACM_TYPE, + + .bmCapabilities = 0x00, +}; + +#endif #ifndef DEV_CONFIG_CDC @@ -414,7 +528,7 @@ static const struct usb_cdc_ether_desc ether_desc = { .bNumberPowerFilters = 0, }; -#if defined(DEV_CONFIG_CDC) +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) /* * include the status endpoint if we can, even where it's optional. @@ -426,6 +540,9 @@ static const struct usb_cdc_ether_desc ether_desc = { * 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 5 /* 1 << 5 == 32 msec */ @@ -478,6 +595,26 @@ data_intf = { #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 /* @@ -558,12 +695,30 @@ static inline void fs_subset_descriptors(void) #endif } +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_descriptor_header *fs_rndis_function[] = { + (struct usb_descriptor_header *) &otg_descriptor, + /* 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, + NULL, +}; +#endif + /* * usb 2.0 devices need to expose both high speed and full speed * descriptors, unless they only run at full speed. */ -#if defined(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, @@ -640,6 +795,25 @@ static inline void hs_subset_descriptors(void) #endif } +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_descriptor_header *hs_rndis_function[] = { + (struct usb_descriptor_header *) &otg_descriptor, + /* 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, + NULL, +}; +#endif + + /* maxpacket and other transfer characteristics vary by speed. */ static inline struct usb_endpoint_descriptor * ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, @@ -675,6 +849,10 @@ static struct usb_string strings[] = { #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 */ }; @@ -735,8 +913,20 @@ config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg) if (index >= device_desc.bNumConfigurations) return -EINVAL; - config = ð_config; - function = which_fn(eth); +#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) { + config = &rndis_config; + function = which_fn(rndis); + } else +#endif + { + config = ð_config; + function = which_fn(eth); + } /* for now, don't advertise srp-only devices */ if (!is_otg) @@ -751,6 +941,7 @@ config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg) /*-------------------------------------------------------------------------*/ +static void eth_start(struct eth_dev *dev, gfp_t gfp_flags); static int alloc_requests(struct eth_dev *dev, unsigned n, gfp_t gfp_flags); static int @@ -759,8 +950,8 @@ set_ether_config(struct eth_dev *dev, gfp_t gfp_flags) int result = 0; struct usb_gadget *gadget = dev->gadget; -#if defined(DEV_CONFIG_CDC) - /* status endpoint used for (optionally) CDC */ +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) + /* status endpoint used for RNDIS and (optionally) CDC */ if (!subset_active(dev) && dev->status_ep) { dev->status = ep_desc(gadget, &hs_status_desc, &fs_status_desc); @@ -785,6 +976,10 @@ set_ether_config(struct eth_dev *dev, gfp_t gfp_flags) * 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. */ if (!cdc_active(dev)) { result = usb_ep_enable(dev->in_ep, dev->in); @@ -815,6 +1010,12 @@ done: (void) usb_ep_disable(dev->out_ep); dev->in = NULL; dev->out = NULL; + } else if (!cdc_active(dev)) { + /* + * activate non-CDC configs right away + * this isn't strictly according to the RNDIS spec + */ + eth_start(dev, GFP_ATOMIC); } /* caller is responsible for cleanup on error */ @@ -828,6 +1029,8 @@ static void eth_reset_config(struct eth_dev *dev) debug("%s\n", __func__); + rndis_uninit(dev->rndis_config); + /* * disable endpoints, forcing (synchronous) completion of * pending i/o. then free the requests. @@ -850,6 +1053,7 @@ static void eth_reset_config(struct eth_dev *dev) if (dev->status) usb_ep_disable(dev->status_ep); + dev->rndis = 0; dev->cdc_filter = 0; dev->config = 0; } @@ -877,6 +1081,12 @@ static int eth_set_config(struct eth_dev *dev, unsigned number, case DEV_CONFIG_VALUE: 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; +#endif default: result = -EINVAL; /* FALL THROUGH */ @@ -910,7 +1120,10 @@ static int eth_set_config(struct eth_dev *dev, unsigned number, dev->config = number; printf("%s speed config #%d: %d mA, %s, using %s\n", speed, number, power, driver_desc, - (cdc_active(dev) ? "CDC Ethernet" + rndis_active(dev) + ? "RNDIS" + : (cdc_active(dev) + ? "CDC Ethernet" : "CDC Ethernet Subset")); } return result; @@ -923,7 +1136,7 @@ static int eth_set_config(struct eth_dev *dev, unsigned number, /* * The interrupt endpoint is used in CDC networking models (Ethernet, ATM) * only to notify the host about link status changes (which we support) or - * report completion of some encapsulated command. Since + * report completion of some encapsulated command (as used in RNDIS). Since * we want this CDC Ethernet code to be vendor-neutral, we don't use that * command mechanism; and only one status request is ever queued. */ @@ -1014,6 +1227,30 @@ static void eth_setup_complete(struct usb_ep *ep, struct usb_request *req) req->status, req->actual, req->length); } +#ifdef CONFIG_USB_ETH_RNDIS + +static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("rndis response complete --> %d, %d/%d\n", + req->status, req->actual, req->length); + + /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */ +} + +static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + int status; + + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ + status = rndis_msg_parser(dev->rndis_config, (u8 *) req->buf); + if (status < 0) + error("%s: rndis parse error %d", __func__, status); +} + +#endif /* RNDIS */ + /* * The setup() callback implements all the ep0 functionality that's not * handled lower down. CDC has a number of less-common features: @@ -1126,6 +1363,7 @@ eth_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) usb_ep_disable(dev->status_ep); usb_ep_enable(dev->status_ep, dev->status); } + value = 0; break; case 1: /* data intf */ @@ -1147,8 +1385,8 @@ eth_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) dev->cdc_filter = DEFAULT_FILTER; if (dev->status) issue_start_status(dev); + eth_start(dev, GFP_ATOMIC); } - value = 0; break; } @@ -1167,11 +1405,11 @@ done_set_intf: || !dev->config || wIndex > 1) break; - if (!(cdc_active(dev)) && wIndex != 0) + if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0) break; /* for CDC, iff carrier is on, data interface is active. */ - if (wIndex != 1) + if (rndis_active(dev) || wIndex != 1) *(u8 *)req->buf = 0; else { /* *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; */ @@ -1207,6 +1445,49 @@ done_set_intf: #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 USB_CDC_SEND_ENCAPSULATED_COMMAND: + if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) + || !rndis_active(dev) + || wLength > USB_BUFSIZ + || wValue + || rndis_control_intf.bInterfaceNumber + != wIndex) + break; + /* read the request, then process it */ + value = wLength; + req->complete = rndis_command_complete; + /* later, rndis_control_ack () sends a notification */ + break; + + case USB_CDC_GET_ENCAPSULATED_RESPONSE: + if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) + == ctrl->bRequestType + && rndis_active(dev) + /* && wLength >= 0x0400 */ + && !wValue + && rndis_control_intf.bInterfaceNumber + == wIndex) { + u8 *buf; + u32 n; + + /* return the result */ + buf = rndis_get_next_response(dev->rndis_config, &n); + if (buf) { + memcpy(req->buf, buf, n); + req->complete = rndis_response_complete; + rndis_free_response(dev->rndis_config, buf); + value = n; + } + /* else stalls ... spec says to avoid that */ + } + break; +#endif /* RNDIS */ + default: debug("unknown control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, @@ -1248,17 +1529,24 @@ static int rx_submit(struct eth_dev *dev, struct usb_request *req, * already allocated. Some hardware doesn't deal well with short * reads (e.g. DMA must be N*maxpacket), so for now don't trim a * byte off the end (to force hardware errors on overflow). + * + * 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. */ debug("%s\n", __func__); size = (ETHER_HDR_SIZE + dev->mtu + RX_EXTRA); size += dev->out_ep->maxpacket - 1; + if (rndis_active(dev)) + size += sizeof(struct rndis_packet_msg_type); size -= size % dev->out_ep->maxpacket; /* * Some platforms perform better when IP packets are aligned, - * but on at least one, checksumming fails otherwise. + * but on at least one, checksumming fails otherwise. Note: + * RNDIS headers involve variable numbers of LE32 values. */ req->buf = (u8 *) NetRxPackets[0]; @@ -1281,6 +1569,22 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) switch (req->status) { /* normal completion */ case 0: + if (rndis_active(dev)) { + /* we know MaxPacketsPerTransfer == 1 here */ + int length = rndis_rm_hdr(req->buf, req->actual); + if (length < 0) + goto length_err; + req->length -= length; + req->actual -= length; + } + if (req->actual < ETH_HLEN || ETH_FRAME_LEN < req->actual) { +length_err: + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + debug("rx length %d\n", req->length); + break; + } + dev->stats.rx_packets++; dev->stats.rx_bytes += req->length; break; @@ -1470,9 +1774,11 @@ drop: static void eth_unbind(struct usb_gadget *gadget) { - struct eth_dev *dev = get_gadget_data(gadget); + struct eth_dev *dev = get_gadget_data(gadget); debug("%s...\n", __func__); + rndis_deregister(dev->rndis_config); + rndis_exit(); /* we've already been disconnected ... no i/o is active */ if (dev->req) { @@ -1504,6 +1810,7 @@ static void eth_unbind(struct usb_gadget *gadget) static void eth_disconnect(struct usb_gadget *gadget) { eth_reset_config(get_gadget_data(gadget)); + /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */ } static void eth_suspend(struct usb_gadget *gadget) @@ -1518,6 +1825,115 @@ static void eth_resume(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_ETH_RNDIS + +/* + * The interrupt endpoint is used in RNDIS to notify the host when messages + * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT + * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even + * REMOTE_NDIS_KEEPALIVE_MSG. + * + * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and + * normally just one notification will be queued. + */ + +static void rndis_control_ack_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + + debug("%s...\n", __func__); + if (req->status || req->actual != req->length) + debug("rndis control ack complete --> %d, %d/%d\n", + req->status, req->actual, req->length); + + if (!l_ethdev.network_started) { + if (rndis_get_state(dev->rndis_config) + == RNDIS_DATA_INITIALIZED) { + l_ethdev.network_started = 1; + printf("USB RNDIS network up!\n"); + } + } + + req->context = NULL; + + if (req != dev->stat_req) + usb_ep_free_request(ep, req); +} + +static char rndis_resp_buf[8] __attribute__((aligned(sizeof(__le32)))); + +static int rndis_control_ack(struct eth_device *net) +{ + struct eth_dev *dev = &l_ethdev; + int length; + struct usb_request *resp = dev->stat_req; + + /* in case RNDIS calls this after disconnect */ + if (!dev->status) { + debug("status ENODEV\n"); + return -ENODEV; + } + + /* in case queue length > 1 */ + if (resp->context) { + resp = usb_ep_alloc_request(dev->status_ep, GFP_ATOMIC); + if (!resp) + return -ENOMEM; + resp->buf = rndis_resp_buf; + } + + /* + * Send RNDIS RESPONSE_AVAILABLE notification; + * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too + */ + resp->length = 8; + resp->complete = rndis_control_ack_complete; + resp->context = dev; + + *((__le32 *) resp->buf) = __constant_cpu_to_le32(1); + *((__le32 *) (resp->buf + 4)) = __constant_cpu_to_le32(0); + + length = usb_ep_queue(dev->status_ep, resp, GFP_ATOMIC); + if (length < 0) { + resp->status = 0; + rndis_control_ack_complete(dev->status_ep, resp); + } + + return 0; +} + +#else + +#define rndis_control_ack NULL + +#endif /* RNDIS */ + +static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) +{ + if (rndis_active(dev)) { + rndis_set_param_medium(dev->rndis_config, + NDIS_MEDIUM_802_3, + BITRATE(dev->gadget)/100); + rndis_signal_connect(dev->rndis_config); + } +} + +static int eth_stop(struct eth_dev *dev) +{ + if (rndis_active(dev)) { + rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0); + rndis_signal_disconnect(dev->rndis_config); + + rndis_uninit(dev->rndis_config); + dev->rndis = 0; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + static int is_eth_addr_valid(char *str) { if (strlen(str) == 17) { @@ -1579,8 +1995,9 @@ static int get_ether_addr(const char *str, u8 *dev_addr) static int eth_bind(struct usb_gadget *gadget) { struct eth_dev *dev = &l_ethdev; - u8 cdc = 1, zlp = 1; + u8 cdc = 1, zlp = 1, rndis = 1; struct usb_ep *in_ep, *out_ep, *status_ep = NULL; + int status = -ENOMEM; int gcnum; u8 tmp[7]; @@ -1588,6 +2005,9 @@ static int eth_bind(struct usb_gadget *gadget) #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. @@ -1602,6 +2022,7 @@ static int eth_bind(struct usb_gadget *gadget) } else if (gadget_is_sh(gadget)) { /* sh doesn't support multiple interfaces or configs */ cdc = 0; + rndis = 0; } else if (gadget_is_sa1100(gadget)) { /* hardware can't write zlps */ zlp = 0; @@ -1627,22 +2048,45 @@ static int eth_bind(struct usb_gadget *gadget) } /* - * CDC subset ... recognized by Linux since 2.4.10, but Windows - * drivers aren't widely available. (That may be improved by - * supporting one submode of the "SAFE" variant of MDLM.) + * 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 (!cdc) { + if (rndis) { +#if defined(CONFIG_USB_RNDIS_VENDOR_ID) && defined(CONFIG_USB_RNDIS_PRODUCT_ID) device_desc.idVendor = - __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); + __constant_cpu_to_le16(CONFIG_USB_RNDIS_VENDOR_ID); device_desc.idProduct = - __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); - } + __constant_cpu_to_le16(CONFIG_USB_RNDIS_PRODUCT_ID); +#else + device_desc.idVendor = + __constant_cpu_to_le16(RNDIS_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); +#endif + sprintf(product_desc, "RNDIS/%s", driver_desc); - /* support optional vendor/distro customization */ + /* + * CDC subset ... recognized by Linux since 2.4.10, but Windows + * drivers aren't widely available. (That may be improved by + * supporting one submode of the "SAFE" variant of MDLM.) + */ + } else { #if defined(CONFIG_USB_CDC_VENDOR_ID) && defined(CONFIG_USB_CDC_PRODUCT_ID) - device_desc.idVendor = cpu_to_le16(CONFIG_USB_CDC_VENDOR_ID); - device_desc.idProduct = cpu_to_le16(CONFIG_USB_CDC_PRODUCT_ID); + device_desc.idVendor = cpu_to_le16(CONFIG_USB_CDC_VENDOR_ID); + device_desc.idProduct = cpu_to_le16(CONFIG_USB_CDC_PRODUCT_ID); +#else + if (!cdc) { + device_desc.idVendor = + __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); + } #endif + } + /* support optional vendor/distro customization */ if (bcdDevice) device_desc.bcdDevice = cpu_to_le16(bcdDevice); if (iManufacturer) @@ -1670,18 +2114,23 @@ autoconf_fail: goto autoconf_fail; out_ep->driver_data = out_ep; /* claim */ -#if defined(DEV_CONFIG_CDC) +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) /* * 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) { status_ep = usb_ep_autoconfig(gadget, &fs_status_desc); if (status_ep) { status_ep->driver_data = status_ep; /* claim */ + } else if (rndis) { + error("can't run RNDIS on %s", gadget->name); + return -ENODEV; +#ifdef DEV_CONFIG_CDC } else if (cdc) { control_intf.bNumEndpoints = 0; /* FIXME remove endpoint from descriptor list */ +#endif } } #endif @@ -1702,8 +2151,14 @@ autoconf_fail: device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; usb_gadget_set_selfpowered(gadget); + /* For now RNDIS is always a second config */ + if (rndis) + device_desc.bNumConfigurations = 2; + if (gadget_is_dualspeed(gadget)) { - 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 ... */ @@ -1714,7 +2169,7 @@ autoconf_fail: fs_source_desc.bEndpointAddress; hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; -#if defined(DEV_CONFIG_CDC) +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) if (status_ep) hs_status_desc.bEndpointAddress = fs_status_desc.bEndpointAddress; @@ -1725,8 +2180,14 @@ autoconf_fail: otg_descriptor.bmAttributes |= USB_OTG_HNP, eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; eth_config.bMaxPower = 4; +#ifdef CONFIG_USB_ETH_RNDIS + rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + rndis_config.bMaxPower = 4; +#endif } + + /* network device setup */ dev->net = &l_netdev; dev->cdc = cdc; @@ -1738,7 +2199,7 @@ autoconf_fail: /* * Module params for these addresses should come from ID proms. - * The host side address is used with CDC, and commonly + * The host side address is used with CDC and RNDIS, and commonly * ends up in a persistent config database. It's not clear if * host side code for the SAFE thing cares -- its original BLAN * thing didn't, Sharp never assigned those addresses on Zaurii. @@ -1755,21 +2216,12 @@ autoconf_fail: dev->host_mac[2], dev->host_mac[3], dev->host_mac[4], dev->host_mac[5]); - printf("using %s, OUT %s IN %s%s%s\n", gadget->name, - out_ep->name, in_ep->name, - status_ep ? " STATUS " : "", - status_ep ? status_ep->name : "" - ); - printf("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", - dev->net->enetaddr[0], dev->net->enetaddr[1], - dev->net->enetaddr[2], dev->net->enetaddr[3], - dev->net->enetaddr[4], dev->net->enetaddr[5]); - - if (cdc) { - printf("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]); + if (rndis) { + status = rndis_init(); + if (status < 0) { + error("can't init RNDIS, %d", status); + goto fail; + } } /* @@ -1786,7 +2238,7 @@ autoconf_fail: dev->req->complete = eth_setup_complete; /* ... and maybe likewise for status transfer */ -#if defined(DEV_CONFIG_CDC) +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) if (dev->status_ep) { dev->stat_req = usb_ep_alloc_request(dev->status_ep, GFP_KERNEL); @@ -1810,14 +2262,60 @@ autoconf_fail: * - iff DATA transfer is active, carrier is "on" * - tx queueing enabled if open *and* carrier is "on" */ + + printf("using %s, OUT %s IN %s%s%s\n", gadget->name, + out_ep->name, in_ep->name, + status_ep ? " STATUS " : "", + status_ep ? status_ep->name : "" + ); + printf("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->net->enetaddr[0], dev->net->enetaddr[1], + dev->net->enetaddr[2], dev->net->enetaddr[3], + dev->net->enetaddr[4], dev->net->enetaddr[5]); + + if (cdc || rndis) + printf("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]); + + 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: + eth_unbind(gadget); + debug("RNDIS setup failed\n"); + status = -ENODEV; + goto fail; + } + + /* 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->mtu, + &dev->stats, &dev->cdc_filter)) + 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; + printf("RNDIS ready\n"); + } return 0; fail: - error("%s failed", __func__); + error("%s failed, status = %d", __func__, status); eth_unbind(gadget); - return -ENOMEM; + return status; } +/*-------------------------------------------------------------------------*/ + static int usb_eth_init(struct eth_device *netdev, bd_t *bd) { struct eth_dev *dev = &l_ethdev; @@ -1890,6 +2388,7 @@ static int usb_eth_send(struct eth_device *netdev, volatile void *packet, int length) { int retval; + void *rndis_pkt = NULL; struct eth_dev *dev = &l_ethdev; struct usb_request *req = dev->tx_req; unsigned long ts; @@ -1897,6 +2396,20 @@ static int usb_eth_send(struct eth_device *netdev, debug("%s:...\n", __func__); + /* new buffer is needed to include RNDIS header */ + if (rndis_active(dev)) { + rndis_pkt = malloc(length + + sizeof(struct rndis_packet_msg_type)); + if (!rndis_pkt) { + error("No memory to alloc RNDIS packet"); + goto drop; + } + rndis_add_hdr(rndis_pkt, length); + memcpy(rndis_pkt + sizeof(struct rndis_packet_msg_type), + (void *)packet, length); + packet = rndis_pkt; + length += sizeof(struct rndis_packet_msg_type); + } req->buf = (void *)packet; req->context = NULL; req->complete = tx_complete; @@ -1932,8 +2445,13 @@ static int usb_eth_send(struct eth_device *netdev, } usb_gadget_handle_interrupts(); } + if (rndis_pkt) + free(rndis_pkt); return 0; +drop: + dev->stats.tx_dropped++; + return -ENOMEM; } static int usb_eth_recv(struct eth_device *netdev) @@ -1968,6 +2486,8 @@ void usb_eth_halt(struct eth_device *netdev) if (!dev->gadget) return; + eth_stop(dev); + usb_gadget_disconnect(dev->gadget); /* Clear pending interrupt */ |