From 60743cbbe8b7378473029b9a3656f4840eb777bd Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 22 Feb 2010 12:18:48 +0100 Subject: [PATCH] s5pc110: USB Ethernet Gadget - enabled and fixed RNDIS mode Signed-off-by: Marek Szyprowski --- drivers/usb/gadget/ether.c | 121 ++++++++++++++++++------------ drivers/usb/gadget/rndis.c | 51 +++++++------ drivers/usb/gadget/rndis.h | 2 +- drivers/usb/gadget/s3c_udc_otg.c | 15 +++- drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 30 +++++++- 5 files changed, 143 insertions(+), 76 deletions(-) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 5baaf5a..cb1e62c 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -28,11 +28,14 @@ #include /* #include */ #include +#include #include "gadget_chips.h" -#define USB_NET_NAME "CDC Ethernet" +#define USB_NET_NAME "USB Ethernet" + #define dprintf(x, ...) +//#define dprintf printf #undef INFO #define INFO(x, s...) printf(s) #define dev_err(x, stuff...) printf(stuff) @@ -40,7 +43,6 @@ #define dev_warn dev_err #define DEBUG dev_err #define VDEBUG DEBUG -#define likely #define atomic_read extern struct platform_data brd; #define spin_lock(x) @@ -105,9 +107,6 @@ static const char driver_desc [] = DRIVER_DESC; #include "rndis.h" -/* TODO: move it to some configuration file */ -#define CONFIG_USB_ETH_RNDIS - #ifndef CONFIG_USB_ETH_RNDIS #define rndis_uninit(x) do{}while(0) #define rndis_deregister(c) do{}while(0) @@ -141,8 +140,8 @@ static inline int is_cdc(struct eth_dev *dev) #endif } -#define subset_active(dev) (!is_cdc(dev)) -#define cdc_active(dev) ( is_cdc(dev)) +#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 */ @@ -279,6 +278,14 @@ static char *iSerialNumber; static char dev_addr[18]; static char host_addr[18]; + +void eth_connection_established(void) +{ + l_ethdev.network_started=1; + printf("connection established.\n"); +} + + /*-------------------------------------------------------------------------*/ /* USB DRIVER HOOKUP (to the hardware driver, below us), mostly @@ -921,6 +928,7 @@ 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) || defined(CONFIG_USB_ETH_RNDIS) /* status endpoint used for (optionally) CDC */ if (!subset_active(dev) && dev->status_ep) { @@ -978,6 +986,21 @@ done: dev->out = NULL; } + /* activate non-CDC configs right away + * this isn't strictly according to the RNDIS spec + */ + else if (!cdc_active (dev)) { + /* and open the tx floodgates */ +// atomic_set (&dev->tx_qlen, 0); + dev->tx_qlen = 0; + if (rndis_active(dev)) { + rndis_set_param_medium (dev->rndis_config, + NDIS_MEDIUM_802_3, + BITRATE(dev->gadget)/100); + (void) rndis_signal_connect (dev->rndis_config); + } + } + /* caller is responsible for cleanup on error */ return result; } @@ -1099,9 +1122,7 @@ static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) /* issue the second notification if host reads the first */ if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION && value == 0) { - char *src, *dst = req->buf + sizeof *event; - __le32 data; - int i; + __le32 val, *data = req->buf + sizeof *event; event->bmRequestType = 0xA1; event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; @@ -1110,15 +1131,9 @@ static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) event->wLength = __constant_cpu_to_le16 (8); /* SPEED_CHANGE data is up/down speeds in bits/sec */ - - data = cpu_to_le32 (BITRATE (dev->gadget)); - - src = &data; - for (i=0; i<4; i++) - *dst++ = *src++; - src = &data; - for (i=0; i<4; i++) - *dst++ = *src++; + val = cpu_to_le32 (BITRATE (dev->gadget)); + put_unaligned_le32(val, &data[0]); + put_unaligned_le32(val, &data[1]); req->length = STATUS_BYTECOUNT; value = usb_ep_queue (ep, req, GFP_ATOMIC); @@ -1130,10 +1145,7 @@ static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) event->bNotificationType, value); if (event->bNotificationType== USB_CDC_NOTIFY_SPEED_CHANGE) - { - l_ethdev.network_started=1; - printf("USB network up!\n"); - } + eth_connection_established(); } req->context = NULL; dprintf("done\n"); @@ -1239,11 +1251,6 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) */ dprintf("eth_setup:...\n"); - if (rndis_active(dev)) { - rndis_set_param_medium(dev->rndis_config, - NDIS_MEDIUM_802_3, 0); - (void) rndis_signal_connect (dev->rndis_config); - } req->complete = eth_setup_complete; switch (ctrl->bRequest) { @@ -1542,22 +1549,21 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req) int length; dprintf("%s\n", __func__); - dprintf("rx status %d\n", req->status); + dprintf("rx status %d, len %d\n", req->status, req->actual); packet_received=1; if (!req) return; buf = req->buf; - length = req->length; + length = req->actual; if (rndis_active(dev)) { - if(rndis_rm_hdr(&buf, &length)) + if(rndis_rm_hdr(buf, &length)) dprintf("%s: incorrect RNDIS packet\n", __func__); /* XXX: what to do if RNDIS fails !?!? */ } - req->buf = buf; - req->length = length; + req->actual = length; dev->rx_req=req; } @@ -1934,6 +1940,7 @@ static int eth_bind(struct usb_gadget *gadget) #ifndef CONFIG_USB_ETH_RNDIS rndis = 0; #endif +#if 0 /* Because most host side USB stacks handle CDC Ethernet, that * standard protocol is _strongly_ preferred for interop purposes. * (By everyone except Microsoft.) @@ -1956,7 +1963,7 @@ static int eth_bind(struct usb_gadget *gadget) */ cdc = 0; } - +#endif gcnum = usb_gadget_controller_number (gadget); if (gcnum >= 0) device_desc.bcdDevice = cpu_to_le16 (0x0300 + gcnum); @@ -1971,6 +1978,12 @@ static int eth_bind(struct usb_gadget *gadget) return -ENODEV; } + /* support optional vendor/distro customization */ +#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); +#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 @@ -1984,6 +1997,12 @@ static int eth_bind(struct usb_gadget *gadget) __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); sprintf (product_desc, "RNDIS/%s", driver_desc); +#if defined(CONFIG_USB_RNDIS_VENDOR_ID) && defined(CONFIG_USB_RNDIS_PRODUCT_ID) + device_desc.idVendor = cpu_to_le16(CONFIG_USB_RNDIS_VENDOR_ID); + device_desc.idProduct = cpu_to_le16(CONFIG_USB_RNDIS_PRODUCT_ID); +#endif + + /* 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.) @@ -1995,11 +2014,6 @@ static int eth_bind(struct usb_gadget *gadget) __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); } - /* support optional vendor/distro customization */ -#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); -#endif if (bcdDevice) device_desc.bcdDevice = cpu_to_le16(bcdDevice); if (iManufacturer) @@ -2107,7 +2121,6 @@ autoconf_fail: dev->cdc = cdc; dev->zlp = zlp; - dev->rndis = rndis; dev->in_ep = in_ep; dev->out_ep = out_ep; @@ -2143,6 +2156,7 @@ autoconf_fail: /* use PKTSIZE (or aligned... from u-boot) and set * wMaxSegmentSize accordingly*/ dev->mtu = PKTSIZE_ALIGN; /* RNDIS does not like this, only 1514, TODO*/ + dev->net->mtu = 1500; /* hack for RNDIS OID_GEN_MAXIMUM_FRAME_SIZE */ /* preallocate control message data and buffer */ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); @@ -2204,12 +2218,11 @@ fail0: goto fail; } - /* XXX warkaround for missing stats in eth_dev structure */ - static struct eth_device_stats dev_stats; /* 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, &dev->cdc_filter)) + NULL, &dev->cdc_filter)) goto fail0; if (rndis_set_param_vendor(dev->rndis_config, vendorID, manufacturer)) @@ -2261,6 +2274,9 @@ static int usb_eth_init(struct eth_device* netdev, bd_t* bd) if (getenv("cdc_connect_timeout")) timeout = simple_strtoul(getenv("cdc_connect_timeout"), NULL, 10) * CONFIG_SYS_HZ; + + printf("Initializing USB..."); + ts = get_timer(0); while (!l_ethdev.network_started) { @@ -2270,13 +2286,26 @@ static int usb_eth_init(struct eth_device* netdev, bd_t* bd) printf("The remote end did not respond in time.\n"); goto fail; } + irq_res = usb_gadget_handle_interrupts(); /* hack nice progress display */ if (irq_res && !l_ethdev.network_started) - printf("."); + printf("."); } + if (rndis_active(dev)) { + printf("Waiting for RNDIS link..."); + + /* wait for RNDIS to setup */ + ts = get_timer(0); + while (get_timer(ts) < 250) + { + if (usb_gadget_handle_interrupts()) + printf("."); + } + printf("\n"); + } rx_submit (dev, dev->rx_req, 0); return 0; fail: @@ -2355,7 +2384,7 @@ static int usb_eth_recv(struct eth_device* netdev) if (dev->rx_req) { void *data = dev->rx_req->buf; - int len = dev->rx_req->length; + int len = dev->rx_req->actual; NetReceive(data, len); packet_received=0; rx_submit (dev, dev->rx_req, 0); @@ -2399,7 +2428,7 @@ int usb_eth_initialize(bd_t *bi) int status = 0; struct eth_device *netdev=&l_netdev; - sprintf(netdev->name, "USB CDC Ethernet"); + sprintf(netdev->name, USB_NET_NAME); netdev->init = usb_eth_init; netdev->send = usb_eth_send; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 40bdf90..f7689c9 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -48,7 +48,7 @@ #define ETH_ALEN 6 /* copied from ether.c */ #define netif_running(...) 1 -#define netif_carrier_on(...) do { } while(0) +#define netif_carrier_on(...) eth_connection_established() #define netif_wake_queue(...) do { } while(0) #define netif_carrier_off(...) do { } while(0) #define netif_stop_queue(...) do { } while(0) @@ -63,13 +63,7 @@ * and will be happier if you provide the host_addr module parameter. */ -#if 0 -static int rndis_debug = 0; -module_param (rndis_debug, int, 0); -MODULE_PARM_DESC (rndis_debug, "enable debugging"); -#else #define rndis_debug 0 -#endif #define DBG(str,args...) do { \ if (rndis_debug) \ @@ -830,10 +824,9 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) return -ENOMEM; resp = (rndis_init_cmplt_type *) r->buf; - resp->MessageType = __constant_cpu_to_le32 ( - REMOTE_NDIS_INITIALIZE_CMPLT); + resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_INITIALIZE_CMPLT); resp->MessageLength = __constant_cpu_to_le32 (52); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->RequestID = get_unaligned_le32(&buf->RequestID); /* Still LE in msg buffer */ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION); resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION); @@ -861,7 +854,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) rndis_query_cmplt_type *resp; rndis_resp_t *r; - // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); + // DBG("%s: OID = %08X\n", __func__, get_unaligned_le32(&buf->OID)); if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; /* @@ -877,12 +870,12 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) resp = (rndis_query_cmplt_type *) r->buf; resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->RequestID = get_unaligned_le32(&buf->RequestID); /* Still LE in msg buffer */ - if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID), - le32_to_cpu(buf->InformationBufferOffset) + if (gen_ndis_query_resp (configNr, get_unaligned_le32(&buf->OID), + get_unaligned_le32(&buf->InformationBufferOffset) + 8 + (u8 *) buf, - le32_to_cpu(buf->InformationBufferLength), + get_unaligned_le32(&buf->InformationBufferLength), r)) { /* OID not supported */ resp->Status = __constant_cpu_to_le32 ( @@ -913,8 +906,8 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) return -ENOMEM; resp = (rndis_set_cmplt_type *) r->buf; - BufLength = le32_to_cpu (buf->InformationBufferLength); - BufOffset = le32_to_cpu (buf->InformationBufferOffset); + BufLength = get_unaligned_le32(&buf->InformationBufferLength); + BufOffset = get_unaligned_le32(&buf->InformationBufferOffset); #ifdef VERBOSE DBG("%s: Length: %d\n", __func__, BufLength); @@ -930,8 +923,8 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); resp->MessageLength = __constant_cpu_to_le32 (16); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID), + resp->RequestID = get_unaligned_le32(&buf->RequestID); /* Still LE in msg buffer */ + if (gen_ndis_set_resp (configNr, get_unaligned_le32(&buf->OID), ((u8 *) buf) + 8 + BufOffset, BufLength, r)) resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED); else @@ -983,7 +976,7 @@ static int rndis_keepalive_response (int configNr, resp->MessageType = __constant_cpu_to_le32 ( REMOTE_NDIS_KEEPALIVE_CMPLT); resp->MessageLength = __constant_cpu_to_le32 (16); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->RequestID = get_unaligned_le32(&buf->RequestID); /* Still LE in msg buffer */ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); if (rndis_per_dev_params [configNr].ack) @@ -1192,7 +1185,7 @@ int rndis_set_param_dev (u8 configNr, struct eth_device *dev, u16 *cdc_filter) { DBG("%s:\n", __func__ ); - if (!dev || !stats) return -1; +// if (!dev || !stats) return -1; if (configNr >= RNDIS_MAX_CONFIGS) return -1; rndis_per_dev_params [configNr].dev = dev; @@ -1249,6 +1242,7 @@ rndis_packet_buffer *rndis_packet_create(void *data, int len) memcpy(rndis_buffer.data + header_size, data, len); + DBG("Created RNDIS packet of size %d data offset %d total %d\n", len, header_size, total_size); rndis_buffer.len = total_size; rndis_buffer.in_use = 1; return &rndis_buffer; @@ -1334,11 +1328,15 @@ static rndis_resp_t *rndis_add_response (int configNr, u32 length) return r; } -int rndis_rm_hdr(void **data, int *len) +int rndis_rm_hdr(void *data, int *len) { + int i; + char *dst = data; + char *src = data; /* tmp points to a struct rndis_packet_msg_type */ - __le32 *tmp = (void *) *data; + __le32 *tmp = (void *) data; int DataOffset, DataLength; + DBG("%s, %d\n", __FUNCTION__, __LINE__); /* MessageType, MessageLength */ if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG) @@ -1348,11 +1346,16 @@ int rndis_rm_hdr(void **data, int *len) DataOffset = get_unaligned_le32(tmp++); DataLength = get_unaligned_le32(tmp++); + src += DataOffset + 8; if (DataLength > *len) return -EOVERFLOW; + for (i=0; ibmAttributes << DEPCTL_TYPE_BIT); ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) | (ep->ep.maxpacket << DEPCTL_MPS_BIT); - ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP); + ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK); if (ep_is_in(ep)) { writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num)); @@ -1056,6 +1060,8 @@ static int s3c_udc_clear_feature(struct usb_ep *_ep) return 0; } +#if 0 + /* Set into the test mode for Test Mode set_feature request */ static inline void set_test_mode(void) { @@ -1117,6 +1123,8 @@ static inline void set_test_mode(void) } } +#endif + static int s3c_udc_set_feature(struct usb_ep *_ep) { struct s3c_ep *ep; @@ -1138,12 +1146,12 @@ static int s3c_udc_set_feature(struct usb_ep *_ep) case USB_DEVICE_REMOTE_WAKEUP: DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_REMOTE_WAKEUP\n"); break; - +#if 0 case USB_DEVICE_TEST_MODE: DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_TEST_MODE\n"); set_test_mode(); break; - +#endif case USB_DEVICE_B_HNP_ENABLE: DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); break; @@ -1205,6 +1213,22 @@ static void s3c_ep0_setup(struct s3c_udc *dev) (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", usb_ctrl->bRequest, usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex); +#ifdef DEBUG_S3C_UDC + { + int i, len = sizeof(*usb_ctrl); + char *p = usb_ctrl; + + printf("pkt = "); + for (i=0; ibRequest == GET_MAX_LUN_REQUEST && usb_ctrl->wLength != 1) { DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid wLength = %d, setup returned\n", __func__, usb_ctrl->wLength); -- 2.7.4