--- old_rndis_host.c 2007-04-22 18:35:48.000000000 +0100 +++ rndis_host.c 2007-04-22 18:36:59.000000000 +0100 @@ -1,7 +1,6 @@ /* * Host Side support for RNDIS Networking Links * Copyright (C) 2005 by David Brownell - * Copyright (C) 2006 Ole Andre Vadla Ravnaas * * 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 @@ -18,14 +17,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define DEBUG // error path messages, extra info -#define VERBOSE // more; success messages +// #define DEBUG // error path messages, extra info +// #define VERBOSE // more; success messages -#include #include +#include #include #include -#include #include #include #include @@ -33,29 +31,15 @@ #include #include -#include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) - #include +#include #else - #include +#include #endif -#include "ndis.h" #include "usbnet.h" -#define DRIVER_REVISION "r17" - - -static int use_status = 1; -module_param(use_status, int, 0); -MODULE_PARM_DESC(use_status, "Use the status endpoint"); - -static int send_keepalives = 1; -module_param(send_keepalives, int, 0); -MODULE_PARM_DESC(send_keepalives, "Send keepalives to make broken devices happy"); - - /* * RNDIS is NDIS remoted over USB. It's a MSFT variant of CDC ACM ... of * course ACM was intended for modems, not Ethernet links! USB's standard @@ -71,6 +55,8 @@ * - In some cases, MS-Windows will emit undocumented requests; this * matters more to peripheral implementations than host ones. * + * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync". + * * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and * currently rare) "Ethernet Emulation Model" (EEM). @@ -81,6 +67,11 @@ * - control-out: SEND_ENCAPSULATED * - interrupt-in: RESPONSE_AVAILABLE * - control-in: GET_ENCAPSULATED + * + * We'll try to ignore the RESPONSE_AVAILABLE notifications. + * + * REVISIT some RNDIS implementations seem to have curious issues still + * to be resolved. */ struct rndis_msg_hdr { __le32 msg_type; /* RNDIS_MSG_* */ @@ -91,16 +82,18 @@ // ... and more } __attribute__ ((packed)); -#define CONTROL_BUFFER_SIZE 1025 +/* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */ +#define CONTROL_BUFFER_SIZE 1025 -/* RNDIS defines this (absurdly huge) control timeout */ -#define RNDIS_CONTROL_TIMEOUT_MS (10 * 1000) +/* RNDIS defines an (absurdly huge) 10 second control timeout, + * but ActiveSync seems to use a more usual 5 second timeout + * (which matches the USB 2.0 spec). + */ +#define RNDIS_CONTROL_TIMEOUT_MS (5 * 1000) #define ccpu2 __constant_cpu_to_le32 -#define RESPONSE_AVAILABLE ccpu2(0x00000001) - #define RNDIS_MSG_COMPLETION ccpu2(0x80000000) /* codes for "msg_type" field of rndis messages; @@ -262,15 +255,13 @@ __le32 status; } __attribute__ ((packed)); -/* internal state */ -struct rndis_context { - int running; - int status_initialized; - unsigned packet_align; - struct completion compl; - struct task_struct *thread_task; - struct completion thread_compl; -}; +/* NOTE: about 30 OIDs are "mandatory" for peripherals to support ... and + * there are gobs more that may optionally be supported. We'll avoid as much + * of that mess as possible. + */ +#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101) +#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106) +#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e) /* * RNDIS notifications from device: command completion; "reverse" @@ -278,27 +269,10 @@ */ static void rndis_status(struct usbnet *dev, struct urb *urb) { - struct cdc_state *info = (void *) &dev->data; - struct rndis_context *ctx = info->context; - - if (urb->actual_length == 8) { - u32 response, reserved; - - response = *((u32 *) urb->transfer_buffer); - reserved = *((u32 *) urb->transfer_buffer + 4); - - if (response == RESPONSE_AVAILABLE) { - if (use_status) - complete(&ctx->compl); - } else { - dev_dbg(&info->control->dev, - "response = 0x%08x, reserved = 0x%08x\n", - le32_to_cpu(response), le32_to_cpu(reserved)); - } - } else { - dev_dbg(&info->control->dev, - "unexpected actual_length %d\n", urb->actual_length); - } + devdbg(dev, "rndis status urb, len %d stat %d", + urb->actual_length, urb->status); + // FIXME for keepalives, respond immediately (asynchronously) + // if not an RNDIS status, do like cdc_status(dev,urb) does } /* @@ -311,11 +285,10 @@ * Call context is likely probe(), before interface name is known, * which is why we won't try to use it in the diagnostics. */ -static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, - gfp_t flags) +static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) { struct cdc_state *info = (void *) &dev->data; - struct rndis_context *ctx = info->context; + int master_ifnum; int retval; unsigned count; __le32 rsp; @@ -324,63 +297,33 @@ /* REVISIT when this gets called from contexts other than probe() or * disconnect(): either serialize, or dispatch responses on xid */ - - /* Issue the request */ + + /* Issue the request; xid is unique, don't bother byteswapping it */ if (likely(buf->msg_type != RNDIS_MSG_HALT && buf->msg_type != RNDIS_MSG_RESET)) { xid = dev->xid++; if (!xid) xid = dev->xid++; - buf->request_id = ccpu2(xid); + buf->request_id = (__force __le32) xid; } + master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber; retval = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), USB_CDC_SEND_ENCAPSULATED_COMMAND, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, info->bMasterInterface0, + 0, master_ifnum, buf, le32_to_cpu(buf->msg_len), RNDIS_CONTROL_TIMEOUT_MS); if (unlikely(retval < 0 || xid == 0)) return retval; - /* initialize status endpoint if not already done */ - if (unlikely(!ctx->status_initialized)) { - retval = usbnet_init_status(dev, info->control, flags); - if (unlikely(retval < 0)) - return retval; - - ctx->status_initialized = 1; - - /* poke status endpoint */ - memset(dev->interrupt->transfer_buffer, 0, - dev->interrupt->transfer_buffer_length); - retval = usb_submit_urb(dev->interrupt, flags); - if (unlikely(retval != 0)) { - dev_err(&info->control->dev, "failed to submit interrupt urb: %d\n", retval); - return retval; - } - } - - if (use_status) { - /* wait for RESPONSE_AVAILABLE notification */ - if (!wait_for_completion_timeout(&ctx->compl, - msecs_to_jiffies(RNDIS_CONTROL_TIMEOUT_MS))) { - dev_dbg(&info->control->dev, "rndis response timeout " - "waiting for status ep\n"); - return -ETIMEDOUT; - } - } - -#if 0 - /* do CLEAR_FEATURE like the Windows driver does */ - retval = usb_clear_halt(dev->udev, usb_pipeendpoint(usb_rcvctrlpipe(dev->udev, 0))); - if (retval) { - dev_err(&info->control->dev, "failed to perform CLEAR_FEATURE on ctrl ep\n"); - return retval; - } -#endif + // FIXME Seems like some devices discard responses when + // we time out and cancel our "get response" requests... + // so, this is fragile. Probably need to poll for status. - /* get the response */ + /* ignore status endpoint, just poll the control channel; + * the request probably completed immediately + */ rsp = buf->msg_type | RNDIS_MSG_COMPLETION; for (count = 0; count < 10; count++) { memset(buf, 0, CONTROL_BUFFER_SIZE); @@ -388,12 +331,12 @@ usb_rcvctrlpipe(dev->udev, 0), USB_CDC_GET_ENCAPSULATED_RESPONSE, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, info->bMasterInterface0, + 0, master_ifnum, buf, CONTROL_BUFFER_SIZE, RNDIS_CONTROL_TIMEOUT_MS); if (likely(retval >= 8)) { msg_len = le32_to_cpu(buf->msg_len); - request_id = le32_to_cpu(buf->request_id); + request_id = (__force u32) buf->request_id; if (likely(buf->msg_type == rsp)) { if (likely(request_id == xid)) { if (unlikely(rsp == RNDIS_MSG_RESET_C)) @@ -427,7 +370,7 @@ usb_sndctrlpipe(dev->udev, 0), USB_CDC_SEND_ENCAPSULATED_COMMAND, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, info->bMasterInterface0, + 0, master_ifnum, msg, sizeof *msg, RNDIS_CONTROL_TIMEOUT_MS); if (unlikely(retval < 0)) @@ -452,164 +395,6 @@ return -ETIMEDOUT; } -static const char *oid_to_string(u32 oid) -{ - switch (oid) { - case OID_GEN_SUPPORTED_LIST: - return "OID_GEN_SUPPORTED_LIST"; - case OID_GEN_HARDWARE_STATUS: - return "OID_GEN_HARDWARE_STATUS"; - case OID_GEN_MEDIA_SUPPORTED: - return "OID_GEN_MEDIA_SUPPORTED"; - case OID_GEN_MEDIA_IN_USE: - return "OID_GEN_MEDIA_IN_USE"; - case OID_GEN_MAXIMUM_LOOKAHEAD: - return "OID_GEN_MAXIMUM_LOOKAHEAD"; - case OID_GEN_MAXIMUM_FRAME_SIZE: - return "OID_GEN_MAXIMUM_FRAME_SIZE"; - case OID_GEN_LINK_SPEED: - return "OID_GEN_LINK_SPEED"; - case OID_GEN_TRANSMIT_BUFFER_SPACE: - return "OID_GEN_TRANSMIT_BUFFER_SPACE"; - case OID_GEN_RECEIVE_BUFFER_SPACE: - return "OID_GEN_RECEIVE_BUFFER_SPACE"; - case OID_GEN_TRANSMIT_BLOCK_SIZE: - return "OID_GEN_TRANSMIT_BLOCK_SIZE"; - case OID_GEN_RECEIVE_BLOCK_SIZE: - return "OID_GEN_RECEIVE_BLOCK_SIZE"; - case OID_GEN_VENDOR_ID: - return "OID_GEN_VENDOR_ID"; - case OID_GEN_VENDOR_DESCRIPTION: - return "OID_GEN_VENDOR_DESCRIPTION"; - case OID_GEN_CURRENT_PACKET_FILTER: - return "OID_GEN_CURRENT_PACKET_FILTER"; - case OID_GEN_CURRENT_LOOKAHEAD: - return "OID_GEN_CURRENT_LOOKAHEAD"; - case OID_GEN_DRIVER_VERSION: - return "OID_GEN_DRIVER_VERSION"; - case OID_GEN_MAXIMUM_TOTAL_SIZE: - return "OID_GEN_MAXIMUM_TOTAL_SIZE"; - case OID_GEN_PROTOCOL_OPTIONS: - return "OID_GEN_PROTOCOL_OPTIONS"; - case OID_GEN_MAC_OPTIONS: - return "OID_GEN_MAC_OPTIONS"; - case OID_GEN_MEDIA_CONNECT_STATUS: - return "OID_GEN_MEDIA_CONNECT_STATUS"; - case OID_GEN_MAXIMUM_SEND_PACKETS: - return "OID_GEN_MAXIMUM_SEND_PACKETS"; - case OID_GEN_VENDOR_DRIVER_VERSION: - return "OID_GEN_VENDOR_DRIVER_VERSION"; - case OID_GEN_SUPPORTED_GUIDS: - return "OID_GEN_SUPPORTED_GUIDS"; - case OID_GEN_NETWORK_LAYER_ADDRESSES: - return "OID_GEN_NETWORK_LAYER_ADDRESSES"; - case OID_GEN_TRANSPORT_HEADER_OFFSET: - return "OID_GEN_TRANSPORT_HEADER_OFFSET"; - case OID_GEN_MACHINE_NAME: - return "OID_GEN_MACHINE_NAME"; - case OID_GEN_RNDIS_CONFIG_PARAMETER: - return "OID_GEN_RNDIS_CONFIG_PARAMETER"; - case OID_GEN_VLAN_ID: - return "OID_GEN_VLAN_ID"; - case OID_GEN_MEDIA_CAPABILITIES: - return "OID_GEN_MEDIA_CAPABILITIES"; - case OID_GEN_PHYSICAL_MEDIUM: - return "OID_GEN_PHYSICAL_MEDIUM"; - case OID_GEN_XMIT_OK: - return "OID_GEN_XMIT_OK"; - case OID_GEN_RCV_OK: - return "OID_GEN_RCV_OK"; - case OID_GEN_XMIT_ERROR: - return "OID_GEN_XMIT_ERROR"; - case OID_GEN_RCV_ERROR: - return "OID_GEN_RCV_ERROR"; - case OID_GEN_RCV_NO_BUFFER: - return "OID_GEN_RCV_NO_BUFFER"; - case OID_GEN_DIRECTED_BYTES_XMIT: - return "OID_GEN_DIRECTED_BYTES_XMIT"; - case OID_GEN_DIRECTED_FRAMES_XMIT: - return "OID_GEN_DIRECTED_FRAMES_XMIT"; - case OID_GEN_MULTICAST_BYTES_XMIT: - return "OID_GEN_MULTICAST_BYTES_XMIT"; - case OID_GEN_MULTICAST_FRAMES_XMIT: - return "OID_GEN_MULTICAST_FRAMES_XMIT"; - case OID_GEN_BROADCAST_BYTES_XMIT: - return "OID_GEN_BROADCAST_BYTES_XMIT"; - case OID_GEN_BROADCAST_FRAMES_XMIT: - return "OID_GEN_BROADCAST_FRAMES_XMIT"; - case OID_GEN_DIRECTED_BYTES_RCV: - return "OID_GEN_DIRECTED_BYTES_RCV"; - case OID_GEN_DIRECTED_FRAMES_RCV: - return "OID_GEN_DIRECTED_FRAMES_RCV"; - case OID_GEN_MULTICAST_BYTES_RCV: - return "OID_GEN_MULTICAST_BYTES_RCV"; - case OID_GEN_MULTICAST_FRAMES_RCV: - return "OID_GEN_MULTICAST_FRAMES_RCV"; - case OID_GEN_BROADCAST_BYTES_RCV: - return "OID_GEN_BROADCAST_BYTES_RCV"; - case OID_GEN_BROADCAST_FRAMES_RCV: - return "OID_GEN_BROADCAST_FRAMES_RCV"; - case OID_GEN_RCV_CRC_ERROR: - return "OID_GEN_RCV_CRC_ERROR"; - case OID_GEN_TRANSMIT_QUEUE_LENGTH: - return "OID_GEN_TRANSMIT_QUEUE_LENGTH"; - case OID_GEN_GET_TIME_CAPS: - return "OID_GEN_GET_TIME_CAPS"; - case OID_GEN_GET_NETCARD_TIME: - return "OID_GEN_GET_NETCARD_TIME"; - case OID_GEN_NETCARD_LOAD: - return "OID_GEN_NETCARD_LOAD"; - case OID_GEN_DEVICE_PROFILE: - return "OID_GEN_DEVICE_PROFILE"; - case OID_GEN_INIT_TIME_MS: - return "OID_GEN_INIT_TIME_MS"; - case OID_GEN_RESET_COUNTS: - return "OID_GEN_RESET_COUNTS"; - case OID_GEN_MEDIA_SENSE_COUNTS: - return "OID_GEN_MEDIA_SENSE_COUNTS"; - case OID_GEN_FRIENDLY_NAME: - return "OID_GEN_FRIENDLY_NAME"; - case OID_GEN_MINIPORT_INFO: - return "OID_GEN_MINIPORT_INFO"; - case OID_GEN_RESET_VERIFY_PARAMETERS: - return "OID_GEN_RESET_VERIFY_PARAMETERS"; - case NDIS_802_3_MAC_OPTION_PRIORITY: - return "NDIS_802_3_MAC_OPTION_PRIORITY"; - case OID_802_3_PERMANENT_ADDRESS: - return "OID_802_3_PERMANENT_ADDRESS"; - case OID_802_3_CURRENT_ADDRESS: - return "OID_802_3_CURRENT_ADDRESS"; - case OID_802_3_MULTICAST_LIST: - return "OID_802_3_MULTICAST_LIST"; - case OID_802_3_MAXIMUM_LIST_SIZE: - return "OID_802_3_MAXIMUM_LIST_SIZE"; - case OID_802_3_MAC_OPTIONS: - return "OID_802_3_MAC_OPTIONS"; - case OID_802_3_RCV_ERROR_ALIGNMENT: - return "OID_802_3_RCV_ERROR_ALIGNMENT"; - case OID_802_3_XMIT_ONE_COLLISION: - return "OID_802_3_XMIT_ONE_COLLISION"; - case OID_802_3_XMIT_MORE_COLLISIONS: - return "OID_802_3_XMIT_MORE_COLLISIONS"; - case OID_802_3_XMIT_DEFERRED: - return "OID_802_3_XMIT_DEFERRED"; - case OID_802_3_XMIT_MAX_COLLISIONS: - return "OID_802_3_XMIT_MAX_COLLISIONS"; - case OID_802_3_RCV_OVERRUN: - return "OID_802_3_RCV_OVERRUN"; - case OID_802_3_XMIT_UNDERRUN: - return "OID_802_3_XMIT_UNDERRUN"; - case OID_802_3_XMIT_HEARTBEAT_FAILURE: - return "OID_802_3_XMIT_HEARTBEAT_FAILURE"; - case OID_802_3_XMIT_TIMES_CRS_LOST: - return "OID_802_3_XMIT_TIMES_CRS_LOST"; - case OID_802_3_XMIT_LATE_COLLISIONS: - return "OID_802_3_XMIT_LATE_COLLISIONS"; - default: - return NULL; - } -} - /* * rndis_query: * @@ -618,13 +403,13 @@ * to -1 then the reply length is checked against this value, * resulting in an error if it doesn't match. * - * NOTE: Adding a payload of n bytes seems to be the way to tell - * the device the maximum number of bytes expected back, and is - * something added by MSFT as of post-RNDIS 1.0. This is - * completely undocumented and was discovered through reverse - * engineering the driver supplied with ActiveSync 4.1. It seems - * to expose the fact that it is built atop some undocumented subset - * of RNDIS. + * NOTE: Adding a payload exactly or greater than the size of + * the expected response payload is an evident requirement MSFT + * added for ActiveSync. + * The only exception is for OIDs that return a variably sized + * response, in which case no payload should be added. + * This undocumented (and nonsensical) issue was found by sniffing + * protocol requests from the ActiveSync 4.1 Windows driver. */ static int rndis_query(struct usbnet *dev, struct usb_interface *intf, void *buf, u32 oid, u32 in_len, @@ -638,29 +423,20 @@ struct rndis_query_c *get_c; } u; u32 off, len; - const char *oid_str; u.buf = buf; memset(u.get, 0, sizeof *u.get + in_len); u.get->msg_type = RNDIS_MSG_QUERY; u.get->msg_len = ccpu2(sizeof *u.get + in_len); - u.get->oid = ccpu2(oid); + u.get->oid = oid; u.get->len = ccpu2(in_len); u.get->offset = ccpu2(20); - retval = rndis_command(dev, u.header, GFP_KERNEL); + retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { - const char *oid_str = oid_to_string(oid); - - if (oid_str) { - dev_err(&intf->dev, "RNDIS_MSG_QUERY(%s) failed, %d\n", - oid_str, retval); - } else { - dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", - oid, retval); - } - + dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", + oid, retval); return retval; } @@ -678,71 +454,15 @@ return retval; response_error: - oid_str = oid_to_string(oid); - - if (oid_str) { - dev_err(&intf->dev, "RNDIS_MSG_QUERY(%s) failed because of an " - "invalid response - off %d len %d\n", - oid_str, off, len); - } else { - dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed because of an " - "invalid response - off %d len %d\n", - oid, off, len); - } - + dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed because of an " + "invalid response - off %d len %d\n", + oid, off, len); return -EDOM; } -#define MAC_OPTIONS_CHECK_FLAG(flag) \ - if ((tmp & flag) != 0) { \ - dev_dbg(&intf->dev, " " #flag "\n"); \ - tmp ^= flag; \ - } - -static int keepalive_thread(void *data) -{ - struct usbnet *dev = data; - struct cdc_state *info = (void *) &dev->data; - struct rndis_context *ctx = info->context; - union { - void *buf; - struct rndis_msg_hdr *header; - struct rndis_query *get; - struct rndis_query_c *get_c; - } u; - int reply_len, retval; - u32 *dwp; - - dev_dbg(&info->control->dev, "keepalive thread starting\n"); - - u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); - if (!u.buf) - goto out; - - while (ctx->running) { - if (msleep_interruptible(187) != 0) - break; - - /* Get link speed as keepalive ... */ - reply_len = 4; - retval = rndis_query(dev, info->control, u.buf, OID_GEN_LINK_SPEED, 4, - (void **) &dwp, &reply_len); - if (retval < 0) - break; - } - - kfree(u.buf); - -out: - dev_dbg(&info->control->dev, "keepalive thread exiting\n"); - complete_and_exit(&ctx->thread_compl, 0); -} - static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) { - int retval, i; - struct cdc_state *info = (void *) &dev->data; - struct rndis_context *ctx; + int retval; struct net_device *net = dev->net; union { void *buf; @@ -758,262 +478,92 @@ int reply_len; unsigned char *bp; - dev_dbg(&intf->dev, "rndis_host WM5 " DRIVER_REVISION "\n"); - dev_dbg(&intf->dev, "use_status = %d, send_keepalives = %d\n", - use_status, send_keepalives); - /* we can't rely on i/o from stack working, or stack allocation */ u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); if (!u.buf) return -ENOMEM; - - /* claim endpoints and such magic */ retval = usbnet_generic_cdc_bind(dev, intf); if (retval < 0) goto done; - - /* allocate a per-device context */ - ctx = kmalloc(sizeof *ctx, GFP_KERNEL); - if (!ctx) { - retval = -ENOMEM; - goto fail; - } - ctx->running = 1; - ctx->status_initialized = 0; - init_completion(&ctx->compl); - init_completion(&ctx->thread_compl); - info->context = ctx; - - net->hard_header_len += sizeof (struct rndis_data_hdr); - - dev_dbg(&intf->dev, "sending RNDIS_MSG_INIT\n"); - /* initialize; max transfer is 16KB at full speed */ u.init->msg_type = RNDIS_MSG_INIT; u.init->msg_len = ccpu2(sizeof *u.init); u.init->major_version = ccpu2(1); u.init->minor_version = ccpu2(0); - u.init->max_transfer_size = ccpu2(16384); /*ccpu2(net->mtu + net->hard_header_len);*/ - retval = rndis_command(dev, u.header, GFP_KERNEL); + /* max transfer (in spec) is 0x4000 at full speed, but for + * TX we'll stick to one Ethernet packet plus RNDIS framing. + * Don't let userspace change these settings. + */ + net->hard_header_len += sizeof (struct rndis_data_hdr); + dev->hard_mtu = net->mtu + net->hard_header_len; + + dev->rx_urb_size = (dev->udev->speed == USB_SPEED_FULL) ? 16384 : 8192; + u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); + + net->change_mtu = NULL; + + retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { + struct cdc_state *info; + /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); fail: - if (ctx) - kfree(ctx); - - usbnet_cdc_unbind(dev, intf); - + info = (void *) &dev->data; + usb_set_intfdata(info->data, NULL); + usb_driver_release_interface(driver_of(intf), info->data); + info->data = NULL; goto done; } - - dev_dbg(&intf->dev, "REMOTE_NDIS_INITIALIZE_MSG reply:\n" - " Status = %d\n" - " MajorVersion = %d\n" - " MinorVersion = %d\n" - " DeviceFlags = %d\n" - " Medium = %d\n" - " MaxPacketsPerTransfer = %d\n" - " MaxTransferSize = %d\n" - " PacketAlignmentFactor = %d\n" - " AFListOffset = %d\n" - " AFListSize = %d\n", - u.init_c->status, u.init_c->major_version, - u.init_c->minor_version, u.init_c->device_flags, - u.init_c->medium, u.init_c->max_packets_per_message, - u.init_c->max_transfer_size, u.init_c->packet_alignment, - u.init_c->af_list_offset, u.init_c->af_list_size); - - dev->hard_mtu = le32_to_cpu(u.init_c->max_transfer_size); - ctx->packet_align = 1 << le32_to_cpu(u.init_c->packet_alignment); - - dev_dbg(&intf->dev, "hard mtu %u, align %d\n", dev->hard_mtu, - 1 << le32_to_cpu(u.init_c->packet_alignment)); - - /* Get list of supported OIDs. */ - reply_len = -1; - retval = rndis_query(dev, intf, u.buf, OID_GEN_SUPPORTED_LIST, 0, - (void **) &dwp, &reply_len); - if (unlikely(retval < 0)) + tmp = le32_to_cpu(u.init_c->max_transfer_size); + if (tmp < dev->hard_mtu) { + dev_err(&intf->dev, + "dev can't take %u byte packets (max %u)\n", + dev->hard_mtu, tmp); goto fail; - - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_GEN_SUPPORTED_LIST) reply: %d OIDs\n", - reply_len / 4); - - for (i = 0; i < reply_len / 4; i++) { - u32 oid = le32_to_cpu(*dwp); - const char *oid_str; - - oid_str = oid_to_string(oid); - if (oid_str) { - dev_dbg(&intf->dev, " %s\n", oid_str); - } else { - dev_dbg(&intf->dev, " 0x%08x\n", oid); - } - - dwp++; } - /* Get vendor driver version. */ - reply_len = 2; - retval = rndis_query(dev, intf, u.buf, OID_GEN_VENDOR_DRIVER_VERSION, 4, - (void **) &bp, &reply_len); - if (unlikely(retval < 0)) - goto fail; - - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_GEN_VENDOR_DRIVER_VERSION) " - "reply: %d.%d\n", bp[1], bp[0]); - - /* Get maximum lookahead. */ - reply_len = 4; - retval = rndis_query(dev, intf, u.buf, OID_GEN_MAXIMUM_LOOKAHEAD, 4, - (void **) &dwp, &reply_len); - if (unlikely(retval < 0)) - goto fail; - - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_GEN_MAXIMUM_LOOKAHEAD) " - "reply: %d\n", le32_to_cpu(*dwp)); - - /* Get MAC options. */ - reply_len = 4; - retval = rndis_query(dev, intf, u.buf, OID_GEN_MAC_OPTIONS, 4, - (void **) &dwp, &reply_len); - if (unlikely(retval < 0)) - goto fail; - - tmp = le32_to_cpu(*dwp); - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_GEN_MAC_OPTIONS) reply:\n"); - - MAC_OPTIONS_CHECK_FLAG(NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA); - MAC_OPTIONS_CHECK_FLAG(NDIS_MAC_OPTION_RECEIVE_SERIALIZED); - MAC_OPTIONS_CHECK_FLAG(NDIS_MAC_OPTION_TRANSFERS_NOT_PEND); - MAC_OPTIONS_CHECK_FLAG(NDIS_MAC_OPTION_NO_LOOPBACK); - MAC_OPTIONS_CHECK_FLAG(NDIS_MAC_OPTION_FULL_DUPLEX); - MAC_OPTIONS_CHECK_FLAG(NDIS_MAC_OPTION_EOTX_INDICATION); - MAC_OPTIONS_CHECK_FLAG(NDIS_MAC_OPTION_8021P_PRIORITY); - MAC_OPTIONS_CHECK_FLAG(NDIS_MAC_OPTION_RESERVED); - if (tmp != 0) { - dev_dbg(&intf->dev, " ..unknown flag(s) left: 0x%08x\n", tmp); - } - - /* Get maximum send packet descriptors. */ - reply_len = 4; - retval = rndis_query(dev, intf, u.buf, OID_GEN_MAXIMUM_SEND_PACKETS, 4, - (void **) &dwp, &reply_len); - if (unlikely(retval < 0)) - goto fail; - - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_GEN_MAXIMUM_SEND_PACKETS) " - "reply: %d\n", le32_to_cpu(*dwp)); - - /* Get maximum number of 802.3 multicast addresses. */ - reply_len = 4; - retval = rndis_query(dev, intf, u.buf, OID_802_3_MAXIMUM_LIST_SIZE, 4, - (void **) &dwp, &reply_len); - if (unlikely(retval < 0)) - goto fail; - - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_802_3_MAXIMUM_LIST_SIZE) " - "reply: %d\n", le32_to_cpu(*dwp)); + /* REVISIT: peripheral "alignment" request is ignored ... */ + dev_dbg(&intf->dev, + "hard mtu %u (%u from dev), rx buflen %Zu, align %d\n", + dev->hard_mtu, tmp, dev->rx_urb_size, + 1 << le32_to_cpu(u.init_c->packet_alignment)); - /* Get current MAC address. */ + /* Get designated host ethernet address. */ reply_len = ETH_ALEN; - retval = rndis_query(dev, intf, u.buf, OID_802_3_CURRENT_ADDRESS, ETH_ALEN, - (void **) &bp, &reply_len); + retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS, + 48, (void **) &bp, &reply_len); if (unlikely(retval < 0)) goto fail; - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_802_3_CURRENT_ADDRESS) " - "reply: %02x:%02x:%02x:%02x:%02x:%02x\n", - bp[0], bp[1], bp[2], bp[3], bp[4], bp[5]); - - /* Get maximum frame size. */ - reply_len = 4; - retval = rndis_query(dev, intf, u.buf, OID_GEN_MAXIMUM_FRAME_SIZE, 4, - (void **) &dwp, &reply_len); - if (unlikely(retval < 0)) - goto fail; + memcpy(net->dev_addr, bp, ETH_ALEN); - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_GEN_MAXIMUM_FRAME_SIZE) " - "reply: %d\n", le32_to_cpu(*dwp)); - - /* Get link speed. */ + /* Get the preferred MTU. */ reply_len = 4; - retval = rndis_query(dev, intf, u.buf, OID_GEN_LINK_SPEED, 4, - (void **) &dwp, &reply_len); - if (unlikely(retval < 0)) - goto fail; - - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_GEN_LINK_SPEED) reply: %d Mbps\n", - le32_to_cpu(*dwp) / 10000); - - /* Set current lookahead. */ - memset(u.set, 0, sizeof *u.set); - u.set->msg_type = RNDIS_MSG_SET; - u.set->msg_len = ccpu2(sizeof *u.set + 4); - u.set->oid = ccpu2(OID_GEN_CURRENT_LOOKAHEAD); - u.set->len = ccpu2(4); - u.set->offset = ccpu2((sizeof *u.set) - 8); - *(__le32 *)(u.buf + sizeof *u.set) = ccpu2(128); - - retval = rndis_command(dev, u.header, GFP_KERNEL); - if (unlikely(retval < 0)) { - dev_err(&intf->dev, "RNDIS_MSG_SET(OID_GEN_CURRENT_LOOKAHEAD = 128) " - "failed: %d\n", retval); - goto fail; - } - - dev_dbg(&intf->dev, "RNDIS_MSG_SET(OID_GEN_CURRENT_LOOKAHEAD = 128) succeeded\n"); - - /* Get designated host ethernet address. */ - reply_len = ETH_ALEN; - retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS, 48, - (void **) &bp, &reply_len); + retval = rndis_query(dev, intf, u.buf, OID_GEN_MAXIMUM_FRAME_SIZE, 4, + (void **) &dwp, &reply_len); if (unlikely(retval < 0)) goto fail; - - dev_dbg(&intf->dev, "RNDIS_MSG_QUERY(OID_802_3_PERMANENT_ADDRESS) " - "reply: %02x:%02x:%02x:%02x:%02x:%02x\n", - bp[0], bp[1], bp[2], bp[3], bp[4], bp[5]); - memcpy(net->dev_addr, bp, ETH_ALEN); + net->mtu = le32_to_cpu(*dwp); + dev->hard_mtu = net->mtu + net->hard_header_len; /* set a nonzero filter to enable data transfers */ - dev_dbg(&intf->dev, "setting packet filter\n"); - memset(u.set, 0, sizeof *u.set); u.set->msg_type = RNDIS_MSG_SET; u.set->msg_len = ccpu2(4 + sizeof *u.set); - u.set->oid = ccpu2(OID_GEN_CURRENT_PACKET_FILTER); + u.set->oid = OID_GEN_CURRENT_PACKET_FILTER; u.set->len = ccpu2(4); u.set->offset = ccpu2((sizeof *u.set) - 8); - *(__le32 *)(u.buf + sizeof *u.set) = ccpu2( - NDIS_PACKET_TYPE_DIRECTED | - NDIS_PACKET_TYPE_MULTICAST | - NDIS_PACKET_TYPE_BROADCAST); + *(__le32 *)(u.buf + sizeof *u.set) = ccpu2(DEFAULT_FILTER); - retval = rndis_command(dev, u.header, GFP_KERNEL); + retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); goto fail; } - if (send_keepalives) { - /* Start keepalive thread */ - ctx->thread_task = kthread_create(keepalive_thread, dev, - "rndis_host"); - if (IS_ERR(ctx->thread_task)) { - dev_err(&intf->dev, "kthread_create failed\n"); - retval = PTR_ERR(ctx->thread_task); - goto fail; - } - - wake_up_process(ctx->thread_task); - } - - dev_dbg(&intf->dev, "initialization completed successfully\n"); - retval = 0; done: kfree(u.buf); @@ -1023,28 +573,20 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf) { struct rndis_halt *halt; - struct cdc_state *info = (void *) &dev->data; - struct rndis_context *ctx = info->context; - - ctx->running = 0; - if (send_keepalives) { - /* wait for the keepalive thread to exit */ - wait_for_completion(&ctx->thread_compl); - } - /* try to clear any rndis state/activity (no i/o from stack!) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + halt = kcalloc(1, sizeof *halt, GFP_KERNEL); +#else halt = kcalloc(1, sizeof *halt, SLAB_KERNEL); +#endif if (halt) { halt->msg_type = RNDIS_MSG_HALT; halt->msg_len = ccpu2(sizeof *halt); - (void) rndis_command(dev, (void *)halt, SLAB_KERNEL); + (void) rndis_command(dev, (void *)halt); kfree(halt); } - /* free the per-device context */ - kfree(ctx); - return usbnet_cdc_unbind(dev, intf); } @@ -1063,25 +605,16 @@ data_offset = le32_to_cpu(hdr->data_offset); data_len = le32_to_cpu(hdr->data_len); - if (le32_to_cpu(hdr->oob_data_len) > 0) { - devdbg(dev, "wow, got some OOB data\n"); - } - /* don't choke if we see oob, per-packet data, etc */ if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET - || (data_offset + data_len + 8) > msg_len)) { + || skb->len < msg_len + || (data_offset + data_len + 8) > msg_len)) { dev->stats.rx_frame_errors++; - devdbg(dev, "bad rndis message 0x%08x/%d/%d/%d, len %d", + devdbg(dev, "bad rndis message %d/%d/%d/%d, len %d", le32_to_cpu(hdr->msg_type), msg_len, data_offset, data_len, skb->len); return 0; } - - if (skb->len < msg_len) { - /* need more data ... */ - return -1; - } - skb_pull(skb, 8 + data_offset); /* at most one packet left? */ @@ -1094,10 +627,9 @@ skb2 = skb_clone(skb, GFP_ATOMIC); if (unlikely(!skb2)) break; + skb_pull(skb, msg_len - sizeof *hdr); skb_trim(skb2, data_len); usbnet_skb_return(dev, skb2); - - skb_pull(skb, msg_len - sizeof *hdr); } /* caller will usbnet_skb_return the remaining packet */ @@ -1107,19 +639,29 @@ static struct sk_buff * rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { - struct cdc_state *info = (void *) &dev->data; - struct rndis_context *ctx = info->context; struct rndis_data_hdr *hdr; struct sk_buff *skb2; unsigned len = skb->len; - unsigned len_with_hdr = (sizeof *hdr) + len; - unsigned padding; - padding = (len_with_hdr % ctx->packet_align == 0) ? 0 : - ctx->packet_align - (len_with_hdr % ctx->packet_align); - - /* create a new skb, with the correct size and padding. */ - skb2 = skb_copy_expand(skb, sizeof *hdr, padding + 1, flags); + if (likely(!skb_cloned(skb))) { + int room = skb_headroom(skb); + + /* enough head room as-is? */ + if (unlikely((sizeof *hdr) <= room)) + goto fill; + + /* enough room, but needs to be readjusted? */ + room += skb_tailroom(skb); + if (likely((sizeof *hdr) <= room)) { + skb->data = memmove(skb->head + sizeof *hdr, + skb->data, len); + skb->tail = skb->data + len; + goto fill; + } + } + + /* create a new skb, with the correct size (and tailpad) */ + skb2 = skb_copy_expand(skb, sizeof *hdr, 1, flags); dev_kfree_skb_any(skb); if (unlikely(!skb2)) return skb2; @@ -1128,7 +670,7 @@ /* fill out the RNDIS header. we won't bother trying to batch * packets; Linux minimizes wasted bandwidth through tx queues. */ - +fill: hdr = (void *) __skb_push(skb, sizeof *hdr); memset(hdr, 0, sizeof *hdr); hdr->msg_type = RNDIS_MSG_PACKET; @@ -1136,15 +678,14 @@ hdr->data_offset = ccpu2(sizeof(*hdr) - 8); hdr->data_len = cpu_to_le32(len); - __skb_put(skb, padding); - /* FIXME make the last packet always be short ... */ return skb; } + static const struct driver_info rndis_info = { .description = "RNDIS device", - .flags = FLAG_ETHER | FLAG_FRAMING_RN, + .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT, .bind = rndis_bind, .unbind = rndis_unbind, .status = rndis_status, @@ -1157,14 +698,14 @@ /*-------------------------------------------------------------------------*/ -#define WM5_SUB_CLASS 0x01 -#define WM5_PROTOCOL 0x01 - static const struct usb_device_id products [] = { { /* RNDIS is MSFT's un-official variant of CDC ACM */ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), - USB_INTERFACE_INFO(USB_CLASS_MISC, WM5_SUB_CLASS, WM5_PROTOCOL), + .driver_info = (unsigned long) &rndis_info, +}, { + /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ + USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), .driver_info = (unsigned long) &rndis_info, }, { }, // END