rendered paste body--- 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 <oleavr@gmail.com>
*
* 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 <linux/config.h>
#include <linux/module.h>
+#include <linux/version.h>
#include <linux/sched.h>
#include <linux/init.h>
-#include <linux/kthread.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
@@ -33,29 +31,15 @@
#include <linux/mii.h>
#include <linux/usb.h>
-#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
- #include <linux/usb/cdc.h>
+#include <linux/usb/cdc.h>
#else
- #include <linux/usb_cdc.h>
+#include <linux/usb_cdc.h>
#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