All pastes #452993 Raw Edit

Stuff

public text v1 · immutable
·published 2007-04-22 17:47 UTC
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