netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] RFC: Wireless Extensions for rndis_host
@ 2007-12-22 21:51 Bjorge Dijkstra
  2007-12-22 21:51 ` [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing Bjorge Dijkstra
                   ` (4 more replies)
  0 siblings, 5 replies; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless

Hello all,

I have here a patchset that needs some review.
This patchset adds wireless extensions for rndis_host to
enable support for RNDIS based USB wireless LAN adapters
(e.g. Linksys WUSB54GS, Belkin F05D7051).

In this patchset:
 1.  Fix sparse warning: returning void valued expression
 2.  Hardwire CDC descriptors when missing
 3.  Use 1KB buffer in rndis_unbind
 4.  Halt device if rndis_bind fails
 5.  Fix rndis packet filter flags
 6.  Split up rndis_host.c
 7.  Add Wireless Extensions for rndis_host
 8.  Use wlan device name for RNDIS wireless devices

The first five patches are more generic fixes that I think
can be applied as they are. Of these, patch 2 is required
to get these wireless devices working. 
Patches 6-8 introduce some larger changes that may need a
closer look.

All these patches should be applied in order.
The entire series should apply cleanly to current linux
kernel and net-2.6.25 trees.

regards,
Bjorge Dijkstra

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH 1/8] Fix sparse warning: returning void-valued expression
       [not found] ` <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
@ 2007-12-22 21:51   ` Bjorge Dijkstra
       [not found]     ` <11983602952341-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
  2007-12-22 21:51   ` [PATCH 4/8] [PATCH] Halt device if rndis_bind fails Bjorge Dijkstra
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

rndis_unbind and usbnet_cdc_unbind don't return anything.

Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
---
 drivers/net/usb/rndis_host.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 1ebe325..96ef6a9 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -585,7 +585,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
 		kfree(halt);
 	}
 
-	return usbnet_cdc_unbind(dev, intf);
+	usbnet_cdc_unbind(dev, intf);
 }
 
 /*
-- 
1.5.2.5

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing
  2007-12-22 21:51 [PATCH 0/8] RFC: Wireless Extensions for rndis_host Bjorge Dijkstra
@ 2007-12-22 21:51 ` Bjorge Dijkstra
  2007-12-23  0:49   ` David Brownell
  2007-12-22 21:51 ` [PATCH 3/8] [PATCH] Use 1KB buffer in rndis_unbind Bjorge Dijkstra
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless

Just as ActiveSync devices, some regular RNDIS devices also lack
the CDC descriptors (e.g. devices based on BCM4320 WLAN chip).
This patch hardwires the CDC descriptors for all RNDIS style devices
when they are missing.

Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>
---
 drivers/net/usb/cdc_ether.c |   10 +++++-----
 1 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index a42acc3..97c17bb 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -228,15 +228,16 @@ next_desc:
 		buf += buf [0];
 	}
 
-	/* Microsoft ActiveSync based RNDIS devices lack the CDC descriptors,
-	 * so we'll hard-wire the interfaces and not check for descriptors.
+	/* Microsoft ActiveSync based and some regular RNDIS devices lack the
+	 * CDC descriptors, so we'll hard-wire the interfaces and not check
+	 * for descriptors.
 	 */
-	if (is_activesync(&intf->cur_altsetting->desc) && !info->u) {
+	if (rndis && !info->u) {
 		info->control = usb_ifnum_to_if(dev->udev, 0);
 		info->data = usb_ifnum_to_if(dev->udev, 1);
 		if (!info->control || !info->data) {
 			dev_dbg(&intf->dev,
-				"activesync: master #0/%p slave #1/%p\n",
+				"rndis: master #0/%p slave #1/%p\n",
 				info->control,
 				info->data);
 			goto bad_desc;
@@ -316,7 +317,6 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
 }
 EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
 
-\f
 /*-------------------------------------------------------------------------
  *
  * Communications Device Class, Ethernet Control model
-- 
1.5.2.5


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 3/8] [PATCH] Use 1KB buffer in rndis_unbind
  2007-12-22 21:51 [PATCH 0/8] RFC: Wireless Extensions for rndis_host Bjorge Dijkstra
  2007-12-22 21:51 ` [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing Bjorge Dijkstra
@ 2007-12-22 21:51 ` Bjorge Dijkstra
       [not found]   ` <11983602953865-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
       [not found] ` <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless, Jussi Kivilinna

From: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>

rndis_command requires the caller to pass in a buffer of at least 1KB.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>
---
 drivers/net/usb/rndis_host.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 96ef6a9..42b161c 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -577,7 +577,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
 	struct rndis_halt	*halt;
 
 	/* try to clear any rndis state/activity (no i/o from stack!) */
-	halt = kzalloc(sizeof *halt, GFP_KERNEL);
+	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
 	if (halt) {
 		halt->msg_type = RNDIS_MSG_HALT;
 		halt->msg_len = ccpu2(sizeof *halt);
-- 
1.5.2.5


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 4/8] [PATCH] Halt device if rndis_bind fails.
       [not found] ` <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
  2007-12-22 21:51   ` [PATCH 1/8] Fix sparse warning: returning void-valued expression Bjorge Dijkstra
@ 2007-12-22 21:51   ` Bjorge Dijkstra
  2007-12-23  0:54     ` David Brownell
  2007-12-22 21:51   ` [PATCH 5/8] Fix rndis packet filter flags Bjorge Dijkstra
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Jussi Kivilinna

From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>

When bind fails after device was initialized, shutdown device properly
by sending RNDIS_MSG_HALT.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
---
 drivers/net/usb/rndis_host.c |   12 +++++++++---
 1 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 42b161c..c686025 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -467,6 +467,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 		struct rndis_query_c	*get_c;
 		struct rndis_set	*set;
 		struct rndis_set_c	*set_c;
+		struct rndis_halt	*halt;
 	} u;
 	u32			tmp;
 	int			reply_len;
@@ -517,7 +518,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 				"dev can't take %u byte packets (max %u)\n",
 				dev->hard_mtu, tmp);
 			retval = -EINVAL;
-			goto fail_and_release;
+			goto halt_fail_and_release;
 		}
 		dev->hard_mtu = tmp;
 		net->mtu = dev->hard_mtu - net->hard_header_len;
@@ -539,7 +540,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 			48, (void **) &bp, &reply_len);
 	if (unlikely(retval< 0)) {
 		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
-		goto fail_and_release;
+		goto halt_fail_and_release;
 	}
 	memcpy(net->dev_addr, bp, ETH_ALEN);
 
@@ -555,7 +556,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 	retval = rndis_command(dev, u.header);
 	if (unlikely(retval < 0)) {
 		dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
-		goto fail_and_release;
+		goto halt_fail_and_release;
 	}
 
 	retval = 0;
@@ -563,6 +564,11 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 	kfree(u.buf);
 	return retval;
 
+halt_fail_and_release:
+	memset(u.halt, 0, sizeof *u.halt);
+	u.halt->msg_type = RNDIS_MSG_HALT;
+	u.halt->msg_len = ccpu2(sizeof *u.halt);
+	(void) rndis_command(dev, (void *)u.halt);
 fail_and_release:
 	usb_set_intfdata(info->data, NULL);
 	usb_driver_release_interface(driver_of(intf), info->data);
-- 
1.5.2.5

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 5/8] Fix rndis packet filter flags.
       [not found] ` <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
  2007-12-22 21:51   ` [PATCH 1/8] Fix sparse warning: returning void-valued expression Bjorge Dijkstra
  2007-12-22 21:51   ` [PATCH 4/8] [PATCH] Halt device if rndis_bind fails Bjorge Dijkstra
@ 2007-12-22 21:51   ` Bjorge Dijkstra
  2007-12-23  1:06     ` David Brownell
  2007-12-22 21:51   ` [PATCH 8/8] [PATCH] Use wlan device name for RNDIS wireless devices Bjorge Dijkstra
  2007-12-23  1:49   ` [PATCH 0/8] RFC: Wireless Extensions for rndis_host David Brownell
  4 siblings, 1 reply; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Jussi Kivilinna

From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>

RNDIS packet filter flags are not exactly the same as CDC flags
so we cannot reuse them.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
---
 drivers/net/usb/rndis_host.c |   23 ++++++++++++++++++++++-
 1 files changed, 22 insertions(+), 1 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index c686025..3c116f9 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -256,6 +256,27 @@ struct rndis_keepalive_c {	/* IN (optionally OUT) */
 #define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)
 #define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
 
+/* packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */
+#define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
+#define RNDIS_PACKET_TYPE_MULTICAST		ccpu2(0x00000002)
+#define RNDIS_PACKET_TYPE_ALL_MULTICAST		ccpu2(0x00000004)
+#define RNDIS_PACKET_TYPE_BROADCAST		ccpu2(0x00000008)
+#define RNDIS_PACKET_TYPE_SOURCE_ROUTING	ccpu2(0x00000010)
+#define RNDIS_PACKET_TYPE_PROMISCUOUS		ccpu2(0x00000020)
+#define RNDIS_PACKET_TYPE_SMT			ccpu2(0x00000040)
+#define RNDIS_PACKET_TYPE_ALL_LOCAL		ccpu2(0x00000080)
+#define RNDIS_PACKET_TYPE_GROUP			ccpu2(0x00001000)
+#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL	ccpu2(0x00002000)
+#define RNDIS_PACKET_TYPE_FUNCTIONAL		ccpu2(0x00004000)
+#define RNDIS_PACKET_TYPE_MAC_FRAME		ccpu2(0x00008000)
+
+/* default filter used with RNDIS devices */
+#define RNDIS_DEFAULT_FILTER ( \
+	RNDIS_PACKET_TYPE_DIRECTED | \
+	RNDIS_PACKET_TYPE_BROADCAST | \
+	RNDIS_PACKET_TYPE_ALL_MULTICAST | \
+	RNDIS_PACKET_TYPE_PROMISCUOUS )
+
 /*
  * RNDIS notifications from device: command completion; "reverse"
  * keepalives; etc
@@ -551,7 +572,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 	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(DEFAULT_FILTER);
+	*(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
 
 	retval = rndis_command(dev, u.header);
 	if (unlikely(retval < 0)) {
-- 
1.5.2.5

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 6/8] [PATCH] Split up rndis_host.c
  2007-12-22 21:51 [PATCH 0/8] RFC: Wireless Extensions for rndis_host Bjorge Dijkstra
                   ` (2 preceding siblings ...)
       [not found] ` <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
@ 2007-12-22 21:51 ` Bjorge Dijkstra
  2007-12-23  1:17   ` David Brownell
  2007-12-22 21:51 ` [PATCH 7/8] Add Wireless Extensions to rndis_host Bjorge Dijkstra
  4 siblings, 1 reply; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless

Split up rndis_host.c into rndis_host.h and rndis_base.c and
change Makefile accordingly. This is done so we can add extra
source files to the rndis_host module later on.

Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>
---
 drivers/net/usb/Makefile     |    1 +
 drivers/net/usb/rndis_base.c |  548 ++++++++++++++++++++++++++++++
 drivers/net/usb/rndis_host.c |  763 ------------------------------------------
 drivers/net/usb/rndis_host.h |  256 ++++++++++++++
 4 files changed, 805 insertions(+), 763 deletions(-)
 create mode 100644 drivers/net/usb/rndis_base.c
 delete mode 100644 drivers/net/usb/rndis_host.c
 create mode 100644 drivers/net/usb/rndis_host.h

diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 595a539..611ab62 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_USB_NET_GL620A)	+= gl620a.o
 obj-$(CONFIG_USB_NET_NET1080)	+= net1080.o
 obj-$(CONFIG_USB_NET_PLUSB)	+= plusb.o
 obj-$(CONFIG_USB_NET_RNDIS_HOST)	+= rndis_host.o
+rndis_host-objs			+= rndis_base.o
 obj-$(CONFIG_USB_NET_CDC_SUBSET)	+= cdc_subset.o
 obj-$(CONFIG_USB_NET_ZAURUS)	+= zaurus.o
 obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
diff --git a/drivers/net/usb/rndis_base.c b/drivers/net/usb/rndis_base.c
new file mode 100644
index 0000000..f3b0c00
--- /dev/null
+++ b/drivers/net/usb/rndis_base.c
@@ -0,0 +1,548 @@
+/*
+ * Host Side support for RNDIS Networking Links
+ * Copyright (C) 2005 by David Brownell
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// #define	DEBUG			// error path messages, extra info
+// #define	VERBOSE			// more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+
+#include "usbnet.h"
+#include "rndis_host.h"
+
+
+/*
+ * 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
+ * for Ethernet links is "CDC Ethernet", which is significantly simpler.
+ *
+ * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete.  Issues
+ * include:
+ *    - Power management in particular relies on information that's scattered
+ *	through other documentation, and which is incomplete or incorrect even
+ *	there.
+ *    - There are various undocumented protocol requirements, such as the
+ *	need to send unused garbage in control-OUT messages.
+ *    - 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).
+ */
+
+/*
+ * RNDIS notifications from device: command completion; "reverse"
+ * keepalives; etc
+ */
+void rndis_status(struct usbnet *dev, struct urb *urb)
+{
+	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
+}
+EXPORT_SYMBOL_GPL(rndis_status);
+
+/*
+ * RPC done RNDIS-style.  Caller guarantees:
+ * - message is properly byteswapped
+ * - there's no other request pending
+ * - buf can hold up to 1KB response (required by RNDIS spec)
+ * On return, the first few entries are already byteswapped.
+ *
+ * Call context is likely probe(), before interface name is known,
+ * which is why we won't try to use it in the diagnostics.
+ */
+int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
+{
+	struct cdc_state	*info = (void *) &dev->data;
+	int			master_ifnum;
+	int			retval;
+	unsigned		count;
+	__le32			rsp;
+	u32			xid = 0, msg_len, request_id;
+
+	/* REVISIT when this gets called from contexts other than probe() or
+	 * disconnect(): either serialize, or dispatch responses on xid
+	 */
+
+	/* 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 = (__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, master_ifnum,
+		buf, le32_to_cpu(buf->msg_len),
+		RNDIS_CONTROL_TIMEOUT_MS);
+	if (unlikely(retval < 0 || xid == 0))
+		return retval;
+
+	// 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.
+
+	/* 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);
+		retval = usb_control_msg(dev->udev,
+			usb_rcvctrlpipe(dev->udev, 0),
+			USB_CDC_GET_ENCAPSULATED_RESPONSE,
+			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			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 = (__force u32) buf->request_id;
+			if (likely(buf->msg_type == rsp)) {
+				if (likely(request_id == xid)) {
+					if (unlikely(rsp == RNDIS_MSG_RESET_C))
+						return 0;
+					if (likely(RNDIS_STATUS_SUCCESS
+							== buf->status))
+						return 0;
+					dev_dbg(&info->control->dev,
+						"rndis reply status %08x\n",
+						le32_to_cpu(buf->status));
+					return -EL3RST;
+				}
+				dev_dbg(&info->control->dev,
+					"rndis reply id %d expected %d\n",
+					request_id, xid);
+				/* then likely retry */
+			} else switch (buf->msg_type) {
+			case RNDIS_MSG_INDICATE: {	/* fault */
+				// struct rndis_indicate *msg = (void *)buf;
+				dev_info(&info->control->dev,
+					"rndis fault indication\n");
+				}
+				break;
+			case RNDIS_MSG_KEEPALIVE: {	/* ping */
+				struct rndis_keepalive_c *msg = (void *)buf;
+
+				msg->msg_type = RNDIS_MSG_KEEPALIVE_C;
+				msg->msg_len = ccpu2(sizeof *msg);
+				msg->status = RNDIS_STATUS_SUCCESS;
+				retval = usb_control_msg(dev->udev,
+					usb_sndctrlpipe(dev->udev, 0),
+					USB_CDC_SEND_ENCAPSULATED_COMMAND,
+					USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+					0, master_ifnum,
+					msg, sizeof *msg,
+					RNDIS_CONTROL_TIMEOUT_MS);
+				if (unlikely(retval < 0))
+					dev_dbg(&info->control->dev,
+						"rndis keepalive err %d\n",
+						retval);
+				}
+				break;
+			default:
+				dev_dbg(&info->control->dev,
+					"unexpected rndis msg %08x len %d\n",
+					le32_to_cpu(buf->msg_type), msg_len);
+			}
+		} else {
+			/* device probably issued a protocol stall; ignore */
+			dev_dbg(&info->control->dev,
+				"rndis response error, code %d\n", retval);
+		}
+		msleep(2);
+	}
+	dev_dbg(&info->control->dev, "rndis response timeout\n");
+	return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(rndis_command);
+
+/*
+ * rndis_query:
+ *
+ * Performs a query for @oid along with 0 or more bytes of payload as
+ * specified by @in_len. If @reply_len is not set to -1 then the reply
+ * length is checked against this value, resulting in an error if it
+ * doesn't match.
+ *
+ * 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,
+		void **reply, int *reply_len)
+{
+	int retval;
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_query	*get;
+		struct rndis_query_c	*get_c;
+	} u;
+	u32 off, len;
+
+	u.buf = buf;
+
+	memset(u.get, 0, sizeof *u.get + in_len);
+	u.get->msg_type = RNDIS_MSG_QUERY;
+	u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
+	u.get->oid = oid;
+	u.get->len = cpu_to_le32(in_len);
+	u.get->offset = ccpu2(20);
+
+	retval = rndis_command(dev, u.header);
+	if (unlikely(retval < 0)) {
+		dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
+				oid, retval);
+		return retval;
+	}
+
+	off = le32_to_cpu(u.get_c->offset);
+	len = le32_to_cpu(u.get_c->len);
+	if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
+		goto response_error;
+
+	if (*reply_len != -1 && len != *reply_len)
+		goto response_error;
+
+	*reply = (unsigned char *) &u.get_c->request_id + off;
+	*reply_len = len;
+
+	return retval;
+
+response_error:
+	dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
+			"invalid response - off %d len %d\n",
+		oid, off, len);
+	return -EDOM;
+}
+
+int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int			retval;
+	struct net_device	*net = dev->net;
+	struct cdc_state	*info = (void *) &dev->data;
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_init	*init;
+		struct rndis_init_c	*init_c;
+		struct rndis_query	*get;
+		struct rndis_query_c	*get_c;
+		struct rndis_set	*set;
+		struct rndis_set_c	*set_c;
+		struct rndis_halt	*halt;
+	} u;
+	u32			tmp;
+	int			reply_len;
+	unsigned char		*bp;
+
+	/* 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;
+	retval = usbnet_generic_cdc_bind(dev, intf);
+	if (retval < 0)
+		goto fail;
+
+	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);
+
+	/* max transfer (in spec) is 0x4000 at full speed, but for
+	 * TX we'll stick to one Ethernet packet plus RNDIS framing.
+	 * For RX we handle drivers that zero-pad to end-of-packet.
+	 * Don't let userspace change these settings.
+	 *
+	 * NOTE: there still seems to be wierdness here, as if we need
+	 * to do some more things to make sure WinCE targets accept this.
+	 * They default to jumbograms of 8KB or 16KB, which is absurd
+	 * for such low data rates and which is also more than Linux
+	 * can usually expect to allocate for SKB data...
+	 */
+	net->hard_header_len += sizeof (struct rndis_data_hdr);
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1);
+	dev->rx_urb_size &= ~(dev->maxpacket - 1);
+	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)) {
+		/* it might not even be an RNDIS device!! */
+		dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
+		goto fail_and_release;
+	}
+	tmp = le32_to_cpu(u.init_c->max_transfer_size);
+	if (tmp < dev->hard_mtu) {
+		if (tmp <= net->hard_header_len) {
+			dev_err(&intf->dev,
+				"dev can't take %u byte packets (max %u)\n",
+				dev->hard_mtu, tmp);
+			retval = -EINVAL;
+			goto halt_fail_and_release;
+		}
+		dev->hard_mtu = tmp;
+		net->mtu = dev->hard_mtu - net->hard_header_len;
+		dev_warn(&intf->dev,
+			 "dev can't take %u byte packets (max %u), "
+			 "adjusting MTU to %u\n",
+			 dev->hard_mtu, tmp, net->mtu);
+	}
+
+	/* 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 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);
+	if (unlikely(retval< 0)) {
+		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
+		goto halt_fail_and_release;
+	}
+	memcpy(net->dev_addr, bp, ETH_ALEN);
+
+	/* set a nonzero filter to enable data transfers */
+	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 = OID_GEN_CURRENT_PACKET_FILTER;
+	u.set->len = ccpu2(4);
+	u.set->offset = ccpu2((sizeof *u.set) - 8);
+	*(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
+
+	retval = rndis_command(dev, u.header);
+	if (unlikely(retval < 0)) {
+		dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
+		goto halt_fail_and_release;
+	}
+
+	retval = 0;
+
+	kfree(u.buf);
+	return retval;
+
+halt_fail_and_release:
+	memset(u.halt, 0, sizeof *u.halt);
+	u.halt->msg_type = RNDIS_MSG_HALT;
+	u.halt->msg_len = ccpu2(sizeof *u.halt);
+	(void) rndis_command(dev, (void *)u.halt);
+fail_and_release:
+	usb_set_intfdata(info->data, NULL);
+	usb_driver_release_interface(driver_of(intf), info->data);
+	info->data = NULL;
+fail:
+	kfree(u.buf);
+	return retval;
+}
+EXPORT_SYMBOL_GPL(rndis_bind);
+
+void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct rndis_halt	*halt;
+
+	/* try to clear any rndis state/activity (no i/o from stack!) */
+	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
+	if (halt) {
+		halt->msg_type = RNDIS_MSG_HALT;
+		halt->msg_len = ccpu2(sizeof *halt);
+		(void) rndis_command(dev, (void *)halt);
+		kfree(halt);
+	}
+
+	usbnet_cdc_unbind(dev, intf);
+}
+EXPORT_SYMBOL_GPL(rndis_unbind);
+
+/*
+ * DATA -- host must not write zlps
+ */
+int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	/* peripheral may have batched packets to us... */
+	while (likely(skb->len)) {
+		struct rndis_data_hdr	*hdr = (void *)skb->data;
+		struct sk_buff		*skb2;
+		u32			msg_len, data_offset, data_len;
+
+		msg_len = le32_to_cpu(hdr->msg_len);
+		data_offset = le32_to_cpu(hdr->data_offset);
+		data_len = le32_to_cpu(hdr->data_len);
+
+		/* don't choke if we see oob, per-packet data, etc */
+		if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET
+				|| skb->len < msg_len
+				|| (data_offset + data_len + 8) > msg_len)) {
+			dev->stats.rx_frame_errors++;
+			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;
+		}
+		skb_pull(skb, 8 + data_offset);
+
+		/* at most one packet left? */
+		if (likely((data_len - skb->len) <= sizeof *hdr)) {
+			skb_trim(skb, data_len);
+			break;
+		}
+
+		/* try to return all the packets in the batch */
+		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);
+	}
+
+	/* caller will usbnet_skb_return the remaining packet */
+	return 1;
+}
+EXPORT_SYMBOL_GPL(rndis_rx_fixup);
+
+struct sk_buff *
+rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	struct rndis_data_hdr	*hdr;
+	struct sk_buff		*skb2;
+	unsigned		len = skb->len;
+
+	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_set_tail_pointer(skb, 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;
+	skb = skb2;
+
+	/* 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;
+	hdr->msg_len = cpu_to_le32(skb->len);
+	hdr->data_offset = ccpu2(sizeof(*hdr) - 8);
+	hdr->data_len = cpu_to_le32(len);
+
+	/* FIXME make the last packet always be short ... */
+	return skb;
+}
+EXPORT_SYMBOL_GPL(rndis_tx_fixup);
+
+
+static const struct driver_info	rndis_info = {
+	.description =	"RNDIS device",
+	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.bind =		rndis_bind,
+	.unbind =	rndis_unbind,
+	.status =	rndis_status,
+	.rx_fixup =	rndis_rx_fixup,
+	.tx_fixup =	rndis_tx_fixup,
+};
+
+#undef ccpu2
+
+
+/*-------------------------------------------------------------------------*/
+
+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),
+	.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
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver rndis_driver = {
+	.name =		"rndis_host",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.disconnect =	usbnet_disconnect,
+	.suspend =	usbnet_suspend,
+	.resume =	usbnet_resume,
+};
+
+static int __init rndis_init(void)
+{
+	return usb_register(&rndis_driver);
+}
+module_init(rndis_init);
+
+static void __exit rndis_exit(void)
+{
+	usb_deregister(&rndis_driver);
+}
+module_exit(rndis_exit);
+
+MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("USB Host side RNDIS driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
deleted file mode 100644
index 3c116f9..0000000
--- a/drivers/net/usb/rndis_host.c
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * Host Side support for RNDIS Networking Links
- * Copyright (C) 2005 by David Brownell
- *
- * 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-// #define	DEBUG			// error path messages, extra info
-// #define	VERBOSE			// more; success messages
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/workqueue.h>
-#include <linux/mii.h>
-#include <linux/usb.h>
-#include <linux/usb/cdc.h>
-
-#include "usbnet.h"
-
-
-/*
- * 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
- * for Ethernet links is "CDC Ethernet", which is significantly simpler.
- *
- * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete.  Issues
- * include:
- *    - Power management in particular relies on information that's scattered
- *	through other documentation, and which is incomplete or incorrect even
- *	there.
- *    - There are various undocumented protocol requirements, such as the
- *	need to send unused garbage in control-OUT messages.
- *    - 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).
- */
-
-/*
- * CONTROL uses CDC "encapsulated commands" with funky notifications.
- *  - 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_* */
-	__le32	msg_len;
-	// followed by data that varies between messages
-	__le32	request_id;
-	__le32	status;
-	// ... and more
-} __attribute__ ((packed));
-
-/* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */
-#define	CONTROL_BUFFER_SIZE		1025
-
-/* 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 RNDIS_MSG_COMPLETION	ccpu2(0x80000000)
-
-/* codes for "msg_type" field of rndis messages;
- * only the data channel uses packet messages (maybe batched);
- * everything else goes on the control channel.
- */
-#define RNDIS_MSG_PACKET	ccpu2(0x00000001)	/* 1-N packets */
-#define RNDIS_MSG_INIT		ccpu2(0x00000002)
-#define RNDIS_MSG_INIT_C	(RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION)
-#define RNDIS_MSG_HALT		ccpu2(0x00000003)
-#define RNDIS_MSG_QUERY		ccpu2(0x00000004)
-#define RNDIS_MSG_QUERY_C	(RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION)
-#define RNDIS_MSG_SET		ccpu2(0x00000005)
-#define RNDIS_MSG_SET_C		(RNDIS_MSG_SET|RNDIS_MSG_COMPLETION)
-#define RNDIS_MSG_RESET		ccpu2(0x00000006)
-#define RNDIS_MSG_RESET_C	(RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION)
-#define RNDIS_MSG_INDICATE	ccpu2(0x00000007)
-#define RNDIS_MSG_KEEPALIVE	ccpu2(0x00000008)
-#define RNDIS_MSG_KEEPALIVE_C	(RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION)
-
-/* codes for "status" field of completion messages */
-#define	RNDIS_STATUS_SUCCESS		ccpu2(0x00000000)
-#define	RNDIS_STATUS_FAILURE		ccpu2(0xc0000001)
-#define	RNDIS_STATUS_INVALID_DATA	ccpu2(0xc0010015)
-#define	RNDIS_STATUS_NOT_SUPPORTED	ccpu2(0xc00000bb)
-#define	RNDIS_STATUS_MEDIA_CONNECT	ccpu2(0x4001000b)
-#define	RNDIS_STATUS_MEDIA_DISCONNECT	ccpu2(0x4001000c)
-
-
-struct rndis_data_hdr {
-	__le32	msg_type;		/* RNDIS_MSG_PACKET */
-	__le32	msg_len;		// rndis_data_hdr + data_len + pad
-	__le32	data_offset;		// 36 -- right after header
-	__le32	data_len;		// ... real packet size
-
-	__le32	oob_data_offset;	// zero
-	__le32	oob_data_len;		// zero
-	__le32	num_oob;		// zero
-	__le32	packet_data_offset;	// zero
-
-	__le32	packet_data_len;	// zero
-	__le32	vc_handle;		// zero
-	__le32	reserved;		// zero
-} __attribute__ ((packed));
-
-struct rndis_init {		/* OUT */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_INIT */
-	__le32	msg_len;			// 24
-	__le32	request_id;
-	__le32	major_version;			// of rndis (1.0)
-	__le32	minor_version;
-	__le32	max_transfer_size;
-} __attribute__ ((packed));
-
-struct rndis_init_c {		/* IN */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_INIT_C */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	status;
-	__le32	major_version;			// of rndis (1.0)
-	__le32	minor_version;
-	__le32	device_flags;
-	__le32	medium;				// zero == 802.3
-	__le32	max_packets_per_message;
-	__le32	max_transfer_size;
-	__le32	packet_alignment;		// max 7; (1<<n) bytes
-	__le32	af_list_offset;			// zero
-	__le32	af_list_size;			// zero
-} __attribute__ ((packed));
-
-struct rndis_halt {		/* OUT (no reply) */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_HALT */
-	__le32	msg_len;
-	__le32	request_id;
-} __attribute__ ((packed));
-
-struct rndis_query {		/* OUT */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_QUERY */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	oid;
-	__le32	len;
-	__le32	offset;
-/*?*/	__le32	handle;				// zero
-} __attribute__ ((packed));
-
-struct rndis_query_c {		/* IN */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_QUERY_C */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	status;
-	__le32	len;
-	__le32	offset;
-} __attribute__ ((packed));
-
-struct rndis_set {		/* OUT */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_SET */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	oid;
-	__le32	len;
-	__le32	offset;
-/*?*/	__le32	handle;				// zero
-} __attribute__ ((packed));
-
-struct rndis_set_c {		/* IN */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_SET_C */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	status;
-} __attribute__ ((packed));
-
-struct rndis_reset {		/* IN */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_RESET */
-	__le32	msg_len;
-	__le32	reserved;
-} __attribute__ ((packed));
-
-struct rndis_reset_c {		/* OUT */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_RESET_C */
-	__le32	msg_len;
-	__le32	status;
-	__le32	addressing_lost;
-} __attribute__ ((packed));
-
-struct rndis_indicate {		/* IN (unrequested) */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_INDICATE */
-	__le32	msg_len;
-	__le32	status;
-	__le32	length;
-	__le32	offset;
-/**/	__le32	diag_status;
-	__le32	error_offset;
-/**/	__le32	message;
-} __attribute__ ((packed));
-
-struct rndis_keepalive {	/* OUT (optionally IN) */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE */
-	__le32	msg_len;
-	__le32	request_id;
-} __attribute__ ((packed));
-
-struct rndis_keepalive_c {	/* IN (optionally OUT) */
-	// header and:
-	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE_C */
-	__le32	msg_len;
-	__le32	request_id;
-	__le32	status;
-} __attribute__ ((packed));
-
-/* 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)
-
-/* packet filter bits used by NDIS_OID_PACKET_FILTER */
-#define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
-#define RNDIS_PACKET_TYPE_MULTICAST		ccpu2(0x00000002)
-#define RNDIS_PACKET_TYPE_ALL_MULTICAST		ccpu2(0x00000004)
-#define RNDIS_PACKET_TYPE_BROADCAST		ccpu2(0x00000008)
-#define RNDIS_PACKET_TYPE_SOURCE_ROUTING	ccpu2(0x00000010)
-#define RNDIS_PACKET_TYPE_PROMISCUOUS		ccpu2(0x00000020)
-#define RNDIS_PACKET_TYPE_SMT			ccpu2(0x00000040)
-#define RNDIS_PACKET_TYPE_ALL_LOCAL		ccpu2(0x00000080)
-#define RNDIS_PACKET_TYPE_GROUP			ccpu2(0x00001000)
-#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL	ccpu2(0x00002000)
-#define RNDIS_PACKET_TYPE_FUNCTIONAL		ccpu2(0x00004000)
-#define RNDIS_PACKET_TYPE_MAC_FRAME		ccpu2(0x00008000)
-
-/* default filter used with RNDIS devices */
-#define RNDIS_DEFAULT_FILTER ( \
-	RNDIS_PACKET_TYPE_DIRECTED | \
-	RNDIS_PACKET_TYPE_BROADCAST | \
-	RNDIS_PACKET_TYPE_ALL_MULTICAST | \
-	RNDIS_PACKET_TYPE_PROMISCUOUS )
-
-/*
- * RNDIS notifications from device: command completion; "reverse"
- * keepalives; etc
- */
-static void rndis_status(struct usbnet *dev, struct urb *urb)
-{
-	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
-}
-
-/*
- * RPC done RNDIS-style.  Caller guarantees:
- * - message is properly byteswapped
- * - there's no other request pending
- * - buf can hold up to 1KB response (required by RNDIS spec)
- * On return, the first few entries are already byteswapped.
- *
- * 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)
-{
-	struct cdc_state	*info = (void *) &dev->data;
-	int			master_ifnum;
-	int			retval;
-	unsigned		count;
-	__le32			rsp;
-	u32			xid = 0, msg_len, request_id;
-
-	/* REVISIT when this gets called from contexts other than probe() or
-	 * disconnect(): either serialize, or dispatch responses on xid
-	 */
-
-	/* 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 = (__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, master_ifnum,
-		buf, le32_to_cpu(buf->msg_len),
-		RNDIS_CONTROL_TIMEOUT_MS);
-	if (unlikely(retval < 0 || xid == 0))
-		return retval;
-
-	// 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.
-
-	/* 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);
-		retval = usb_control_msg(dev->udev,
-			usb_rcvctrlpipe(dev->udev, 0),
-			USB_CDC_GET_ENCAPSULATED_RESPONSE,
-			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-			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 = (__force u32) buf->request_id;
-			if (likely(buf->msg_type == rsp)) {
-				if (likely(request_id == xid)) {
-					if (unlikely(rsp == RNDIS_MSG_RESET_C))
-						return 0;
-					if (likely(RNDIS_STATUS_SUCCESS
-							== buf->status))
-						return 0;
-					dev_dbg(&info->control->dev,
-						"rndis reply status %08x\n",
-						le32_to_cpu(buf->status));
-					return -EL3RST;
-				}
-				dev_dbg(&info->control->dev,
-					"rndis reply id %d expected %d\n",
-					request_id, xid);
-				/* then likely retry */
-			} else switch (buf->msg_type) {
-			case RNDIS_MSG_INDICATE: {	/* fault */
-				// struct rndis_indicate *msg = (void *)buf;
-				dev_info(&info->control->dev,
-					"rndis fault indication\n");
-				}
-				break;
-			case RNDIS_MSG_KEEPALIVE: {	/* ping */
-				struct rndis_keepalive_c *msg = (void *)buf;
-
-				msg->msg_type = RNDIS_MSG_KEEPALIVE_C;
-				msg->msg_len = ccpu2(sizeof *msg);
-				msg->status = RNDIS_STATUS_SUCCESS;
-				retval = usb_control_msg(dev->udev,
-					usb_sndctrlpipe(dev->udev, 0),
-					USB_CDC_SEND_ENCAPSULATED_COMMAND,
-					USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-					0, master_ifnum,
-					msg, sizeof *msg,
-					RNDIS_CONTROL_TIMEOUT_MS);
-				if (unlikely(retval < 0))
-					dev_dbg(&info->control->dev,
-						"rndis keepalive err %d\n",
-						retval);
-				}
-				break;
-			default:
-				dev_dbg(&info->control->dev,
-					"unexpected rndis msg %08x len %d\n",
-					le32_to_cpu(buf->msg_type), msg_len);
-			}
-		} else {
-			/* device probably issued a protocol stall; ignore */
-			dev_dbg(&info->control->dev,
-				"rndis response error, code %d\n", retval);
-		}
-		msleep(2);
-	}
-	dev_dbg(&info->control->dev, "rndis response timeout\n");
-	return -ETIMEDOUT;
-}
-
-/*
- * rndis_query:
- *
- * Performs a query for @oid along with 0 or more bytes of payload as
- * specified by @in_len. If @reply_len is not set to -1 then the reply
- * length is checked against this value, resulting in an error if it
- * doesn't match.
- *
- * 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,
-		void **reply, int *reply_len)
-{
-	int retval;
-	union {
-		void			*buf;
-		struct rndis_msg_hdr	*header;
-		struct rndis_query	*get;
-		struct rndis_query_c	*get_c;
-	} u;
-	u32 off, len;
-
-	u.buf = buf;
-
-	memset(u.get, 0, sizeof *u.get + in_len);
-	u.get->msg_type = RNDIS_MSG_QUERY;
-	u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len);
-	u.get->oid = oid;
-	u.get->len = cpu_to_le32(in_len);
-	u.get->offset = ccpu2(20);
-
-	retval = rndis_command(dev, u.header);
-	if (unlikely(retval < 0)) {
-		dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
-				oid, retval);
-		return retval;
-	}
-
-	off = le32_to_cpu(u.get_c->offset);
-	len = le32_to_cpu(u.get_c->len);
-	if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
-		goto response_error;
-
-	if (*reply_len != -1 && len != *reply_len)
-		goto response_error;
-
-	*reply = (unsigned char *) &u.get_c->request_id + off;
-	*reply_len = len;
-
-	return retval;
-
-response_error:
-	dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) "
-			"invalid response - off %d len %d\n",
-		oid, off, len);
-	return -EDOM;
-}
-
-static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
-{
-	int			retval;
-	struct net_device	*net = dev->net;
-	struct cdc_state	*info = (void *) &dev->data;
-	union {
-		void			*buf;
-		struct rndis_msg_hdr	*header;
-		struct rndis_init	*init;
-		struct rndis_init_c	*init_c;
-		struct rndis_query	*get;
-		struct rndis_query_c	*get_c;
-		struct rndis_set	*set;
-		struct rndis_set_c	*set_c;
-		struct rndis_halt	*halt;
-	} u;
-	u32			tmp;
-	int			reply_len;
-	unsigned char		*bp;
-
-	/* 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;
-	retval = usbnet_generic_cdc_bind(dev, intf);
-	if (retval < 0)
-		goto fail;
-
-	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);
-
-	/* max transfer (in spec) is 0x4000 at full speed, but for
-	 * TX we'll stick to one Ethernet packet plus RNDIS framing.
-	 * For RX we handle drivers that zero-pad to end-of-packet.
-	 * Don't let userspace change these settings.
-	 *
-	 * NOTE: there still seems to be wierdness here, as if we need
-	 * to do some more things to make sure WinCE targets accept this.
-	 * They default to jumbograms of 8KB or 16KB, which is absurd
-	 * for such low data rates and which is also more than Linux
-	 * can usually expect to allocate for SKB data...
-	 */
-	net->hard_header_len += sizeof (struct rndis_data_hdr);
-	dev->hard_mtu = net->mtu + net->hard_header_len;
-
-	dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1);
-	dev->rx_urb_size &= ~(dev->maxpacket - 1);
-	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)) {
-		/* it might not even be an RNDIS device!! */
-		dev_err(&intf->dev, "RNDIS init failed, %d\n", retval);
-		goto fail_and_release;
-	}
-	tmp = le32_to_cpu(u.init_c->max_transfer_size);
-	if (tmp < dev->hard_mtu) {
-		if (tmp <= net->hard_header_len) {
-			dev_err(&intf->dev,
-				"dev can't take %u byte packets (max %u)\n",
-				dev->hard_mtu, tmp);
-			retval = -EINVAL;
-			goto halt_fail_and_release;
-		}
-		dev->hard_mtu = tmp;
-		net->mtu = dev->hard_mtu - net->hard_header_len;
-		dev_warn(&intf->dev,
-			 "dev can't take %u byte packets (max %u), "
-			 "adjusting MTU to %u\n",
-			 dev->hard_mtu, tmp, net->mtu);
-	}
-
-	/* 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 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);
-	if (unlikely(retval< 0)) {
-		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
-		goto halt_fail_and_release;
-	}
-	memcpy(net->dev_addr, bp, ETH_ALEN);
-
-	/* set a nonzero filter to enable data transfers */
-	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 = OID_GEN_CURRENT_PACKET_FILTER;
-	u.set->len = ccpu2(4);
-	u.set->offset = ccpu2((sizeof *u.set) - 8);
-	*(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
-
-	retval = rndis_command(dev, u.header);
-	if (unlikely(retval < 0)) {
-		dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
-		goto halt_fail_and_release;
-	}
-
-	retval = 0;
-
-	kfree(u.buf);
-	return retval;
-
-halt_fail_and_release:
-	memset(u.halt, 0, sizeof *u.halt);
-	u.halt->msg_type = RNDIS_MSG_HALT;
-	u.halt->msg_len = ccpu2(sizeof *u.halt);
-	(void) rndis_command(dev, (void *)u.halt);
-fail_and_release:
-	usb_set_intfdata(info->data, NULL);
-	usb_driver_release_interface(driver_of(intf), info->data);
-	info->data = NULL;
-fail:
-	kfree(u.buf);
-	return retval;
-}
-
-static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
-{
-	struct rndis_halt	*halt;
-
-	/* try to clear any rndis state/activity (no i/o from stack!) */
-	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
-	if (halt) {
-		halt->msg_type = RNDIS_MSG_HALT;
-		halt->msg_len = ccpu2(sizeof *halt);
-		(void) rndis_command(dev, (void *)halt);
-		kfree(halt);
-	}
-
-	usbnet_cdc_unbind(dev, intf);
-}
-
-/*
- * DATA -- host must not write zlps
- */
-static int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
-{
-	/* peripheral may have batched packets to us... */
-	while (likely(skb->len)) {
-		struct rndis_data_hdr	*hdr = (void *)skb->data;
-		struct sk_buff		*skb2;
-		u32			msg_len, data_offset, data_len;
-
-		msg_len = le32_to_cpu(hdr->msg_len);
-		data_offset = le32_to_cpu(hdr->data_offset);
-		data_len = le32_to_cpu(hdr->data_len);
-
-		/* don't choke if we see oob, per-packet data, etc */
-		if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET
-				|| skb->len < msg_len
-				|| (data_offset + data_len + 8) > msg_len)) {
-			dev->stats.rx_frame_errors++;
-			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;
-		}
-		skb_pull(skb, 8 + data_offset);
-
-		/* at most one packet left? */
-		if (likely((data_len - skb->len) <= sizeof *hdr)) {
-			skb_trim(skb, data_len);
-			break;
-		}
-
-		/* try to return all the packets in the batch */
-		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);
-	}
-
-	/* caller will usbnet_skb_return the remaining packet */
-	return 1;
-}
-
-static struct sk_buff *
-rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
-{
-	struct rndis_data_hdr	*hdr;
-	struct sk_buff		*skb2;
-	unsigned		len = skb->len;
-
-	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_set_tail_pointer(skb, 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;
-	skb = skb2;
-
-	/* 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;
-	hdr->msg_len = cpu_to_le32(skb->len);
-	hdr->data_offset = ccpu2(sizeof(*hdr) - 8);
-	hdr->data_len = cpu_to_le32(len);
-
-	/* 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 | FLAG_NO_SETINT,
-	.bind =		rndis_bind,
-	.unbind =	rndis_unbind,
-	.status =	rndis_status,
-	.rx_fixup =	rndis_rx_fixup,
-	.tx_fixup =	rndis_tx_fixup,
-};
-
-#undef ccpu2
-
-
-/*-------------------------------------------------------------------------*/
-
-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),
-	.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
-};
-MODULE_DEVICE_TABLE(usb, products);
-
-static struct usb_driver rndis_driver = {
-	.name =		"rndis_host",
-	.id_table =	products,
-	.probe =	usbnet_probe,
-	.disconnect =	usbnet_disconnect,
-	.suspend =	usbnet_suspend,
-	.resume =	usbnet_resume,
-};
-
-static int __init rndis_init(void)
-{
-	return usb_register(&rndis_driver);
-}
-module_init(rndis_init);
-
-static void __exit rndis_exit(void)
-{
-	usb_deregister(&rndis_driver);
-}
-module_exit(rndis_exit);
-
-MODULE_AUTHOR("David Brownell");
-MODULE_DESCRIPTION("USB Host side RNDIS driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/rndis_host.h b/drivers/net/usb/rndis_host.h
new file mode 100644
index 0000000..56de532
--- /dev/null
+++ b/drivers/net/usb/rndis_host.h
@@ -0,0 +1,256 @@
+/*
+ * Host Side support for RNDIS Networking Links
+ * Copyright (C) 2005 by David Brownell
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef	__RNDIS_HOST_H
+#define	__RNDIS_HOST_H
+
+
+/*
+ * CONTROL uses CDC "encapsulated commands" with funky notifications.
+ *  - 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_* */
+	__le32	msg_len;
+	// followed by data that varies between messages
+	__le32	request_id;
+	__le32	status;
+	// ... and more
+} __attribute__ ((packed));
+
+/* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */
+#define	CONTROL_BUFFER_SIZE		1025
+
+/* 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 RNDIS_MSG_COMPLETION	ccpu2(0x80000000)
+
+/* codes for "msg_type" field of rndis messages;
+ * only the data channel uses packet messages (maybe batched);
+ * everything else goes on the control channel.
+ */
+#define RNDIS_MSG_PACKET	ccpu2(0x00000001)	/* 1-N packets */
+#define RNDIS_MSG_INIT		ccpu2(0x00000002)
+#define RNDIS_MSG_INIT_C	(RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION)
+#define RNDIS_MSG_HALT		ccpu2(0x00000003)
+#define RNDIS_MSG_QUERY		ccpu2(0x00000004)
+#define RNDIS_MSG_QUERY_C	(RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION)
+#define RNDIS_MSG_SET		ccpu2(0x00000005)
+#define RNDIS_MSG_SET_C		(RNDIS_MSG_SET|RNDIS_MSG_COMPLETION)
+#define RNDIS_MSG_RESET		ccpu2(0x00000006)
+#define RNDIS_MSG_RESET_C	(RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION)
+#define RNDIS_MSG_INDICATE	ccpu2(0x00000007)
+#define RNDIS_MSG_KEEPALIVE	ccpu2(0x00000008)
+#define RNDIS_MSG_KEEPALIVE_C	(RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION)
+
+/* codes for "status" field of completion messages */
+#define	RNDIS_STATUS_SUCCESS		ccpu2(0x00000000)
+#define	RNDIS_STATUS_FAILURE		ccpu2(0xc0000001)
+#define	RNDIS_STATUS_INVALID_DATA	ccpu2(0xc0010015)
+#define	RNDIS_STATUS_NOT_SUPPORTED	ccpu2(0xc00000bb)
+#define	RNDIS_STATUS_MEDIA_CONNECT	ccpu2(0x4001000b)
+#define	RNDIS_STATUS_MEDIA_DISCONNECT	ccpu2(0x4001000c)
+
+
+struct rndis_data_hdr {
+	__le32	msg_type;		/* RNDIS_MSG_PACKET */
+	__le32	msg_len;		// rndis_data_hdr + data_len + pad
+	__le32	data_offset;		// 36 -- right after header
+	__le32	data_len;		// ... real packet size
+
+	__le32	oob_data_offset;	// zero
+	__le32	oob_data_len;		// zero
+	__le32	num_oob;		// zero
+	__le32	packet_data_offset;	// zero
+
+	__le32	packet_data_len;	// zero
+	__le32	vc_handle;		// zero
+	__le32	reserved;		// zero
+} __attribute__ ((packed));
+
+struct rndis_init {		/* OUT */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_INIT */
+	__le32	msg_len;			// 24
+	__le32	request_id;
+	__le32	major_version;			// of rndis (1.0)
+	__le32	minor_version;
+	__le32	max_transfer_size;
+} __attribute__ ((packed));
+
+struct rndis_init_c {		/* IN */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_INIT_C */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	status;
+	__le32	major_version;			// of rndis (1.0)
+	__le32	minor_version;
+	__le32	device_flags;
+	__le32	medium;				// zero == 802.3
+	__le32	max_packets_per_message;
+	__le32	max_transfer_size;
+	__le32	packet_alignment;		// max 7; (1<<n) bytes
+	__le32	af_list_offset;			// zero
+	__le32	af_list_size;			// zero
+} __attribute__ ((packed));
+
+struct rndis_halt {		/* OUT (no reply) */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_HALT */
+	__le32	msg_len;
+	__le32	request_id;
+} __attribute__ ((packed));
+
+struct rndis_query {		/* OUT */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_QUERY */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	oid;
+	__le32	len;
+	__le32	offset;
+/*?*/	__le32	handle;				// zero
+} __attribute__ ((packed));
+
+struct rndis_query_c {		/* IN */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_QUERY_C */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	status;
+	__le32	len;
+	__le32	offset;
+} __attribute__ ((packed));
+
+struct rndis_set {		/* OUT */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_SET */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	oid;
+	__le32	len;
+	__le32	offset;
+/*?*/	__le32	handle;				// zero
+} __attribute__ ((packed));
+
+struct rndis_set_c {		/* IN */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_SET_C */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	status;
+} __attribute__ ((packed));
+
+struct rndis_reset {		/* IN */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_RESET */
+	__le32	msg_len;
+	__le32	reserved;
+} __attribute__ ((packed));
+
+struct rndis_reset_c {		/* OUT */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_RESET_C */
+	__le32	msg_len;
+	__le32	status;
+	__le32	addressing_lost;
+} __attribute__ ((packed));
+
+struct rndis_indicate {		/* IN (unrequested) */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_INDICATE */
+	__le32	msg_len;
+	__le32	status;
+	__le32	length;
+	__le32	offset;
+/**/	__le32	diag_status;
+	__le32	error_offset;
+/**/	__le32	message;
+} __attribute__ ((packed));
+
+struct rndis_keepalive {	/* OUT (optionally IN) */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE */
+	__le32	msg_len;
+	__le32	request_id;
+} __attribute__ ((packed));
+
+struct rndis_keepalive_c {	/* IN (optionally OUT) */
+	// header and:
+	__le32	msg_type;			/* RNDIS_MSG_KEEPALIVE_C */
+	__le32	msg_len;
+	__le32	request_id;
+	__le32	status;
+} __attribute__ ((packed));
+
+/* 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)
+
+/* packet filter bits used by NDIS_OID_PACKET_FILTER */
+#define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
+#define RNDIS_PACKET_TYPE_MULTICAST		ccpu2(0x00000002)
+#define RNDIS_PACKET_TYPE_ALL_MULTICAST		ccpu2(0x00000004)
+#define RNDIS_PACKET_TYPE_BROADCAST		ccpu2(0x00000008)
+#define RNDIS_PACKET_TYPE_SOURCE_ROUTING	ccpu2(0x00000010)
+#define RNDIS_PACKET_TYPE_PROMISCUOUS		ccpu2(0x00000020)
+#define RNDIS_PACKET_TYPE_SMT			ccpu2(0x00000040)
+#define RNDIS_PACKET_TYPE_ALL_LOCAL		ccpu2(0x00000080)
+#define RNDIS_PACKET_TYPE_GROUP			ccpu2(0x00001000)
+#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL	ccpu2(0x00002000)
+#define RNDIS_PACKET_TYPE_FUNCTIONAL		ccpu2(0x00004000)
+#define RNDIS_PACKET_TYPE_MAC_FRAME		ccpu2(0x00008000)
+
+/* default filter used with RNDIS devices */
+#define RNDIS_DEFAULT_FILTER ( \
+	RNDIS_PACKET_TYPE_DIRECTED | \
+	RNDIS_PACKET_TYPE_BROADCAST | \
+	RNDIS_PACKET_TYPE_ALL_MULTICAST | \
+	RNDIS_PACKET_TYPE_PROMISCUOUS )
+
+extern void rndis_status(struct usbnet *dev, struct urb *urb);
+extern int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf);
+extern int rndis_bind(struct usbnet *dev, struct usb_interface *intf);
+extern void rndis_unbind(struct usbnet *dev, struct usb_interface *intf);
+extern int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb);
+extern struct sk_buff *
+rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags);
+
+#endif	/* __RNDIS_HOST_H */
+
-- 
1.5.2.5


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 7/8] Add Wireless Extensions to rndis_host
  2007-12-22 21:51 [PATCH 0/8] RFC: Wireless Extensions for rndis_host Bjorge Dijkstra
                   ` (3 preceding siblings ...)
  2007-12-22 21:51 ` [PATCH 6/8] [PATCH] Split up rndis_host.c Bjorge Dijkstra
@ 2007-12-22 21:51 ` Bjorge Dijkstra
       [not found]   ` <11983602951628-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
  4 siblings, 1 reply; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell; +Cc: netdev, linux-wireless

The bulk of this patch is the addition of a new file that
implements the wireless extensions for RNDIS devices.
The rest are some smaller changes to usbnet and rndis_host
to hook the wireless extensions into them:
* a private data pointer is added to usbnet.
* a callback is added to driver_info to signal link state changes.
* a physical medium type check is added to rndis_bind to check for
  wireless lan devices and turn on the wireless extensions.
* and finally a Kconfig option to enable/disable this all.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>
---
 drivers/net/usb/Kconfig      |    7 +
 drivers/net/usb/Makefile     |    4 +
 drivers/net/usb/rndis_base.c |   50 +-
 drivers/net/usb/rndis_host.h |   18 +
 drivers/net/usb/rndis_wext.c | 2177 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/usbnet.h     |    4 +
 6 files changed, 2255 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/usb/rndis_wext.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index a12c9c4..cc38294 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -239,6 +239,13 @@ config USB_NET_RNDIS_HOST
 	  The protocol specification is incomplete, and is controlled by
 	  (and for) Microsoft; it isn't an "Open" ecosystem or market.
 
+config USB_NET_RNDIS_WEXT
+	boolean "Wireless Extensions for RNDIS host driver"
+	depends on USB_NET_RNDIS_HOST && WLAN_80211 && EXPERIMENTAL
+	select WIRELESS_EXT
+	help
+	  This option enables support for RNDIS based wireless LAN devices.
+
 config USB_NET_CDC_SUBSET
 	tristate "Simple USB Network Links (CDC Ethernet subset)"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 611ab62..83c14f1 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -14,6 +14,10 @@ obj-$(CONFIG_USB_NET_NET1080)	+= net1080.o
 obj-$(CONFIG_USB_NET_PLUSB)	+= plusb.o
 obj-$(CONFIG_USB_NET_RNDIS_HOST)	+= rndis_host.o
 rndis_host-objs			+= rndis_base.o
+ifeq ($(CONFIG_USB_NET_RNDIS_WEXT),y)
+rndis_host-objs += rndis_wext.o
+endif
+
 obj-$(CONFIG_USB_NET_CDC_SUBSET)	+= cdc_subset.o
 obj-$(CONFIG_USB_NET_ZAURUS)	+= zaurus.o
 obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
diff --git a/drivers/net/usb/rndis_base.c b/drivers/net/usb/rndis_base.c
index f3b0c00..97faaed 100644
--- a/drivers/net/usb/rndis_base.c
+++ b/drivers/net/usb/rndis_base.c
@@ -148,10 +148,26 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf)
 					request_id, xid);
 				/* then likely retry */
 			} else switch (buf->msg_type) {
-			case RNDIS_MSG_INDICATE: {	/* fault */
-				// struct rndis_indicate *msg = (void *)buf;
-				dev_info(&info->control->dev,
-					"rndis fault indication\n");
+			case RNDIS_MSG_INDICATE: {	/* fault/event */
+				struct rndis_indicate *msg = (void *)buf;
+
+				switch (msg->status) {
+				case RNDIS_STATUS_MEDIA_CONNECT:
+					dev_info(&info->control->dev,
+						"rndis media connect\n");
+					if (dev->driver_info->link_change)
+					    dev->driver_info->link_change(dev, 1);
+					break;
+				case RNDIS_STATUS_MEDIA_DISCONNECT:
+					dev_info(&info->control->dev,
+						"rndis media disconnect\n");
+					if (dev->driver_info->link_change)
+					    dev->driver_info->link_change(dev, 0);
+					break;
+				default:
+					dev_info(&info->control->dev,
+						"rndis indication\n");
+				}
 				}
 				break;
 			case RNDIS_MSG_KEEPALIVE: {	/* ping */
@@ -271,7 +287,7 @@ int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 		struct rndis_set_c	*set_c;
 		struct rndis_halt	*halt;
 	} u;
-	u32			tmp;
+	u32			tmp, *phym;
 	int			reply_len;
 	unsigned char		*bp;
 
@@ -336,6 +352,30 @@ int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 		dev->hard_mtu, tmp, dev->rx_urb_size,
 		1 << le32_to_cpu(u.init_c->packet_alignment));
 
+	/* Check physical medium */
+	reply_len = sizeof *phym;
+	retval = rndis_query(dev, intf, u.buf, OID_GEN_PHYSICAL_MEDIUM,
+			0, (void **) &phym, &reply_len);
+	if (retval != 0) {
+		dev_err(&intf->dev, "rndis get physical medium, %d\n", retval);
+		tmp = RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED;
+	} else
+		tmp = *phym;
+
+	if (tmp == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {
+#ifdef CONFIG_USB_NET_RNDIS_WEXT
+		dev_info(&info->control->dev, "RNDIS wlan device,"
+				" turning on wireless extensions\n");
+		dev->driver_info = &rndis_wext_info;
+		retval = dev->driver_info->bind(dev, intf);
+		if (retval < 0)
+			goto halt_fail_and_release;
+#else
+		dev_info(&info->control->dev, "RNDIS wlan device,"
+				" wireless extensions not available\n");
+#endif
+	}
+
 	/* Get designated host ethernet address */
 	reply_len = ETH_ALEN;
 	retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS,
diff --git a/drivers/net/usb/rndis_host.h b/drivers/net/usb/rndis_host.h
index 56de532..6427315 100644
--- a/drivers/net/usb/rndis_host.h
+++ b/drivers/net/usb/rndis_host.h
@@ -82,6 +82,18 @@ struct rndis_msg_hdr {
 #define	RNDIS_STATUS_MEDIA_CONNECT	ccpu2(0x4001000b)
 #define	RNDIS_STATUS_MEDIA_DISCONNECT	ccpu2(0x4001000c)
 
+/* codes for OID_GEN_PHYSICAL_MEDIUM */
+#define RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED       ccpu2(0x00000000)
+#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN      ccpu2(0x00000001)
+#define RNDIS_PHYSICAL_MEDIUM_CABLE_MODEM       ccpu2(0x00000002)
+#define RNDIS_PHYSICAL_MEDIUM_PHONE_LINE        ccpu2(0x00000003)
+#define RNDIS_PHYSICAL_MEDIUM_POWER_LINE        ccpu2(0x00000004)
+#define RNDIS_PHYSICAL_MEDIUM_DSL               ccpu2(0x00000005)
+#define RNDIS_PHYSICAL_MEDIUM_FIBRE_CHANNEL     ccpu2(0x00000006)
+#define RNDIS_PHYSICAL_MEDIUM_1394              ccpu2(0x00000007)
+#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_WAN      ccpu2(0x00000008)
+#define RNDIS_PHYSICAL_MEDIUM_MAX               ccpu2(0x00000009)
+
 
 struct rndis_data_hdr {
 	__le32	msg_type;		/* RNDIS_MSG_PACKET */
@@ -222,6 +234,7 @@ struct rndis_keepalive_c {	/* IN (optionally OUT) */
 #define OID_802_3_PERMANENT_ADDRESS	ccpu2(0x01010101)
 #define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)
 #define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
+#define OID_GEN_PHYSICAL_MEDIUM		ccpu2(0x00010202)
 
 /* packet filter bits used by NDIS_OID_PACKET_FILTER */
 #define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
@@ -252,5 +265,10 @@ extern int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb);
 extern struct sk_buff *
 rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags);
 
+#ifdef CONFIG_USB_NET_RNDIS_WEXT
+/* from rndis_wext.c */
+extern struct driver_info rndis_wext_info;
+#endif
+
 #endif	/* __RNDIS_HOST_H */
 
diff --git a/drivers/net/usb/rndis_wext.c b/drivers/net/usb/rndis_wext.c
new file mode 100644
index 0000000..a9ce944
--- /dev/null
+++ b/drivers/net/usb/rndis_wext.c
@@ -0,0 +1,2177 @@
+/*
+ * Wireless Extensions for RNDIS host
+ * Copyright (C) 2007 by Bjorge Dijkstra (bjd@jooz.net)
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Portions of this file are based on NDISwrapper project,
+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ * http://ndiswrapper.sourceforge.net/
+ */
+
+// #define	DEBUG			// error path messages, extra info
+// #define	VERBOSE			// more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+
+
+#include "usbnet.h"
+#include "rndis_host.h"
+
+/* various RNDIS OID defs */
+#define OID_GEN_LINK_SPEED			ccpu2(0x00010107)
+#define OID_GEN_RNDIS_CONFIG_PARAMETER		ccpu2(0x0001021b)
+
+#define OID_802_3_PERMANENT_ADDRESS		ccpu2(0x01010101)
+#define OID_802_3_CURRENT_ADDRESS		ccpu2(0x01010102)
+#define OID_802_3_MULTICAST_LIST        	ccpu2(0x01010103)
+
+#define OID_802_11_BSSID			ccpu2(0x0d010101)
+#define OID_802_11_SSID				ccpu2(0x0d010102)
+#define OID_802_11_INFRASTRUCTURE_MODE		ccpu2(0x0d010108)
+#define OID_802_11_ADD_WEP			ccpu2(0x0d010113)
+#define OID_802_11_REMOVE_WEP			ccpu2(0x0d010114)
+#define OID_802_11_DISASSOCIATE			ccpu2(0x0d010115)
+#define OID_802_11_AUTHENTICATION_MODE		ccpu2(0x0d010118)
+#define OID_802_11_PRIVACY_FILTER		ccpu2(0x0d010119)
+#define OID_802_11_BSSID_LIST_SCAN		ccpu2(0x0d01011a)
+#define OID_802_11_ENCRYPTION_STATUS		ccpu2(0x0d01011b)
+#define OID_802_11_ADD_KEY			ccpu2(0x0d01011d)
+#define OID_802_11_REMOVE_KEY			ccpu2(0x0d01011e)
+#define OID_802_11_PMKID			ccpu2(0x0d010123)
+#define OID_802_11_NETWORK_TYPES_SUPPORTED	ccpu2(0x0d010203)
+#define OID_802_11_NETWORK_TYPE_IN_USE		ccpu2(0x0d010204)
+#define OID_802_11_TX_POWER_LEVEL		ccpu2(0x0d010205)
+#define OID_802_11_RSSI				ccpu2(0x0d010206)
+#define OID_802_11_RSSI_TRIGGER			ccpu2(0x0d010207)
+#define OID_802_11_FRAGMENTATION_THRESHOLD	ccpu2(0x0d010209)
+#define OID_802_11_RTS_THRESHOLD		ccpu2(0x0d01020a)
+#define OID_802_11_SUPPORTED_RATES		ccpu2(0x0d01020e)
+#define OID_802_11_CONFIGURATION		ccpu2(0x0d010211)
+#define OID_802_11_BSSID_LIST			ccpu2(0x0d010217)
+#define OID_802_11_STATISTICS			ccpu2(0x0d020212)
+
+
+/* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */
+#define	WL_NOISE	-96	/* typical noise level in dBm */
+#define	WL_SIGMAX	-32	/* typical maximum signal level in dBm */
+
+
+/* Assume that Broadcom 4320 (only chipset at time of writing known to be
+ * based on wireless rndis) has default txpower of 13dBm as Linksys WUSB54GSC.
+ * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications.
+ *   13dBm == 19.9mW
+ */
+#define BCM4320_DEFAULT_TXPOWER 20
+
+
+/* codes for "status" field of completion messages */
+#define RNDIS_STATUS_ADAPTER_NOT_READY		ccpu2(0xc0010011)
+#define RNDIS_STATUS_ADAPTER_NOT_OPEN		ccpu2(0xc0010012)
+
+
+/* NDIS data structures. Taken from wpa_supplicant driver_ndis.c
+ * slightly modified for datatype endianess, etc
+ */
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+struct NDIS_802_11_SSID {
+	__le32 SsidLength;
+	u8 Ssid[NDIS_802_11_LENGTH_SSID];
+};
+
+enum NDIS_802_11_NETWORK_TYPE {
+	Ndis802_11FH,
+	Ndis802_11DS,
+	Ndis802_11OFDM5,
+	Ndis802_11OFDM24,
+	Ndis802_11NetworkTypeMax
+};
+
+struct NDIS_802_11_CONFIGURATION_FH {
+	__le32 Length;
+	__le32 HopPattern;
+	__le32 HopSet;
+	__le32 DwellTime;
+};
+
+struct NDIS_802_11_CONFIGURATION {
+	__le32 Length;
+	__le32 BeaconPeriod;
+	__le32 ATIMWindow;
+	__le32 DSConfig;
+	struct NDIS_802_11_CONFIGURATION_FH FHConfig;
+};
+
+struct NDIS_802_11_STATS {
+	__le32 Length;
+	__le64 TxFrag;
+	__le64 TxMultiFrag;
+	__le64 Failed;
+	__le64 Retry;
+	__le64 MultiRetry;
+	__le64 RtssSucc;
+	__le64 RtssFail;
+	__le64 AckFail;
+	__le64 FrameDup;
+	__le64 RxFrag;
+	__le64 RxMultiFrag;
+	__le64 FcsErr;
+	__le64 TkipLocalMicFailures;
+	__le64 TkipIcvErrors;
+	__le64 TkipCounterMeasuresInvoked;
+	__le64 TkipReplays;
+	__le64 CcmpFormatErrors;
+	__le64 CcmpReplays;
+	__le64 CcmpDecryptErrors;
+	__le64 FourwayHandshakeFailures;
+	__le64 WepUndecryptableCount;
+	__le64 WepIcvErrorcount;
+	__le64 DecryptSuccessCount;
+	__le64 DecryptFailureCount;
+} __attribute__((packed));
+
+enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+	Ndis802_11IBSS,
+	Ndis802_11Infrastructure,
+	Ndis802_11AutoUnknown,
+	Ndis802_11InfrastructureMax
+};
+
+enum NDIS_802_11_AUTHENTICATION_MODE {
+	Ndis802_11AuthModeOpen,
+	Ndis802_11AuthModeShared,
+	Ndis802_11AuthModeAutoSwitch,
+	Ndis802_11AuthModeWPA,
+	Ndis802_11AuthModeWPAPSK,
+	Ndis802_11AuthModeWPANone,
+	Ndis802_11AuthModeWPA2,
+	Ndis802_11AuthModeWPA2PSK,
+	Ndis802_11AuthModeMax
+};
+
+enum NDIS_802_11_ENCRYPTION_STATUS {
+	Ndis802_11WEPEnabled,
+	Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+	Ndis802_11WEPDisabled,
+	Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+	Ndis802_11WEPKeyAbsent,
+	Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+	Ndis802_11WEPNotSupported,
+	Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+	Ndis802_11Encryption2Enabled,
+	Ndis802_11Encryption2KeyAbsent,
+	Ndis802_11Encryption3Enabled,
+	Ndis802_11Encryption3KeyAbsent
+};
+
+enum NDIS_802_11_PRIVACY_FILTER {
+	Ndis802_11PrivFilterAcceptAll,
+	Ndis802_11PrivFilter8021xWEP
+};
+
+struct NDIS_WLAN_BSSID_EX {
+	__le32 Length;
+	u8 MacAddress[6];
+	u8 Padding[2];
+	struct NDIS_802_11_SSID Ssid;
+	__le32 Privacy;
+	__le32 Rssi;
+	enum NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+	struct NDIS_802_11_CONFIGURATION Configuration;
+	enum NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+	u8 SupportedRates[NDIS_802_11_LENGTH_RATES_EX];
+	__le32 IELength;
+	u8 IEs[0];
+};
+
+struct NDIS_802_11_BSSID_LIST_EX {
+	__le32 NumberOfItems;
+	struct NDIS_WLAN_BSSID_EX Bssid[0];
+};
+
+struct NDIS_802_11_FIXED_IEs {
+	u8 Timestamp[8];
+	__le16 BeaconInterval;
+	__le16 Capabilities;
+};
+
+struct NDIS_802_11_WEP {
+	__le32 Length;
+	__le32 KeyIndex;
+	__le32 KeyLength;
+	u8 KeyMaterial[0];
+};
+
+struct NDIS_802_11_KEY {
+	__le32 Length;
+	__le32 KeyIndex;
+	__le32 KeyLength;
+	u8 Bssid[6];
+	u8 Padding[6];
+	u8 KeyRSC[8];
+	u8 KeyMaterial[32];
+};
+
+struct NDIS_802_11_REMOVE_KEY {
+	__le32 Length;
+	__le32 KeyIndex;
+	u8 Bssid[6];
+};
+
+struct RNDIS_CONFIG_PARAMETER_INFOBUFFER {
+	__le32 ParameterNameOffset;
+	__le32 ParameterNameLength;
+	__le32 ParameterType;
+	__le32 ParameterValueOffset;
+	__le32 ParameterValueLength;
+} __attribute__((packed));
+
+/*
+ *  private data
+ */
+#define NET_TYPE_11FB	0
+
+#define CAP_MODE_80211A		1
+#define CAP_MODE_80211B		2
+#define CAP_MODE_80211G		4
+#define CAP_MODE_MASK		7
+#define CAP_SUPPORT_TXPOWER	8
+
+/* RNDIS device private data */
+struct rndis_wext_private {
+	char name[32];
+
+	struct usbnet *usbdev;
+
+	struct workqueue_struct *workqueue;
+	struct delayed_work stats_work;
+	struct work_struct work;
+	struct mutex command_lock;
+	struct mutex stats_lock;
+
+	struct iw_statistics iwstats;
+	struct iw_statistics privstats;
+	int  nick_len;
+	char nick[32];
+
+	int caps;
+	int mode;
+	int radio_on;
+
+	/* general encryption stuff */
+	int privacy;
+
+	/* wep stuff */
+	int  wep_def_key;
+	int  wep_enabled;
+	char wep_keys[4][13];
+	int  wep_key_len[4];
+
+	/* wpa stuff */
+	int wpa_enabled;
+	int wpa_version;
+	int wpa_keymgmt;
+	int wpa_authalg;
+	int wpa_ie_len;
+	u8 *wpa_ie;
+	int wpa_cipher_pair;
+	int wpa_cipher_group;
+	struct NDIS_802_11_KEY wpa_keys[4];
+};
+
+
+static const int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+				2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+
+static const int rates_80211g[8] = { 6, 9, 12, 18, 24, 36, 48, 54 };
+
+static const int bcm4320_power_output[4] = { 25, 50, 75, 100 };
+
+static const unsigned char zero_bssid[ETH_ALEN] = {0,};
+
+
+static inline struct rndis_wext_private *get_rndis_wext_priv(struct usbnet *dev)
+{
+	return (struct rndis_wext_private *)dev->driver_priv;
+}
+
+
+static inline u32 get_bcm4320_power(struct rndis_wext_private *priv)
+{
+	return BCM4320_DEFAULT_TXPOWER;
+}
+
+
+/* translate error code */
+static int rndis_error_status(__le32 rndis_status)
+{
+	int ret = -EINVAL;
+	switch (rndis_status) {
+	case RNDIS_STATUS_SUCCESS:
+		ret = 0;
+		break;
+	case RNDIS_STATUS_FAILURE:
+	case RNDIS_STATUS_INVALID_DATA:
+		ret = -EINVAL;
+		break;
+	case RNDIS_STATUS_NOT_SUPPORTED:
+		ret = -EOPNOTSUPP;
+		break;
+	case RNDIS_STATUS_ADAPTER_NOT_READY:
+	case RNDIS_STATUS_ADAPTER_NOT_OPEN:
+		ret = -EBUSY;
+		break;
+	}
+	return ret;
+}
+
+
+static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_query	*get;
+		struct rndis_query_c	*get_c;
+	} u;
+	int ret, buflen;
+
+	buflen = *len + sizeof(*u.get);
+	if (buflen < CONTROL_BUFFER_SIZE)
+		buflen = CONTROL_BUFFER_SIZE;
+	u.buf = kmalloc(buflen, GFP_KERNEL);
+	if (!u.buf)
+		return -ENOMEM;
+	memset(u.get, 0, sizeof *u.get);
+	u.get->msg_type = RNDIS_MSG_QUERY;
+	u.get->msg_len = ccpu2(sizeof *u.get);
+	u.get->oid = oid;
+
+	mutex_lock(&priv->command_lock);
+	ret = rndis_command(dev, u.header);
+	mutex_unlock(&priv->command_lock);
+
+	if (ret == 0) {
+		ret = le32_to_cpu(u.get_c->len);
+		*len = (*len > ret) ? ret : *len;
+		memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len);
+		ret = rndis_error_status(u.get_c->status);
+	}
+
+	kfree(u.buf);
+	return ret;
+}
+
+
+static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	union {
+		void			*buf;
+		struct rndis_msg_hdr	*header;
+		struct rndis_set	*set;
+		struct rndis_set_c	*set_c;
+	} u;
+	int ret, buflen;
+
+	buflen = len + sizeof(*u.set);
+	if (buflen < CONTROL_BUFFER_SIZE)
+		buflen = CONTROL_BUFFER_SIZE;
+	u.buf = kmalloc(buflen, GFP_KERNEL);
+	if (!u.buf)
+		return -ENOMEM;
+
+	memset(u.set, 0, sizeof *u.set);
+	u.set->msg_type = RNDIS_MSG_SET;
+	u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len);
+	u.set->oid = oid;
+	u.set->len = cpu_to_le32(len);
+	u.set->offset = ccpu2(sizeof(*u.set) - 8);
+	u.set->handle = ccpu2(0);
+	memcpy(u.buf + sizeof(*u.set), data, len);
+
+	mutex_lock(&priv->command_lock);
+	ret = rndis_command(dev, u.header);
+	mutex_unlock(&priv->command_lock);
+
+	if (ret == 0)
+		ret = rndis_error_status(u.set_c->status);
+
+	kfree(u.buf);
+	return ret;
+}
+
+
+/*
+ * Specs say that we can only set config parameters only soon after device
+ * initialization.
+ *   value_type: 0 = u32, 2 = unicode string
+ */
+static int rndis_set_config_parameter(struct usbnet *dev, char *param,
+						int value_type, void *value)
+{
+	struct RNDIS_CONFIG_PARAMETER_INFOBUFFER *infobuf;
+	int value_len, info_len, param_len, ret, i;
+	__le16 *unibuf;
+	__le32 *dst_value;
+
+	if (value_type == 0)
+		value_len = sizeof(__le32);
+	else if (value_type == 2)
+		value_len = strlen(value) * sizeof(__le16);
+	else
+		return -EINVAL;
+
+	param_len = strlen(param) * sizeof(__le16);
+	info_len = sizeof(*infobuf) + param_len + value_len;
+
+	infobuf = kmalloc(info_len + 12, GFP_KERNEL);
+	if (!infobuf)
+		return -ENOMEM;
+
+#ifdef DEBUG
+	/* extra 12 bytes are for padding (debug output) */
+	memset(infobuf, 0xCC, info_len + 12);
+#endif
+
+	if (value_type == 2)
+		devdbg(dev, "setting config parameter: %s, value: %s",
+						param, (u8 *)value);
+	else
+		devdbg(dev, "setting config parameter: %s, value: %d",
+						param, *(u32 *)value);
+
+	infobuf->ParameterNameOffset = cpu_to_le32(sizeof(*infobuf));
+	infobuf->ParameterNameLength = cpu_to_le32(param_len);
+	infobuf->ParameterType = cpu_to_le32(value_type);
+	infobuf->ParameterValueOffset = cpu_to_le32(sizeof(*infobuf) +
+								param_len);
+	infobuf->ParameterValueLength = cpu_to_le32(value_len);
+
+	/* simple string to unicode string conversion */
+	unibuf = (void *)infobuf + sizeof(*infobuf);
+	for (i = 0; i < param_len / sizeof(__le16); i++)
+		unibuf[i] = cpu_to_le16(param[i]);
+
+	if (value_type == 2) {
+		unibuf = (void *)infobuf + sizeof(*infobuf) + param_len;
+		for (i = 0; i < value_len / sizeof(__le16); i++)
+			unibuf[i] = cpu_to_le16(((u8 *)value)[i]);
+	} else {
+		dst_value = (void *)infobuf + sizeof(*infobuf) + param_len;
+		*dst_value = cpu_to_le32(*(u32 *)value);
+	}
+
+#ifdef DEBUG
+	devdbg(dev, "info buffer (len: %d):", info_len);
+	for (i = 0; i < info_len; i += 12) {
+		u32 *tmp = (u32 *)((u8 *)infobuf + i);
+		devdbg(dev, "%08X:%08X:%08X",
+			cpu_to_be32(tmp[0]),
+			cpu_to_be32(tmp[1]),
+			cpu_to_be32(tmp[2]));
+	}
+#endif
+
+	ret = rndis_set_oid(dev, OID_GEN_RNDIS_CONFIG_PARAMETER,
+							infobuf, info_len);
+	if (ret != 0)
+		devdbg(dev, "setting rndis config parameter failed, %d.", ret);
+
+	kfree(infobuf);
+	return ret;
+}
+
+static inline int rndis_set_config_parameter_str(struct usbnet *dev,
+						char *param, char *value)
+{
+	return(rndis_set_config_parameter(dev, param, 2, value));
+}
+
+static inline int rndis_set_config_parameter_u32(struct usbnet *dev,
+						char *param, u32 value)
+{
+	return(rndis_set_config_parameter(dev, param, 0, &value));
+}
+
+
+/*
+ * data conversion functions
+ */
+static inline int level_to_qual(int level)
+{
+	int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE);
+	return qual >= 0 ? (qual <= 100 ? qual : 100) : 0;
+}
+
+
+static inline void dsconfig_to_freq(unsigned int dsconfig, struct iw_freq *freq)
+{
+	freq->e = 0;
+	freq->i = 0;
+	freq->flags = 0;
+
+	/* see comment in wireless.h above the "struct iw_freq"
+	 * definition for an explanation of this if
+	 * NOTE: 1000000 is due to the kHz
+	 */
+	if (dsconfig > 1000000) {
+		freq->m = dsconfig / 10;
+		freq->e = 1;
+	} else
+		freq->m = dsconfig;
+
+	/* convert from kHz to Hz */
+	freq->e += 3;
+}
+
+
+static inline int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)
+{
+	if (freq->m < 1000 && freq->e == 0) {
+		if (freq->m >= 1 &&
+			freq->m <= (sizeof(freq_chan) / sizeof(freq_chan[0])))
+			*dsconfig = freq_chan[freq->m - 1] * 1000;
+		else
+			return -1;
+	} else {
+		int i;
+		*dsconfig = freq->m;
+		for (i = freq->e; i > 0; i--)
+			*dsconfig *= 10;
+		*dsconfig /= 1000;
+	}
+
+	return 0;
+}
+
+
+/*
+ * common functions
+ */
+static void set_default_iw_params(struct usbnet *dev)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	__le32 tmp;
+
+	priv->wpa_keymgmt = 0;
+	priv->wpa_version = 0;
+
+	tmp = cpu_to_le32(Ndis802_11Infrastructure);
+	rndis_set_oid(dev, OID_802_11_INFRASTRUCTURE_MODE, &tmp, sizeof(tmp));
+	tmp = cpu_to_le32(Ndis802_11EncryptionDisabled);
+	rndis_set_oid(dev, OID_802_11_ENCRYPTION_STATUS, &tmp, sizeof(tmp));
+	tmp = cpu_to_le32(Ndis802_11AuthModeOpen);
+	rndis_set_oid(dev, OID_802_11_AUTHENTICATION_MODE, &tmp, sizeof(tmp));
+	tmp = cpu_to_le32(Ndis802_11PrivFilter8021xWEP);
+	rndis_set_oid(dev, OID_802_11_PRIVACY_FILTER, &tmp, sizeof(tmp));
+}
+
+
+static int get_essid(struct usbnet *usbdev, struct NDIS_802_11_SSID *ssid)
+{
+	int ret, len;
+
+	len = sizeof(*ssid);
+	ret = rndis_query_oid(usbdev, OID_802_11_SSID, ssid, &len);
+
+	if (ret != 0)
+		ssid->SsidLength = 0;
+
+#ifdef DEBUG
+	{
+		unsigned char tmp[NDIS_802_11_LENGTH_SSID + 1];
+
+		memcpy(tmp, ssid->Ssid, le32_to_cpu(ssid->SsidLength));
+		tmp[le32_to_cpu(ssid->SsidLength)] = 0;
+		devdbg(usbdev, "get_essid: '%s', ret: %d", tmp, ret);
+	}
+#endif
+	return ret;
+}
+
+
+static int set_essid(struct usbnet *usbdev, struct NDIS_802_11_SSID *ssid)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int ret;
+
+	ret = rndis_set_oid(usbdev, OID_802_11_SSID, ssid, sizeof(*ssid));
+	if (ret == 0) {
+		priv->radio_on = 1;
+		devdbg(usbdev, "set_essid: radio_on = 1");
+	}
+
+	return ret;
+}
+
+
+static int get_bssid(struct usbnet *usbdev, unsigned char bssid[ETH_ALEN])
+{
+	int ret, len;
+
+	len = ETH_ALEN;
+	ret = rndis_query_oid(usbdev, OID_802_11_BSSID, bssid, &len);
+
+	if (ret != 0)
+		memset(bssid, 0, ETH_ALEN);
+
+	return ret;
+}
+
+
+static int is_associated(struct usbnet *usbdev)
+{
+	unsigned char bssid[ETH_ALEN];
+	int ret;
+
+	ret = get_bssid(usbdev, bssid);
+
+	return(ret == 0 && memcmp(bssid, zero_bssid, ETH_ALEN) != 0);
+}
+
+
+static int disassociate(struct usbnet *usbdev, int reset_ssid)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	struct NDIS_802_11_SSID ssid;
+	int i, ret = 0;
+
+	if (priv->radio_on) {
+		ret = rndis_set_oid(usbdev, OID_802_11_DISASSOCIATE, NULL, 0);
+		if (ret == 0) {
+			priv->radio_on = 0;
+			devdbg(usbdev, "disassociate: radio_on = 0");
+
+			if (reset_ssid)
+				msleep(100);
+		}
+	}
+
+	/* disassociate causes radio to be turned off; if reset_ssid
+	 * is given, set ssid to random to enable radio */
+	if (reset_ssid) {
+		ssid.SsidLength = cpu_to_le32(sizeof(ssid.Ssid));
+		get_random_bytes(&ssid.Ssid[2], sizeof(ssid.Ssid)-2);
+		ssid.Ssid[0] = 0x01;
+		ssid.Ssid[1] = 0xff;
+		for (i = 2; i < sizeof(ssid.Ssid); i++)
+			ssid.Ssid[i] = 0x1 + (ssid.Ssid[i] * 0xfe / 0xff);
+		ret = set_essid(usbdev, &ssid);
+	}
+	return ret;
+}
+
+
+static int deauthenticate(struct usbnet *usbdev)
+{
+	int ret;
+
+	ret = disassociate(usbdev, 1);
+	set_default_iw_params(usbdev);
+	return ret;
+}
+
+
+static int add_wep_key(struct usbnet *usbdev, char *key, int keylength,
+    int index)
+{
+	struct NDIS_802_11_WEP *wepkey;
+	int ret;
+
+	/* get memory for max size wep key */
+	wepkey = kmalloc(sizeof(*wepkey) + 13, GFP_KERNEL);
+	if (!wepkey)
+		return -ENOMEM;
+
+	memcpy(wepkey->KeyMaterial, key, keylength);
+	wepkey->Length = cpu_to_le32(sizeof(*wepkey) + keylength);
+	wepkey->KeyIndex = cpu_to_le32(index);
+	if (index == 0)
+		wepkey->KeyIndex = cpu_to_le32(1 << 31);
+	wepkey->KeyLength = cpu_to_le32(keylength);
+	ret = rndis_set_oid(usbdev, OID_802_11_ADD_WEP, wepkey,
+			sizeof(*wepkey) + keylength);
+
+	kfree(wepkey);
+	return ret;
+}
+
+
+/*
+ * wireless extension handlers
+ */
+
+static int rndis_iw_commit(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	/* dummy op */
+	return 0;
+}
+
+
+static int rndis_iw_get_range(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_range *range = (struct iw_range *)extra;
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int len, ret, i, j, num, has_80211g_rates;
+	u8 rates[8];
+	__le32 tx_power;
+
+	devdbg(usbdev, "SIOCGIWRANGE");
+
+	/* clear iw_range struct */
+	memset(range, 0, sizeof(*range));
+	wrqu->data.length = sizeof(*range);
+
+	range->txpower_capa = IW_TXPOW_MWATT;
+	range->num_txpower = 1;
+	if (priv->caps & CAP_SUPPORT_TXPOWER) {
+		len = sizeof(tx_power);
+		ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+						&tx_power, &len);
+		if (ret == 0 && le32_to_cpu(tx_power) != 0xFF)
+			range->txpower[0] = le32_to_cpu(tx_power);
+		else
+			range->txpower[0] = get_bcm4320_power(priv);
+	} else
+		range->txpower[0] = get_bcm4320_power(priv);
+
+	len = sizeof(rates);
+	ret = rndis_query_oid(usbdev, OID_802_11_SUPPORTED_RATES, &rates,
+								&len);
+	has_80211g_rates = 0;
+	if (ret == 0) {
+		j = 0;
+		for (i = 0; i < len; i++) {
+			if (rates[i] == 0)
+				break;
+			range->bitrate[j] = (rates[i] & 0x7f) * 500000;
+			/* check for non 802.11b rates */
+			if (range->bitrate[j] == 6000000 ||
+				range->bitrate[j] == 9000000 ||
+				(range->bitrate[j] >= 12000000 &&
+				range->bitrate[j] != 22000000))
+				has_80211g_rates = 1;
+			j++;
+		}
+		range->num_bitrates = j;
+	} else
+		range->num_bitrates = 0;
+
+	/* fill in 802.11g rates */
+	if (has_80211g_rates) {
+		num = range->num_bitrates;
+		for (i = 0; i < sizeof(rates_80211g); i++) {
+			for (j = 0; j < num; j++) {
+				if (range->bitrate[j] ==
+					rates_80211g[i] * 1000000)
+					break;
+			}
+			if (j == num)
+				range->bitrate[range->num_bitrates++] =
+					rates_80211g[i] * 1000000;
+			if (range->num_bitrates == IW_MAX_BITRATES)
+				break;
+		}
+
+		/* estimated max real througput in bps */
+		range->throughput = 54 * 1000 * 1000 / 2;
+	} else {
+		/* estimated max real througput in bps */
+		range->throughput = 11 * 1000 * 1000 / 2;
+	}
+
+	range->num_channels = (sizeof(freq_chan)/sizeof(freq_chan[0]));
+
+	for (i = 0; i < (sizeof(freq_chan)/sizeof(freq_chan[0])) &&
+			i < IW_MAX_FREQUENCIES; i++) {
+		range->freq[i].i = i + 1;
+		range->freq[i].m = freq_chan[i] * 100000;
+		range->freq[i].e = 1;
+	}
+	range->num_frequency = i;
+
+	range->min_rts = 0;
+	range->max_rts = 2347;
+	range->min_frag = 256;
+	range->max_frag = 2346;
+
+	/* Setup max_qual as ndiswrapper does in iw_ndis.c */
+	range->max_qual.qual = 100;
+	range->max_qual.level = 154;
+	range->max_qual.updated = IW_QUAL_QUAL_UPDATED
+				| IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_NOISE_INVALID;
+
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = WIRELESS_EXT;
+
+	range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+			IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+	return 0;
+}
+
+
+static int rndis_iw_get_name(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	strcpy(wrqu->name, priv->name);
+	return 0;
+}
+
+
+static int rndis_iw_set_essid(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
+{
+	struct NDIS_802_11_SSID ssid;
+	int length = wrqu->essid.length;
+	struct usbnet *usbdev = dev->priv;
+
+	devdbg(usbdev, "SIOCSIWESSID: [flags:%d,len:%d] '%.32s'",
+		wrqu->essid.flags, wrqu->essid.length, essid);
+
+	if (length > NDIS_802_11_LENGTH_SSID)
+		length = NDIS_802_11_LENGTH_SSID;
+
+	ssid.SsidLength = cpu_to_le32(length);
+	if (length > 0) {
+		memcpy(ssid.Ssid, essid, length);
+	} else {
+		memset(ssid.Ssid, 0, NDIS_802_11_LENGTH_SSID);
+	}
+
+	if (!wrqu->essid.flags || length == 0)
+		return disassociate(usbdev, 1);
+	else
+		return set_essid(usbdev, &ssid);
+}
+
+
+static int rndis_iw_get_essid(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
+{
+	struct NDIS_802_11_SSID ssid;
+	struct usbnet *usbdev = dev->priv;
+	int ret;
+
+	ret = get_essid(usbdev, &ssid);
+
+	if (ret == 0 && ssid.SsidLength) {
+		wrqu->essid.flags = 1;
+		wrqu->essid.length = le32_to_cpu(ssid.SsidLength);
+		memcpy(essid, ssid.Ssid, wrqu->essid.length);
+		essid[wrqu->essid.length] = 0;
+	} else {
+		memset(essid, 0, sizeof(NDIS_802_11_LENGTH_SSID));
+		wrqu->essid.flags = 0;
+		wrqu->essid.length = 0;
+	}
+	devdbg(usbdev, "SIOCGIWESSID: %s", essid);
+	return ret;
+}
+
+
+static int rndis_iw_get_bssid(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	unsigned char bssid[ETH_ALEN];
+	int ret;
+
+	ret = get_bssid(usbdev, bssid);
+
+	if (ret == 0)
+		devdbg(usbdev, "SIOCGIWAP: %02x:%02x:%02x:%02x:%02x:%02x",
+			bssid[0], bssid[1], bssid[2],
+			bssid[3], bssid[4], bssid[5]);
+	else
+		devdbg(usbdev, "SIOCGIWAP: <not associated>");
+
+	wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(wrqu->ap_addr.sa_data, bssid, ETH_ALEN);
+
+	return ret;
+}
+
+
+static int rndis_iw_set_bssid(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	u8 *bssid = (u8 *)wrqu->ap_addr.sa_data;
+
+	devdbg(usbdev, "SIOCSIWAP: %02x:%02x:%02x:%02x:%02x:%02x",
+				bssid[0], bssid[1], bssid[2],
+				bssid[3], bssid[4], bssid[5]);
+
+	return rndis_set_oid(usbdev, OID_802_11_BSSID, bssid, ETH_ALEN);
+}
+
+
+static int rndis_iw_set_auth(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_param *p = &wrqu->param;
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int ret = -ENOTSUPP;
+	int auth = Ndis802_11AuthModeOpen;
+	int enc = Ndis802_11EncryptionDisabled;
+	__le32 tmp;
+
+	switch (p->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+		devdbg(usbdev, "SIOCSIWAUTH: WPA_VERSION, %08x", p->value);
+		priv->wpa_version |= p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_CIPHER_PAIRWISE:
+		devdbg(usbdev, "SIOCSIWAUTH: CIPHER_PAIRWISE, %08x", p->value);
+		priv->wpa_cipher_pair = p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_CIPHER_GROUP:
+		devdbg(usbdev, "SIOCSIWAUTH: CIPHER_GROUP, %08x", p->value);
+		priv->wpa_cipher_group = p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_KEY_MGMT:
+		devdbg(usbdev, "SIOCSIWAUTH: KEY_MGMT, %08x", p->value);
+		priv->wpa_keymgmt = p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_TKIP_COUNTERMEASURES:
+		devdbg(usbdev, "SIOCSIWAUTH: TKIP_COUNTERMEASURES, %08x",
+								p->value);
+		ret = 0;
+		break;
+
+	case IW_AUTH_DROP_UNENCRYPTED:
+		devdbg(usbdev, "SIOCSIWAUTH: DROP_UNENCRYPTED, %08x", p->value);
+		ret = 0;
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		devdbg(usbdev, "SIOCSIWAUTH: 80211_AUTH_ALG, %08x", p->value);
+		priv->wpa_authalg = p->value;
+		ret = 0;
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		devdbg(usbdev, "SIOCSIWAUTH: WPA_ENABLED, %08x", p->value);
+		if (p->value) {
+			deauthenticate(usbdev);
+			priv->wpa_version &= ~IW_AUTH_WPA_VERSION_DISABLED;
+		} else
+			priv->wpa_version |= IW_AUTH_WPA_VERSION_DISABLED;
+		ret = 0;
+		break;
+
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+		devdbg(usbdev, "SIOCSIWAUTH: RX_UNENCRYPTED_EAPOL, %08x",
+								p->value);
+		ret = 0;
+		break;
+
+	case IW_AUTH_ROAMING_CONTROL:
+		devdbg(usbdev, "SIOCSIWAUTH: ROAMING_CONTROL, %08x", p->value);
+		ret = 0;
+		break;
+
+	case IW_AUTH_PRIVACY_INVOKED:
+		devdbg(usbdev, "SIOCSIWAUTH: PRIVACY_INVOKED, %08x", p->value);
+
+		priv->privacy = p->value;
+
+		if (priv->privacy) {
+			switch (priv->wpa_keymgmt) {
+			case IW_AUTH_KEY_MGMT_PSK:
+				if (priv->wpa_version == 0) {
+					auth = Ndis802_11AuthModeShared;
+					enc  = Ndis802_11Encryption1Enabled;
+				} else if (priv->wpa_version ==
+						IW_AUTH_WPA_VERSION_WPA) {
+					auth = Ndis802_11AuthModeWPAPSK;
+					enc  = Ndis802_11Encryption2Enabled;
+				} else if (priv->wpa_version ==
+						IW_AUTH_WPA_VERSION_WPA2) {
+					auth = Ndis802_11AuthModeWPA2PSK;
+					enc  = Ndis802_11Encryption3Enabled;
+				} else {
+					devdbg(usbdev, "weird wpa_version %08x",
+							priv->wpa_version);
+					auth = Ndis802_11AuthModeWPA2PSK;
+					enc  = Ndis802_11Encryption3Enabled;
+				}
+				break;
+
+			case IW_AUTH_KEY_MGMT_802_1X:
+				if (priv->wpa_version == 0) {
+					auth = Ndis802_11AuthModeShared;
+					enc  = Ndis802_11Encryption1Enabled;
+				} else if (priv->wpa_version ==
+						IW_AUTH_WPA_VERSION_WPA) {
+					auth = Ndis802_11AuthModeWPA;
+					enc  = Ndis802_11Encryption2Enabled;
+				} else if (priv->wpa_version ==
+						IW_AUTH_WPA_VERSION_WPA2) {
+					auth = Ndis802_11AuthModeWPA2;
+					enc  = Ndis802_11Encryption3Enabled;
+				} else {
+					devdbg(usbdev, "weird wpa_version %08x",
+							priv->wpa_version);
+					auth = Ndis802_11AuthModeWPA2;
+					enc  = Ndis802_11Encryption3Enabled;
+				}
+				break;
+			}
+		} else {
+			auth = Ndis802_11AuthModeOpen;
+			enc  = Ndis802_11EncryptionDisabled;
+		}
+		tmp = cpu_to_le32(auth);
+		rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp,
+								sizeof(tmp));
+		tmp = cpu_to_le32(enc);
+		rndis_set_oid(usbdev, OID_802_11_ENCRYPTION_STATUS, &tmp,
+								sizeof(tmp));
+		ret = 0;
+		break;
+
+	default:
+		devdbg(usbdev, "SIOCSIWAUTH: UKNOWN  %08x, %08x",
+			p->flags & IW_AUTH_INDEX, p->value);
+	}
+	return ret;
+}
+
+
+static int rndis_iw_get_mode(struct net_device *dev,
+				struct iw_request_info *info,
+				union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	switch (priv->mode) {
+	case Ndis802_11IBSS:
+		wrqu->mode = IW_MODE_ADHOC;
+		break;
+	case Ndis802_11Infrastructure:
+		wrqu->mode = IW_MODE_INFRA;
+		break;
+	/*case Ndis802_11AutoUnknown:*/
+	default:
+		wrqu->mode = IW_MODE_AUTO;
+		break;
+	}
+	devdbg(usbdev, "SIOCGIWMODE: %08x", wrqu->mode);
+	return 0;
+}
+
+
+static int rndis_iw_set_mode(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int i, ret = -EINVAL;
+	__le32 tmp;
+
+	devdbg(usbdev, "SIOCSIWMODE: %08x", wrqu->mode);
+
+	switch (wrqu->mode) {
+	case IW_MODE_ADHOC:
+		priv->mode = Ndis802_11IBSS;
+		break;
+
+	case IW_MODE_INFRA:
+		priv->mode = Ndis802_11Infrastructure;
+		break;
+
+	/*case IW_MODE_AUTO:*/
+	default:
+		priv->mode = Ndis802_11AutoUnknown;
+		break;
+	}
+	tmp = cpu_to_le32(priv->mode);
+	ret = rndis_set_oid(usbdev, OID_802_11_INFRASTRUCTURE_MODE, &tmp,
+								sizeof(tmp));
+	if (ret != 0)
+		return ret;
+
+	/* NDIS drivers clear keys when infrastructure mode is
+	 * changed. But Linux tools assume otherwise. So set the
+	 * keys */
+	if (priv->wpa_keymgmt == 0 ||
+		priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) {
+		for (i = 0; i < 4; i++) {
+			if (priv->wep_key_len[i] > 0)
+				add_wep_key(usbdev, priv->wep_keys[i],
+						priv->wep_key_len[i], i);
+		}
+	}
+
+	return ret;
+}
+
+
+static int rndis_iw_set_encode(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_point *param = &wrqu->encoding;
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int	ret = 0;
+	int	index;
+	int	auth =  Ndis802_11AuthModeOpen;
+	int	enc = Ndis802_11EncryptionDisabled;
+	__le32	tmp;
+
+	devdbg(usbdev, "SIOCSIWENCODE: %08x, %08x",
+		param->flags, param->length);
+
+	index = (param->flags & IW_ENCODE_INDEX) - 1;
+
+	if (param->length > 0) {
+		int current_index = priv->wep_def_key;
+		/* Check the size of the key */
+		if (param->length > 13)
+			return -EINVAL;
+		/* Check the index (none -> use current) */
+		if (index < 0 || index >= 4)
+			index = current_index;
+		else
+			priv->wep_def_key = index;
+		/* Set the length */
+		if (param->length > 5)
+			priv->wep_key_len[index] = 13;
+		else
+			if (param->length > 0)
+				priv->wep_key_len[index] = 5;
+			else
+				/* Disable the key */
+				priv->wep_key_len[index] = 0;
+		/* Check if the key is not marked as invalid */
+		if (!(param->flags & IW_ENCODE_NOKEY)) {
+			/* Cleanup */
+			memset(priv->wep_keys[index], 0, 13);
+			/* Copy the key in the driver */
+			memcpy(priv->wep_keys[index], extra, param->length);
+		}
+		/* WE specify that if a valid key is set, encryption
+		 * should be enabled (user may turn it off later)
+		 * This is also how "iwconfig ethX key on" works
+		 */
+		if (index == current_index && priv->wep_key_len[index] > 0)
+			priv->wep_enabled = 1;
+	} else {
+		if (index >= 0 && index < 4)
+			priv->wep_def_key = index;
+		else
+			/* Don't complain if only change the mode */
+			if (!param->flags & IW_ENCODE_MODE)
+				return -EINVAL;
+	}
+
+	if (index >= 0 && index < 4) {
+		add_wep_key(usbdev, priv->wep_keys[index],
+				priv->wep_key_len[index], index);
+	}
+
+
+	/* Read the flags */
+	if (param->flags & IW_ENCODE_DISABLED) {
+		priv->wep_enabled = 0;
+	} else {
+		priv->wep_enabled = 1;
+	}
+
+	if (param->flags & IW_ENCODE_RESTRICTED)
+		auth = Ndis802_11AuthModeShared;
+	if (param->flags & IW_ENCODE_OPEN)
+		auth = Ndis802_11AuthModeOpen;
+
+	if (priv->wep_enabled) {
+		if (param->flags & IW_ENCODE_MODE)
+			enc = Ndis802_11WEPEnabled;
+	} else
+		enc = Ndis802_11WEPDisabled;
+
+	tmp = cpu_to_le32(auth);
+	rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp,
+			sizeof(tmp));
+	tmp = cpu_to_le32(enc);
+	rndis_set_oid(usbdev, OID_802_11_ENCRYPTION_STATUS, &tmp,
+			sizeof(tmp));
+
+	return ret;
+}
+
+
+static int rndis_iw_set_encode_ext(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_encode_ext *ext = (void *)extra;
+	struct usbnet *usbdev = dev->priv;
+	int ret = -EINVAL, keyindex;
+	struct NDIS_802_11_REMOVE_KEY rem_key;
+	struct NDIS_802_11_KEY add_key;
+
+	devdbg(usbdev, "SIOCSIWENCODEEXT: ext. flags:%08x, alg:%08x: keylen:%d",
+				ext->ext_flags, ext->alg, ext->key_len);
+	devdbg(usbdev, "                : enc. flags:%08x, length:%d",
+				wrqu->encoding.flags, wrqu->encoding.length);
+
+	keyindex = (wrqu->encoding.flags & IW_ENCODE_INDEX) - 1;
+
+	/* TX key */
+	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+		keyindex |= (1 << 31);
+
+	/* pairwise key */
+	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY))
+		keyindex |= (1 << 30);
+
+	if (wrqu->encoding.flags & IW_ENCODE_DISABLED) {
+		memset(&rem_key, 0, sizeof(rem_key));
+		rem_key.Length = cpu_to_le32(sizeof(rem_key));
+		rem_key.KeyIndex = cpu_to_le32(keyindex);
+		if (keyindex & (1 << 30)) {
+			memcpy(rem_key.Bssid, ext->addr.sa_data, ETH_ALEN);
+		} else {
+			memset(rem_key.Bssid, 0xff, ETH_ALEN);
+		}
+		ret = rndis_set_oid(usbdev, OID_802_11_REMOVE_KEY, &rem_key,
+							sizeof(rem_key));
+	} else {
+		if (ext->key_len > sizeof(add_key.KeyMaterial))
+			return -EINVAL;
+
+		memset(&add_key, 0, sizeof(add_key));
+		add_key.Length = cpu_to_le32(sizeof(add_key));
+
+		if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
+			memcpy(&add_key.KeyRSC, ext->rx_seq,
+					IW_ENCODE_SEQ_MAX_SIZE);
+			keyindex |= (1 << 29);
+		}
+
+		if (ext->ext_flags & IW_ENCODE_EXT_TX_SEQ_VALID) {
+			memcpy(&add_key.KeyRSC, ext->tx_seq,
+					IW_ENCODE_SEQ_MAX_SIZE);
+			keyindex |= (1 << 29);
+		}
+
+		add_key.KeyIndex = cpu_to_le32(keyindex);
+		add_key.KeyLength = cpu_to_le32(ext->key_len);
+		if (keyindex & (1 << 30)) {
+			memcpy(add_key.Bssid, ext->addr.sa_data, ETH_ALEN);
+		} else {
+			memset(add_key.Bssid, 0xff, ETH_ALEN);
+		}
+		if (ext->alg == IW_ENCODE_ALG_TKIP) {
+			memcpy(add_key.KeyMaterial, ext->key, 16);
+			memcpy(add_key.KeyMaterial + 16, ext->key + 24, 8);
+			memcpy(add_key.KeyMaterial + 24, ext->key + 16, 8);
+		} else {
+			memcpy(add_key.KeyMaterial, ext->key, ext->key_len);
+		}
+		/* kludge alert - breaking a race between ioctl and network
+		 * packet.  This delay is to prevent encryption being enabled
+		 * while the last authentication handshake packet is still
+		 * underway in the stack somewhere. If encryption is enabled
+		 * too early the AP will not see this last handshake packet and
+		 * association fails.
+		 *
+		 * also see:
+		 * http://lists.shmoo.com/pipermail/hostap/2006-September/\
+		 *						014157.html
+		 */
+		msleep(500);
+
+		ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &add_key,
+							sizeof(add_key));
+	}
+	return ret;
+}
+
+
+static int rndis_iw_set_scan(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct iw_param *param = &wrqu->param;
+	struct usbnet *usbdev = dev->priv;
+	union iwreq_data evt;
+	int ret = -EINVAL;
+	__le32 tmp;
+
+	devdbg(usbdev, "SIOCSIWSCAN");
+
+	if (param->flags == 0) {
+		tmp = ccpu2(1);
+		ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
+								sizeof(tmp));
+		evt.data.flags = 0;
+		evt.data.length = 0;
+		wireless_send_event(dev, SIOCGIWSCAN, &evt, NULL);
+	}
+	return ret;
+}
+
+
+static char *rndis_translate_scan(struct net_device *dev,
+    char *cev, char *end_buf, struct NDIS_WLAN_BSSID_EX *bssid)
+{
+#ifdef DEBUG
+	struct usbnet *usbdev = dev->priv;
+#endif
+	struct ieee80211_info_element *ie;
+	char *current_val;
+	int bssid_len, ie_len, i;
+	u32 beacon, atim;
+	struct iw_event iwe;
+	unsigned char sbuf[32];
+
+	bssid_len = le32_to_cpu(bssid->Length);
+
+	devdbg(usbdev, "BSSID %02x:%02x:%02x:%02x:%02x:%02x",
+			bssid->MacAddress[0], bssid->MacAddress[1],
+			bssid->MacAddress[2], bssid->MacAddress[3],
+			bssid->MacAddress[4], bssid->MacAddress[5]);
+	iwe.cmd = SIOCGIWAP;
+	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(iwe.u.ap_addr.sa_data, bssid->MacAddress, ETH_ALEN);
+	cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+	devdbg(usbdev, "SSID(%d) %s",
+		le32_to_cpu(bssid->Ssid.SsidLength),
+		bssid->Ssid.Ssid);
+	iwe.cmd = SIOCGIWESSID;
+	iwe.u.essid.length = le32_to_cpu(bssid->Ssid.SsidLength);
+	iwe.u.essid.flags = 1;
+	cev = iwe_stream_add_point(cev, end_buf, &iwe,
+						bssid->Ssid.Ssid);
+
+	devdbg(usbdev, "MODE %d",
+			le32_to_cpu(bssid->InfrastructureMode));
+	iwe.cmd = SIOCGIWMODE;
+	switch (le32_to_cpu(bssid->InfrastructureMode)) {
+	case Ndis802_11IBSS:
+		iwe.u.mode = IW_MODE_ADHOC;
+		break;
+	case Ndis802_11Infrastructure:
+		iwe.u.mode = IW_MODE_INFRA;
+		break;
+	/*case Ndis802_11AutoUnknown:*/
+	default:
+		iwe.u.mode = IW_MODE_AUTO;
+		break;
+	}
+	cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN);
+
+	devdbg(usbdev, "FREQ %d kHz",
+		le32_to_cpu(bssid->Configuration.DSConfig));
+	iwe.cmd = SIOCGIWFREQ;
+	dsconfig_to_freq(le32_to_cpu(bssid->Configuration.DSConfig),
+								&iwe.u.freq);
+	cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN);
+
+	devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->Rssi));
+	iwe.cmd = IWEVQUAL;
+	iwe.u.qual.qual  = level_to_qual(le32_to_cpu(bssid->Rssi));
+	iwe.u.qual.level = le32_to_cpu(bssid->Rssi);
+	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
+			| IW_QUAL_LEVEL_UPDATED
+			| IW_QUAL_NOISE_INVALID;
+	cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN);
+
+	devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->Privacy));
+	iwe.cmd = SIOCGIWENCODE;
+	iwe.u.data.length = 0;
+	if (le32_to_cpu(bssid->Privacy) == Ndis802_11PrivFilterAcceptAll)
+		iwe.u.data.flags = IW_ENCODE_DISABLED;
+	else
+		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+
+	cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL);
+
+	devdbg(usbdev, "RATES:");
+	current_val = cev + IW_EV_LCP_LEN;
+	iwe.cmd = SIOCGIWRATE;
+	for (i = 0; i < sizeof(bssid->SupportedRates); i++) {
+		if (bssid->SupportedRates[i] & 0x7f) {
+			iwe.u.bitrate.value =
+				((bssid->SupportedRates[i] & 0x7f) *
+				500000);
+			devdbg(usbdev, " %d", iwe.u.bitrate.value);
+			current_val = iwe_stream_add_value(cev,
+				current_val, end_buf, &iwe,
+				IW_EV_PARAM_LEN);
+		}
+	}
+
+	if ((current_val - cev) > IW_EV_LCP_LEN)
+		cev = current_val;
+
+	beacon = le32_to_cpu(bssid->Configuration.BeaconPeriod);
+	devdbg(usbdev, "BCN_INT %d", beacon);
+	iwe.cmd = IWEVCUSTOM;
+	snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon);
+	iwe.u.data.length = strlen(sbuf);
+	cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf);
+
+	atim = le32_to_cpu(bssid->Configuration.ATIMWindow);
+	devdbg(usbdev, "ATIM %d", atim);
+	iwe.cmd = IWEVCUSTOM;
+	snprintf(sbuf, sizeof(sbuf), "atim=%u", atim);
+	iwe.u.data.length = strlen(sbuf);
+	cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf);
+
+	ie = (void *)(bssid->IEs + sizeof(struct NDIS_802_11_FIXED_IEs));
+	ie_len = min(bssid_len - (int)sizeof(*bssid),
+					(int)le32_to_cpu(bssid->IELength));
+	ie_len -= sizeof(struct NDIS_802_11_FIXED_IEs);
+	while (ie_len >= sizeof(*ie) && sizeof(*ie) + ie->len <= ie_len) {
+		if ((ie->id == MFIE_TYPE_GENERIC && ie->len >= 4 &&
+				memcmp(ie->data, "\x00\x50\xf2\x01", 4) == 0) ||
+				ie->id == MFIE_TYPE_RSN) {
+			devdbg(usbdev, "IE: WPA%d",
+					(ie->id == MFIE_TYPE_RSN) ? 2 : 1);
+			iwe.cmd = IWEVGENIE;
+			iwe.u.data.length = min(ie->len + 2, MAX_WPA_IE_LEN);
+			cev = iwe_stream_add_point(cev, end_buf, &iwe,
+								   (u8 *)ie);
+		}
+
+		ie_len -= sizeof(*ie) + ie->len;
+		ie = (struct ieee80211_info_element *)&ie->data[ie->len];
+	}
+
+	return cev;
+}
+
+
+static int rndis_iw_get_scan(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	void *buf = NULL;
+	char *cev = extra;
+	struct NDIS_802_11_BSSID_LIST_EX *bssid_list;
+	struct NDIS_WLAN_BSSID_EX *bssid;
+	int ret = -EINVAL, len, count, bssid_len;
+
+	devdbg(usbdev, "SIOCGIWSCAN");
+
+	len = CONTROL_BUFFER_SIZE;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
+
+	if (ret != 0)
+		goto out;
+
+	bssid_list = buf;
+	bssid = bssid_list->Bssid;
+	bssid_len = le32_to_cpu(bssid->Length);
+	count = le32_to_cpu(bssid_list->NumberOfItems);
+	devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count);
+
+	while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
+		cev = rndis_translate_scan(dev, cev, extra + IW_SCAN_MAX_DATA,
+									bssid);
+		bssid = (void *)bssid + bssid_len;
+		bssid_len = le32_to_cpu(bssid->Length);
+		count--;
+	}
+
+out:
+	wrqu->data.length = cev - extra;
+	wrqu->data.flags = 0;
+	kfree(buf);
+	return ret;
+}
+
+
+static int rndis_iw_set_genie(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	int ret = 0;
+
+#ifdef DEBUG
+	int j;
+	u8 *gie = extra;
+	for (j = 0; j < wrqu->data.length; j += 8)
+		devdbg(usbdev,
+			"SIOCSIWGENIE %04x - "
+			"%02x %02x %02x %02x %02x %02x %02x %02x", j,
+			gie[j + 0], gie[j + 1], gie[j + 2], gie[j + 3],
+			gie[j + 4], gie[j + 5], gie[j + 6], gie[j + 7]);
+#endif
+	/* clear existing IEs */
+	if (priv->wpa_ie_len) {
+		kfree(priv->wpa_ie);
+		priv->wpa_ie_len = 0;
+	}
+
+	/* set new IEs */
+	priv->wpa_ie = kmalloc(wrqu->data.length, GFP_KERNEL);
+	if (priv->wpa_ie) {
+		priv->wpa_ie_len = wrqu->data.length;
+		memcpy(priv->wpa_ie, extra, priv->wpa_ie_len);
+	} else
+		ret = -ENOMEM;
+	return ret;
+}
+
+
+static int rndis_iw_get_genie(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	devdbg(usbdev, "SIOCGIWGENIE");
+
+	if (priv->wpa_ie_len == 0 || priv->wpa_ie == NULL) {
+		wrqu->data.length = 0;
+		return 0;
+	}
+
+	if (wrqu->data.length < priv->wpa_ie_len)
+		return -E2BIG;
+
+	wrqu->data.length = priv->wpa_ie_len;
+	memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
+
+	return 0;
+}
+
+
+static int rndis_iw_set_rts(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+	devdbg(usbdev, "SIOCSIWRTS");
+
+	tmp = cpu_to_le32(wrqu->rts.value);
+	return rndis_set_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp,
+								sizeof(tmp));
+}
+
+
+static int rndis_iw_get_rts(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+	int len, ret;
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, &len);
+	if (ret == 0) {
+		wrqu->rts.value = le32_to_cpu(tmp);
+		wrqu->rts.flags = 1;
+		wrqu->rts.disabled = 0;
+	}
+
+	devdbg(usbdev, "SIOCGIWRTS: %d", wrqu->rts.value);
+
+	return ret;
+}
+
+
+static int rndis_iw_set_frag(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+
+	devdbg(usbdev, "SIOCSIWFRAG");
+
+	tmp = cpu_to_le32(wrqu->frag.value);
+	return rndis_set_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp,
+								sizeof(tmp));
+}
+
+
+static int rndis_iw_get_frag(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+	int len, ret;
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp,
+									&len);
+	if (ret == 0) {
+		wrqu->frag.value = le32_to_cpu(tmp);
+		wrqu->frag.flags = 1;
+		wrqu->frag.disabled = 0;
+	}
+	devdbg(usbdev, "SIOCGIWFRAG: %d", wrqu->frag.value);
+	return ret;
+}
+
+
+static int rndis_iw_set_nick(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	devdbg(usbdev, "SIOCSIWNICK");
+
+	priv->nick_len = wrqu->data.length;
+	if (priv->nick_len > 32)
+		priv->nick_len = 32;
+
+	memcpy(priv->nick, extra, priv->nick_len);
+	return 0;
+}
+
+
+static int rndis_iw_get_nick(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	wrqu->data.flags = 1;
+	wrqu->data.length = priv->nick_len;
+	memcpy(extra, priv->nick, priv->nick_len);
+
+	devdbg(usbdev, "SIOCGIWNICK: '%s'", priv->nick);
+
+	return 0;
+}
+
+
+static int rndis_iw_set_freq(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct NDIS_802_11_CONFIGURATION config;
+	unsigned int dsconfig;
+	int len, ret;
+
+	/* this OID is valid only when not associated */
+	if (is_associated(usbdev))
+		return 0;
+
+	dsconfig = 0;
+	if (freq_to_dsconfig(&wrqu->freq, &dsconfig))
+		return -EINVAL;
+
+	len = sizeof(config);
+	ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
+	if (ret != 0) {
+		devdbg(usbdev, "SIOCSIWFREQ: querying configuration failed");
+		return 0;
+	}
+
+	config.DSConfig = cpu_to_le32(dsconfig);
+
+	devdbg(usbdev, "SIOCSIWFREQ: %d * 10^%d", wrqu->freq.m, wrqu->freq.e);
+	return rndis_set_oid(usbdev, OID_802_11_CONFIGURATION, &config,
+								sizeof(config));
+}
+
+
+static int rndis_iw_get_freq(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct NDIS_802_11_CONFIGURATION config;
+	int len, ret;
+
+	len = sizeof(config);
+	ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
+	if (ret == 0)
+		dsconfig_to_freq(le32_to_cpu(config.DSConfig), &wrqu->freq);
+
+	devdbg(usbdev, "SIOCGIWFREQ: %d", wrqu->freq.m);
+	return ret;
+}
+
+
+static int rndis_iw_get_txpower(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	__le32 tx_power;
+	int ret = 0, len;
+
+	if (priv->radio_on) {
+		if (priv->caps & CAP_SUPPORT_TXPOWER) {
+			len = sizeof(tx_power);
+			ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+							&tx_power, &len);
+			if (ret != 0)
+				return ret;
+		} else
+			/* fake incase not supported */
+			tx_power = cpu_to_le32(get_bcm4320_power(priv));
+
+		wrqu->txpower.flags = IW_TXPOW_MWATT;
+		wrqu->txpower.value = le32_to_cpu(tx_power);
+		wrqu->txpower.disabled = 0;
+	} else {
+		wrqu->txpower.flags = IW_TXPOW_MWATT;
+		wrqu->txpower.value = 0;
+		wrqu->txpower.disabled = 1;
+	}
+
+	devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value);
+
+	return ret;
+}
+
+
+static int rndis_iw_set_txpower(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	__le32 tx_power;
+	int ret = 0;
+
+	if (wrqu->txpower.disabled)
+		tx_power = 0;
+	else {
+		if (wrqu->txpower.flags == IW_TXPOW_MWATT)
+			tx_power = cpu_to_le32(wrqu->txpower.value);
+		else {
+			/* wrqu->txpower.flags == IW_TXPOW_DBM */
+			if (wrqu->txpower.value > 20)
+				tx_power = cpu_to_le32(128);
+			else if (wrqu->txpower.value < -43)
+				tx_power = cpu_to_le32(127);
+			else {
+				signed char tmp;
+				tmp = wrqu->txpower.value;
+				tmp = -12 - tmp;
+				tmp <<= 2;
+				tx_power = cpu_to_le32((unsigned char)tmp);
+			}
+		}
+	}
+
+	devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power));
+
+	if (le32_to_cpu(tx_power) != 0) {
+		if (priv->caps & CAP_SUPPORT_TXPOWER) {
+			/* turn radio on first */
+			if (!priv->radio_on)
+				disassociate(usbdev, 1);
+
+			ret = rndis_set_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+						&tx_power, sizeof(tx_power));
+			if (ret != 0)
+				ret = -EOPNOTSUPP;
+			return ret;
+		} else {
+			/* txpower unsupported, just turn radio on */
+			if (!priv->radio_on)
+				return disassociate(usbdev, 1);
+			return 0; /* all ready on */
+		}
+	}
+
+	/* tx_power == 0, turn off radio */
+	return disassociate(usbdev, 0);
+}
+
+
+static int rndis_iw_get_rate(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	__le32 tmp;
+	int ret, len;
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &tmp, &len);
+	if (ret == 0) {
+		wrqu->bitrate.value = le32_to_cpu(tmp) * 100;
+		wrqu->bitrate.disabled = 0;
+		wrqu->bitrate.flags = 1;
+	}
+	return ret;
+}
+
+
+static int rndis_iw_set_mlme(struct net_device *dev,
+    struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct iw_mlme *mlme = (struct iw_mlme *)extra;
+	unsigned char bssid[ETH_ALEN];
+
+	get_bssid(usbdev, bssid);
+
+	if (memcmp(bssid, mlme->addr.sa_data, ETH_ALEN))
+		return -EINVAL;
+
+	switch (mlme->cmd) {
+	case IW_MLME_DEAUTH:
+		return deauthenticate(usbdev);
+	case IW_MLME_DISASSOC:
+		return disassociate(usbdev, 1);
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+
+static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	mutex_lock(&priv->stats_lock);
+	memcpy(&priv->iwstats, &priv->privstats, sizeof(priv->iwstats));
+	mutex_unlock(&priv->stats_lock);
+
+	return &priv->iwstats;
+}
+
+
+#define IW_IOCTL(x) [(x) - SIOCSIWCOMMIT]
+static const iw_handler rndis_iw_handler[] =
+{
+	IW_IOCTL(SIOCSIWCOMMIT)    = rndis_iw_commit,
+	IW_IOCTL(SIOCGIWNAME)      = rndis_iw_get_name,
+	IW_IOCTL(SIOCSIWFREQ)      = rndis_iw_set_freq,
+	IW_IOCTL(SIOCGIWFREQ)      = rndis_iw_get_freq,
+	IW_IOCTL(SIOCSIWMODE)      = rndis_iw_set_mode,
+	IW_IOCTL(SIOCGIWMODE)      = rndis_iw_get_mode,
+	IW_IOCTL(SIOCGIWRANGE)     = rndis_iw_get_range,
+	IW_IOCTL(SIOCSIWAP)        = rndis_iw_set_bssid,
+	IW_IOCTL(SIOCGIWAP)        = rndis_iw_get_bssid,
+	IW_IOCTL(SIOCSIWSCAN)      = rndis_iw_set_scan,
+	IW_IOCTL(SIOCGIWSCAN)      = rndis_iw_get_scan,
+	IW_IOCTL(SIOCSIWESSID)     = rndis_iw_set_essid,
+	IW_IOCTL(SIOCGIWESSID)     = rndis_iw_get_essid,
+	IW_IOCTL(SIOCSIWNICKN)     = rndis_iw_set_nick,
+	IW_IOCTL(SIOCGIWNICKN)     = rndis_iw_get_nick,
+	IW_IOCTL(SIOCGIWRATE)      = rndis_iw_get_rate,
+	IW_IOCTL(SIOCSIWRTS)       = rndis_iw_set_rts,
+	IW_IOCTL(SIOCGIWRTS)       = rndis_iw_get_rts,
+	IW_IOCTL(SIOCSIWFRAG)      = rndis_iw_set_frag,
+	IW_IOCTL(SIOCGIWFRAG)      = rndis_iw_get_frag,
+	IW_IOCTL(SIOCSIWTXPOW)     = rndis_iw_set_txpower,
+	IW_IOCTL(SIOCGIWTXPOW)     = rndis_iw_get_txpower,
+	IW_IOCTL(SIOCSIWENCODE)    = rndis_iw_set_encode,
+	IW_IOCTL(SIOCSIWENCODEEXT) = rndis_iw_set_encode_ext,
+	IW_IOCTL(SIOCSIWAUTH)      = rndis_iw_set_auth,
+	IW_IOCTL(SIOCSIWGENIE)     = rndis_iw_set_genie,
+	IW_IOCTL(SIOCGIWGENIE)     = rndis_iw_get_genie,
+	IW_IOCTL(SIOCSIWMLME)      = rndis_iw_set_mlme,
+};
+
+static const iw_handler rndis_wext_private_handler[] = {
+};
+
+static const struct iw_priv_args rndis_wext_private_args[] = {
+};
+
+
+static const struct iw_handler_def rndis_iw_handlers = {
+	.num_standard = ARRAY_SIZE(rndis_iw_handler),
+	.num_private  = ARRAY_SIZE(rndis_wext_private_handler),
+	.num_private_args = ARRAY_SIZE(rndis_wext_private_args),
+	.standard = (iw_handler *)rndis_iw_handler,
+	.private  = (iw_handler *)rndis_wext_private_handler,
+	.private_args = (struct iw_priv_args *)rndis_wext_private_args,
+	.get_wireless_stats = rndis_get_wireless_stats,
+};
+
+
+static void rndis_wext_connect_event_work(struct work_struct *work)
+{
+	struct rndis_wext_private *priv =
+		container_of(work, struct rndis_wext_private, work);
+	struct usbnet *usbdev = priv->usbdev;
+	union iwreq_data evt;
+	unsigned char bssid[ETH_ALEN];
+	int ret;
+
+	ret = get_bssid(usbdev, bssid);
+
+	if (!ret) {
+		evt.data.flags = 0;
+		evt.data.length = 0;
+		memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN);
+		wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL);
+	}
+}
+
+static void rndis_wext_link_change(struct usbnet *dev, int state)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	union iwreq_data evt;
+
+	if (state) {
+		/* queue work to avoid recursive calls into rndis_command */
+		queue_work(priv->workqueue, &priv->work);
+	} else {
+		evt.data.flags = 0;
+		evt.data.length = 0;
+		memset(evt.ap_addr.sa_data, 0, ETH_ALEN);
+		wireless_send_event(dev->net, SIOCGIWAP, &evt, NULL);
+	}
+}
+
+static int rndis_wext_get_caps(struct usbnet *dev)
+{
+	struct {
+		__le32	num_items;
+		__le32	items[8];
+	} networks_supported;
+	int len, retval, i, n;
+	__le32 tx_power;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+
+	/* determine if supports setting txpower */
+	len = sizeof(tx_power);
+	retval = rndis_query_oid(dev, OID_802_11_TX_POWER_LEVEL, &tx_power,
+								&len);
+	if (retval == 0 && le32_to_cpu(tx_power) != 0xFF)
+		priv->caps |= CAP_SUPPORT_TXPOWER;
+
+	/* determine supported modes */
+	len = sizeof(networks_supported);
+	retval = rndis_query_oid(dev, OID_802_11_NETWORK_TYPES_SUPPORTED,
+						&networks_supported, &len);
+	if (retval >= 0) {
+		n = le32_to_cpu(networks_supported.num_items);
+		if (n > 8)
+			n = 8;
+		for (i = 0; i < n; i++) {
+			switch (le32_to_cpu(networks_supported.items[i])) {
+			case Ndis802_11FH:
+			case Ndis802_11DS:
+				priv->caps |= CAP_MODE_80211B;
+				break;
+			case Ndis802_11OFDM5:
+				priv->caps |= CAP_MODE_80211A;
+				break;
+			case Ndis802_11OFDM24:
+				priv->caps |= CAP_MODE_80211G;
+				break;
+			}
+		}
+		if (priv->caps & CAP_MODE_80211A)
+			strcat(priv->name, "a");
+		if (priv->caps & CAP_MODE_80211B)
+			strcat(priv->name, "b");
+		if (priv->caps & CAP_MODE_80211G)
+			strcat(priv->name, "g");
+	}
+
+	return retval;
+}
+
+
+#define STATS_UPDATE_JIFFIES (HZ * 2)
+static void rndis_update_wireless_stats(struct work_struct *work)
+{
+	struct rndis_wext_private *priv =
+		container_of(work, struct rndis_wext_private, stats_work.work);
+	struct usbnet *usbdev = priv->usbdev;
+	struct NDIS_802_11_STATS stats;
+	struct iw_statistics iwstats;
+	__le32 rssi;
+	int len, ret;
+
+	mutex_lock(&priv->stats_lock);
+	memcpy(&iwstats, &priv->privstats, sizeof(iwstats));
+	mutex_unlock(&priv->stats_lock);
+
+	/* only update stats when connected */
+	if (!is_associated(usbdev)) {
+		iwstats.qual.qual = 0;
+		iwstats.qual.level = 0;
+		iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+				| IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_NOISE_INVALID
+				| IW_QUAL_QUAL_INVALID
+				| IW_QUAL_LEVEL_INVALID;
+		goto end;
+	}
+
+	len = sizeof(rssi);
+	ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len);
+
+	devdbg(usbdev, "stats: OID_802_11_RSSI -> %d, rssi:%d", ret,
+							le32_to_cpu(rssi));
+	if (ret == 0) {
+		memset(&iwstats.qual, 0, sizeof(iwstats.qual));
+		iwstats.qual.qual  = level_to_qual(le32_to_cpu(rssi));
+		iwstats.qual.level = le32_to_cpu(rssi);
+		iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+				| IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_NOISE_INVALID;
+	}
+
+	len = sizeof(stats);
+	ret = rndis_query_oid(usbdev, OID_802_11_STATISTICS, &stats, &len);
+
+	devdbg(usbdev, "stats: OID_802_11_STATISTICS -> %d", ret);
+	if (ret == 0) {
+		memset(&iwstats.discard, 0, sizeof(iwstats.discard));
+		iwstats.discard.retries =
+			(u32)le64_to_cpu(stats.Retry) +
+			(u32)le64_to_cpu(stats.MultiRetry);
+		iwstats.discard.misc =
+			(u32)le64_to_cpu(stats.FcsErr) +
+			(u32)le64_to_cpu(stats.RtssFail) +
+			(u32)le64_to_cpu(stats.AckFail) +
+			(u32)le64_to_cpu(stats.FrameDup);
+		iwstats.discard.fragment =
+			(u32)le64_to_cpu(stats.RxFrag) +
+			(u32)le64_to_cpu(stats.RxMultiFrag);
+	}
+
+end:
+	mutex_lock(&priv->stats_lock);
+	memcpy(&priv->privstats, &iwstats, sizeof(iwstats));
+	mutex_unlock(&priv->stats_lock);
+
+	queue_delayed_work(priv->workqueue, &priv->stats_work,
+		round_jiffies_relative(STATS_UPDATE_JIFFIES));
+
+}
+
+static int rndis_wext_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct net_device *net = dev->net;
+	struct rndis_wext_private *priv;
+
+	/* allocate rndis private data */
+	priv = kmalloc(sizeof(struct rndis_wext_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev->driver_priv = priv;
+	memset(priv, 0, sizeof(*priv));
+	memset(priv->name, 0, sizeof(priv->name));
+	strcpy(priv->name, "IEEE802.11");
+	net->wireless_handlers = &rndis_iw_handlers;
+	priv->usbdev = dev;
+
+	mutex_init(&priv->command_lock);
+	mutex_init(&priv->stats_lock);
+
+	priv->iwstats.qual.qual = 0;
+	priv->iwstats.qual.level = 0;
+	priv->iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+					| IW_QUAL_LEVEL_UPDATED
+					| IW_QUAL_NOISE_INVALID
+					| IW_QUAL_QUAL_INVALID
+					| IW_QUAL_LEVEL_INVALID;
+
+	rndis_wext_get_caps(dev);
+	set_default_iw_params(dev);
+
+	/* turn radio on */
+	priv->radio_on = 1;
+	disassociate(dev, 1);
+
+	/* because rndis_command() sleeps we need to use workqueue */
+	priv->workqueue = create_singlethread_workqueue("rndis_wext");
+	INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
+	queue_delayed_work(priv->workqueue, &priv->stats_work,
+		round_jiffies_relative(STATS_UPDATE_JIFFIES));
+	INIT_WORK(&priv->work, rndis_wext_connect_event_work);
+
+	return 0;
+}
+
+static void rndis_wext_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+
+	/* turn radio off */
+	disassociate(dev, 0);
+
+	cancel_delayed_work_sync(&priv->stats_work);
+	cancel_work_sync(&priv->work);
+	flush_workqueue(priv->workqueue);
+	destroy_workqueue(priv->workqueue);
+
+	if (priv && priv->wpa_ie_len)
+		kfree(priv->wpa_ie);
+	kfree(priv);
+
+	rndis_unbind(dev, intf);
+}
+
+static int rndis_wext_reset(struct usbnet *dev)
+{
+	return deauthenticate(dev);
+}
+
+struct driver_info rndis_wext_info = {
+	.description =	"Wireless RNDIS device",
+	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.bind =		rndis_wext_bind,
+	.unbind =	rndis_wext_unbind,
+	.status =	rndis_status,
+	.rx_fixup =	rndis_rx_fixup,
+	.tx_fixup =	rndis_tx_fixup,
+	.reset =	rndis_wext_reset,
+	.link_change =	rndis_wext_link_change,
+};
diff --git a/drivers/net/usb/usbnet.h b/drivers/net/usb/usbnet.h
index 1fae434..83860a0 100644
--- a/drivers/net/usb/usbnet.h
+++ b/drivers/net/usb/usbnet.h
@@ -31,6 +31,7 @@ struct usbnet {
 	struct usb_interface	*intf;
 	struct driver_info	*driver_info;
 	const char		*driver_name;
+	void			*driver_priv;
 	wait_queue_head_t	*wait;
 	struct mutex		phy_mutex;
 	unsigned char		suspend_count;
@@ -109,6 +110,9 @@ struct driver_info {
 	/* fixup rx packet (strip framing) */
 	int	(*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
 
+	/* called when link state changes */
+	void    (*link_change)(struct usbnet *dev, int state);
+
 	/* fixup tx packet (add framing) */
 	struct sk_buff	*(*tx_fixup)(struct usbnet *dev,
 				struct sk_buff *skb, gfp_t flags);
-- 
1.5.2.5


^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [PATCH 8/8] [PATCH] Use wlan device name for RNDIS wireless devices
       [not found] ` <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
                     ` (2 preceding siblings ...)
  2007-12-22 21:51   ` [PATCH 5/8] Fix rndis packet filter flags Bjorge Dijkstra
@ 2007-12-22 21:51   ` Bjorge Dijkstra
       [not found]     ` <11983602952006-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
  2007-12-23  1:49   ` [PATCH 0/8] RFC: Wireless Extensions for rndis_host David Brownell
  4 siblings, 1 reply; 23+ messages in thread
From: Bjorge Dijkstra @ 2007-12-22 21:51 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Jussi Kivilinna

From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>

Use wlan device name for RNDIS wireless devices.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
---
 drivers/net/usb/rndis_wext.c |    2 +-
 drivers/net/usb/usbnet.c     |    3 +++
 drivers/net/usb/usbnet.h     |    2 ++
 3 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/drivers/net/usb/rndis_wext.c b/drivers/net/usb/rndis_wext.c
index a9ce944..1c28b2a 100644
--- a/drivers/net/usb/rndis_wext.c
+++ b/drivers/net/usb/rndis_wext.c
@@ -2166,7 +2166,7 @@ static int rndis_wext_reset(struct usbnet *dev)
 
 struct driver_info rndis_wext_info = {
 	.description =	"Wireless RNDIS device",
-	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
 	.bind =		rndis_wext_bind,
 	.unbind =	rndis_wext_unbind,
 	.status =	rndis_status,
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 8ed1fc5..a2a2d5e 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1204,6 +1204,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 		if ((dev->driver_info->flags & FLAG_ETHER) != 0
 				&& (net->dev_addr [0] & 0x02) == 0)
 			strcpy (net->name, "eth%d");
+		/* WLAN devices should always be named "wlan%d" */
+		if ((dev->driver_info->flags & FLAG_WLAN) != 0)
+			strcpy(net->name, "wlan%d");
 
 		/* maybe the remote can't receive an Ethernet MTU */
 		if (net->mtu > (dev->hard_mtu - net->hard_header_len))
diff --git a/drivers/net/usb/usbnet.h b/drivers/net/usb/usbnet.h
index 83860a0..5c98ddc 100644
--- a/drivers/net/usb/usbnet.h
+++ b/drivers/net/usb/usbnet.h
@@ -88,6 +88,8 @@ struct driver_info {
 #define FLAG_ETHER	0x0020		/* maybe use "eth%d" names */
 
 #define FLAG_FRAMING_AX 0x0040		/* AX88772/178 packets */
+#define FLAG_WLAN	0x0080		/* use "wlan%d" names */
+
 
 	/* init device ... can sleep, or cause probe() failure */
 	int	(*bind)(struct usbnet *, struct usb_interface *);
-- 
1.5.2.5

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [PATCH 1/8] Fix sparse warning: returning void-valued expression
       [not found]     ` <11983602952341-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
@ 2007-12-23  0:42       ` David Brownell
  0 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2007-12-23  0:42 UTC (permalink / raw)
  To: bjd-a1rhEgazXTw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

> From bjd-a1rhEgazXTw@public.gmane.org  Sat Dec 22 13:52:53 2007
> From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
> Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Subject: [PATCH 1/8] Fix sparse warning: returning void-valued expression
> Date: Sat, 22 Dec 2007 22:51:27 +0100
>
> rndis_unbind and usbnet_cdc_unbind don't return anything.
>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>

Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>


> ---
>  drivers/net/usb/rndis_host.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index 1ebe325..96ef6a9 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -585,7 +585,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
>  		kfree(halt);
>  	}
>  
> -	return usbnet_cdc_unbind(dev, intf);
> +	usbnet_cdc_unbind(dev, intf);
>  }
>  
>  /*
> -- 
> 1.5.2.5
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing
  2007-12-22 21:51 ` [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing Bjorge Dijkstra
@ 2007-12-23  0:49   ` David Brownell
  0 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2007-12-23  0:49 UTC (permalink / raw)
  To: bjd; +Cc: netdev, linux-wireless

> From bjd@jooz.net  Sat Dec 22 13:53:00 2007
> X-SourceIP: 213.93.131.145
> From: Bjorge Dijkstra <bjd@jooz.net>
> To: dbrownell@users.sourceforge.net
> Cc: netdev@vger.kernel.org, linux-wireless@vger.kernel.org
> Subject: [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing
> Date: Sat, 22 Dec 2007 22:51:28 +0100
>
> Just as ActiveSync devices, some regular RNDIS devices also lack
> the CDC descriptors (e.g. devices based on BCM4320 WLAN chip).
> This patch hardwires the CDC descriptors for all RNDIS style devices
> when they are missing.
>
> Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>

Acked-by: David Brownell <dbrownell@users.sourceforge.net>

Note what this tells us about how little Microsoft cares about
the interoperability specs they claim to conform to.  Having
those descriptors is *NOT* optional in their (incomplete and
in adequate) RNDIS specification.  (I've not seen ActiveSync
specs, so that support is pure guesswork.)


> ---
>  drivers/net/usb/cdc_ether.c |   10 +++++-----
>  1 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
> index a42acc3..97c17bb 100644
> --- a/drivers/net/usb/cdc_ether.c
> +++ b/drivers/net/usb/cdc_ether.c
> @@ -228,15 +228,16 @@ next_desc:
>  		buf += buf [0];
>  	}
>  
> -	/* Microsoft ActiveSync based RNDIS devices lack the CDC descriptors,
> -	 * so we'll hard-wire the interfaces and not check for descriptors.
> +	/* Microsoft ActiveSync based and some regular RNDIS devices lack the
> +	 * CDC descriptors, so we'll hard-wire the interfaces and not check
> +	 * for descriptors.
>  	 */
> -	if (is_activesync(&intf->cur_altsetting->desc) && !info->u) {
> +	if (rndis && !info->u) {
>  		info->control = usb_ifnum_to_if(dev->udev, 0);
>  		info->data = usb_ifnum_to_if(dev->udev, 1);
>  		if (!info->control || !info->data) {
>  			dev_dbg(&intf->dev,
> -				"activesync: master #0/%p slave #1/%p\n",
> +				"rndis: master #0/%p slave #1/%p\n",
>  				info->control,
>  				info->data);
>  			goto bad_desc;
> @@ -316,7 +317,6 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
>  }
>  EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
>  
> -?
>  /*-------------------------------------------------------------------------
>   *
>   * Communications Device Class, Ethernet Control model
> -- 
> 1.5.2.5
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 3/8] [PATCH] Use 1KB buffer in rndis_unbind
       [not found]   ` <11983602953865-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
@ 2007-12-23  0:51     ` David Brownell
  0 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2007-12-23  0:51 UTC (permalink / raw)
  To: bjd-a1rhEgazXTw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	jussi.kivilinna-E01nCVcF24I

> From bjd-a1rhEgazXTw@public.gmane.org  Sat Dec 22 13:53:03 2007
> From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
> Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
> 	Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
> Subject: [PATCH 3/8] [PATCH] Use 1KB buffer in rndis_unbind
> Date: Sat, 22 Dec 2007 22:51:29 +0100
>
> From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
>
> rndis_command requires the caller to pass in a buffer of at least 1KB.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>

Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>

> ---
>  drivers/net/usb/rndis_host.c |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index 96ef6a9..42b161c 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -577,7 +577,7 @@ static void rndis_unbind(struct usbnet *dev, struct usb_interface *intf)
>  	struct rndis_halt	*halt;
>  
>  	/* try to clear any rndis state/activity (no i/o from stack!) */
> -	halt = kzalloc(sizeof *halt, GFP_KERNEL);
> +	halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
>  	if (halt) {
>  		halt->msg_type = RNDIS_MSG_HALT;
>  		halt->msg_len = ccpu2(sizeof *halt);
> -- 
> 1.5.2.5
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 4/8] [PATCH] Halt device if rndis_bind fails.
  2007-12-22 21:51   ` [PATCH 4/8] [PATCH] Halt device if rndis_bind fails Bjorge Dijkstra
@ 2007-12-23  0:54     ` David Brownell
  0 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2007-12-23  0:54 UTC (permalink / raw)
  To: bjd; +Cc: netdev, linux-wireless, jussi.kivilinna

> From bjd@jooz.net  Sat Dec 22 13:53:07 2007
> From: Bjorge Dijkstra <bjd@jooz.net>
> To: dbrownell@users.sourceforge.net
> Cc: netdev@vger.kernel.org, linux-wireless@vger.kernel.org,
> 	Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
> Subject: [PATCH 4/8] [PATCH] Halt device if rndis_bind fails.
> Date: Sat, 22 Dec 2007 22:51:30 +0100
>
> From: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
>
> When bind fails after device was initialized, shutdown device properly
> by sending RNDIS_MSG_HALT.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
> Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>

Acked-by: David Brownell <dbrownell@users.sourceforge.net>

> ---
>  drivers/net/usb/rndis_host.c |   12 +++++++++---
>  1 files changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index 42b161c..c686025 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -467,6 +467,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
>  		struct rndis_query_c	*get_c;
>  		struct rndis_set	*set;
>  		struct rndis_set_c	*set_c;
> +		struct rndis_halt	*halt;
>  	} u;
>  	u32			tmp;
>  	int			reply_len;
> @@ -517,7 +518,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
>  				"dev can't take %u byte packets (max %u)\n",
>  				dev->hard_mtu, tmp);
>  			retval = -EINVAL;
> -			goto fail_and_release;
> +			goto halt_fail_and_release;
>  		}
>  		dev->hard_mtu = tmp;
>  		net->mtu = dev->hard_mtu - net->hard_header_len;
> @@ -539,7 +540,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
>  			48, (void **) &bp, &reply_len);
>  	if (unlikely(retval< 0)) {
>  		dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval);
> -		goto fail_and_release;
> +		goto halt_fail_and_release;
>  	}
>  	memcpy(net->dev_addr, bp, ETH_ALEN);
>  
> @@ -555,7 +556,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
>  	retval = rndis_command(dev, u.header);
>  	if (unlikely(retval < 0)) {
>  		dev_err(&intf->dev, "rndis set packet filter, %d\n", retval);
> -		goto fail_and_release;
> +		goto halt_fail_and_release;
>  	}
>  
>  	retval = 0;
> @@ -563,6 +564,11 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
>  	kfree(u.buf);
>  	return retval;
>  
> +halt_fail_and_release:
> +	memset(u.halt, 0, sizeof *u.halt);
> +	u.halt->msg_type = RNDIS_MSG_HALT;
> +	u.halt->msg_len = ccpu2(sizeof *u.halt);
> +	(void) rndis_command(dev, (void *)u.halt);
>  fail_and_release:
>  	usb_set_intfdata(info->data, NULL);
>  	usb_driver_release_interface(driver_of(intf), info->data);
> -- 
> 1.5.2.5
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 5/8] Fix rndis packet filter flags.
  2007-12-22 21:51   ` [PATCH 5/8] Fix rndis packet filter flags Bjorge Dijkstra
@ 2007-12-23  1:06     ` David Brownell
  0 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2007-12-23  1:06 UTC (permalink / raw)
  To: bjd; +Cc: netdev, linux-wireless, jussi.kivilinna

> From bjd@jooz.net  Sat Dec 22 13:53:12 2007
> From: Bjorge Dijkstra <bjd@jooz.net>
> To: dbrownell@users.sourceforge.net
> Cc: netdev@vger.kernel.org, linux-wireless@vger.kernel.org,
> 	Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
> Subject: [PATCH 5/8] Fix rndis packet filter flags.
> Date: Sat, 22 Dec 2007 22:51:31 +0100
>
> From: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
>
> RNDIS packet filter flags are not exactly the same as CDC flags
> so we cannot reuse them.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
> Signed-off-by: Bjorge Dijkstra <bjd@jooz.net>

Acked-by: David Brownell <dbrownell@users.sourceforge.net>

Hmm, the list seems to have grown since I last looked at it.
Or maybe it's just that the MSFT documentation was so hard
to make sense of ... I never could figure out what the RNDIS
power management messages were supposed to be doing, or what
the extra seemingly-undocumented messages were there for.


> ---
>  drivers/net/usb/rndis_host.c |   23 ++++++++++++++++++++++-
>  1 files changed, 22 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index c686025..3c116f9 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -256,6 +256,27 @@ struct rndis_keepalive_c {	/* IN (optionally OUT) */
>  #define OID_GEN_MAXIMUM_FRAME_SIZE	ccpu2(0x00010106)
>  #define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
>  
> +/* packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */
> +#define RNDIS_PACKET_TYPE_DIRECTED		ccpu2(0x00000001)
> +#define RNDIS_PACKET_TYPE_MULTICAST		ccpu2(0x00000002)
> +#define RNDIS_PACKET_TYPE_ALL_MULTICAST		ccpu2(0x00000004)
> +#define RNDIS_PACKET_TYPE_BROADCAST		ccpu2(0x00000008)
> +#define RNDIS_PACKET_TYPE_SOURCE_ROUTING	ccpu2(0x00000010)
> +#define RNDIS_PACKET_TYPE_PROMISCUOUS		ccpu2(0x00000020)
> +#define RNDIS_PACKET_TYPE_SMT			ccpu2(0x00000040)
> +#define RNDIS_PACKET_TYPE_ALL_LOCAL		ccpu2(0x00000080)
> +#define RNDIS_PACKET_TYPE_GROUP			ccpu2(0x00001000)
> +#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL	ccpu2(0x00002000)
> +#define RNDIS_PACKET_TYPE_FUNCTIONAL		ccpu2(0x00004000)
> +#define RNDIS_PACKET_TYPE_MAC_FRAME		ccpu2(0x00008000)
> +
> +/* default filter used with RNDIS devices */
> +#define RNDIS_DEFAULT_FILTER ( \
> +	RNDIS_PACKET_TYPE_DIRECTED | \
> +	RNDIS_PACKET_TYPE_BROADCAST | \
> +	RNDIS_PACKET_TYPE_ALL_MULTICAST | \
> +	RNDIS_PACKET_TYPE_PROMISCUOUS )
> +
>  /*
>   * RNDIS notifications from device: command completion; "reverse"
>   * keepalives; etc
> @@ -551,7 +572,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
>  	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(DEFAULT_FILTER);
> +	*(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER;
>  
>  	retval = rndis_command(dev, u.header);
>  	if (unlikely(retval < 0)) {
> -- 
> 1.5.2.5
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 6/8] [PATCH] Split up rndis_host.c
  2007-12-22 21:51 ` [PATCH 6/8] [PATCH] Split up rndis_host.c Bjorge Dijkstra
@ 2007-12-23  1:17   ` David Brownell
       [not found]     ` <20071223011707.1798C2360C6-ZcXrCSuhvln6VZ3dlLfH/g4gEjPzgfUyLrfjE7I9kuVHxeISYlDBzl6hYfS7NtTn@public.gmane.org>
  0 siblings, 1 reply; 23+ messages in thread
From: David Brownell @ 2007-12-23  1:17 UTC (permalink / raw)
  To: bjd; +Cc: netdev, linux-wireless

> From: Bjorge Dijkstra <bjd@jooz.net>
> Subject: [PATCH 6/8] [PATCH] Split up rndis_host.c
> Date: Sat, 22 Dec 2007 22:51:32 +0100
>
> Split up rndis_host.c into rndis_host.h and rndis_base.c and
> change Makefile accordingly. This is done so we can add extra
> source files to the rndis_host module later on.

I'm fine with splitting out a header file and the EXPORT_SYMBOL_GPL.
But why not just have a separate "rndis_wext" module?


> ---
>  drivers/net/usb/Makefile     |    1 +
>  drivers/net/usb/rndis_base.c |  548 ++++++++++++++++++++++++++++++
>  drivers/net/usb/rndis_host.c |  763 ------------------------------------------
>  drivers/net/usb/rndis_host.h |  256 ++++++++++++++
>  4 files changed, 805 insertions(+), 763 deletions(-)
>  create mode 100644 drivers/net/usb/rndis_base.c
>  delete mode 100644 drivers/net/usb/rndis_host.c
>  create mode 100644 drivers/net/usb/rndis_host.h

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 7/8] Add Wireless Extensions to rndis_host
       [not found]   ` <11983602951628-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
@ 2007-12-23  1:27     ` David Brownell
  0 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2007-12-23  1:27 UTC (permalink / raw)
  To: dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f, bjd-a1rhEgazXTw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

> From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> Subject: [PATCH 7/8] Add Wireless Extensions to rndis_host
> Date: Sat, 22 Dec 2007 22:51:33 +0100
>
> The bulk of this patch is the addition of a new file that
> implements the wireless extensions for RNDIS devices.
> The rest are some smaller changes to usbnet and rndis_host
> to hook the wireless extensions into them:
> * a private data pointer is added to usbnet.
> * a callback is added to driver_info to signal link state changes.
> * a physical medium type check is added to rndis_bind to check for
>   wireless lan devices and turn on the wireless extensions.
> * and finally a Kconfig option to enable/disable this all.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>

I'm basically fine with this, but I'll hold off any ack until the
issue with the preceding patch (6/8) is resolved.  That would boil
down to making this a standalone module, or knowing why that's not
such a good idea.

I'd prefer to see the usbnet core extension packaged separately
instead of buried in this patch.  :)

And I think a MAINTAINERS update for rndis_wext would be appropriate.
No way can I take care of that code!

(In fact, would you feel comfortable taking over rndis_host too?)

- Dave


> ---
>  drivers/net/usb/Kconfig      |    7 +
>  drivers/net/usb/Makefile     |    4 +
>  drivers/net/usb/rndis_base.c |   50 +-
>  drivers/net/usb/rndis_host.h |   18 +
>  drivers/net/usb/rndis_wext.c | 2177 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/usb/usbnet.h     |    4 +
>  6 files changed, 2255 insertions(+), 5 deletions(-)
>  create mode 100644 drivers/net/usb/rndis_wext.c

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 8/8] [PATCH] Use wlan device name for RNDIS wireless devices
       [not found]     ` <11983602952006-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
@ 2007-12-23  1:30       ` David Brownell
  0 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2007-12-23  1:30 UTC (permalink / raw)
  To: bjd-a1rhEgazXTw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	jussi.kivilinna-E01nCVcF24I

> From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> Subject: [PATCH 8/8] [PATCH] Use wlan device name for RNDIS wireless devices
> Date: Sat, 22 Dec 2007 22:51:34 +0100
>
> From: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
>
> Use wlan device name for RNDIS wireless devices.
>
> Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
> Signed-off-by: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>

Acked-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>

... though it'd be a bit nicer IMO to see this before patch #7,
and just have one (or two) patches that only add infrastructure
to the usbnet core code, before the rndis_wext patch which uses
that new infrastructure.


> ---
>  drivers/net/usb/rndis_wext.c |    2 +-
>  drivers/net/usb/usbnet.c     |    3 +++
>  drivers/net/usb/usbnet.h     |    2 ++
>  3 files changed, 6 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/usb/rndis_wext.c b/drivers/net/usb/rndis_wext.c
> index a9ce944..1c28b2a 100644
> --- a/drivers/net/usb/rndis_wext.c
> +++ b/drivers/net/usb/rndis_wext.c
> @@ -2166,7 +2166,7 @@ static int rndis_wext_reset(struct usbnet *dev)
>  
>  struct driver_info rndis_wext_info = {
>  	.description =	"Wireless RNDIS device",
> -	.flags =	FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT,
> +	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
>  	.bind =		rndis_wext_bind,
>  	.unbind =	rndis_wext_unbind,
>  	.status =	rndis_status,
> diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> index 8ed1fc5..a2a2d5e 100644
> --- a/drivers/net/usb/usbnet.c
> +++ b/drivers/net/usb/usbnet.c
> @@ -1204,6 +1204,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
>  		if ((dev->driver_info->flags & FLAG_ETHER) != 0
>  				&& (net->dev_addr [0] & 0x02) == 0)
>  			strcpy (net->name, "eth%d");
> +		/* WLAN devices should always be named "wlan%d" */
> +		if ((dev->driver_info->flags & FLAG_WLAN) != 0)
> +			strcpy(net->name, "wlan%d");
>  
>  		/* maybe the remote can't receive an Ethernet MTU */
>  		if (net->mtu > (dev->hard_mtu - net->hard_header_len))
> diff --git a/drivers/net/usb/usbnet.h b/drivers/net/usb/usbnet.h
> index 83860a0..5c98ddc 100644
> --- a/drivers/net/usb/usbnet.h
> +++ b/drivers/net/usb/usbnet.h
> @@ -88,6 +88,8 @@ struct driver_info {
>  #define FLAG_ETHER	0x0020		/* maybe use "eth%d" names */
>  
>  #define FLAG_FRAMING_AX 0x0040		/* AX88772/178 packets */
> +#define FLAG_WLAN	0x0080		/* use "wlan%d" names */
> +
>  
>  	/* init device ... can sleep, or cause probe() failure */
>  	int	(*bind)(struct usbnet *, struct usb_interface *);
> -- 
> 1.5.2.5
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 0/8] RFC: Wireless Extensions for rndis_host
       [not found] ` <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
                     ` (3 preceding siblings ...)
  2007-12-22 21:51   ` [PATCH 8/8] [PATCH] Use wlan device name for RNDIS wireless devices Bjorge Dijkstra
@ 2007-12-23  1:49   ` David Brownell
  4 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2007-12-23  1:49 UTC (permalink / raw)
  To: bjd-a1rhEgazXTw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

> From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> Subject: [PATCH 0/8] RFC: Wireless Extensions for rndis_host
> Date: Sat, 22 Dec 2007 22:51:26 +0100
>
> Hello all,
>
> I have here a patchset that needs some review.
> This patchset adds wireless extensions for rndis_host to
> enable support for RNDIS based USB wireless LAN adapters
> (e.g. Linksys WUSB54GS, Belkin F05D7051).

Cool!  We always like to see support for more USB peripherals.
Even (especially?) if they'e previously been Windows-only,
penguin-deprived hardware.  ;)

Is all that stuff adequately documented?  What I recall from
working with RNDIS before is that docs were lacking in some
critical details, and were sometimes wrong.


> In this patchset:
>  1.  Fix sparse warning: returning void valued expression
>  2.  Hardwire CDC descriptors when missing
>  3.  Use 1KB buffer in rndis_unbind
>  4.  Halt device if rndis_bind fails
>  5.  Fix rndis packet filter flags
>  6.  Split up rndis_host.c
>  7.  Add Wireless Extensions for rndis_host
>  8.  Use wlan device name for RNDIS wireless devices
>
> The first five patches are more generic fixes that I think
> can be applied as they are. Of these, patch 2 is required
> to get these wireless devices working. 

I had no problem with those first five.  I take it none of
those should be prioritized for 2.6.24 integration?


> Patches 6-8 introduce some larger changes that may need a
> closer look.

And you saw my feedback on those.  Minimally, please split
out the usbnet core extensions, instead of combining them
with rndis_wext patches ... and on first princples I'd
think rndis_wext should live in its own module.

I just skimmed patch #7.  There are people far more aware
of the issues with Linux wireless drivers than me.  :)

- Dave


> All these patches should be applied in order.
> The entire series should apply cleanly to current linux
> kernel and net-2.6.25 trees.
>
> regards,
> Bjorge Dijkstra
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 6/8] [PATCH] Split up rndis_host.c
       [not found]     ` <20071223011707.1798C2360C6-ZcXrCSuhvln6VZ3dlLfH/g4gEjPzgfUyLrfjE7I9kuVHxeISYlDBzl6hYfS7NtTn@public.gmane.org>
@ 2008-01-02 20:13       ` Jussi Kivilinna
  2008-01-08 11:19         ` David Brownell
  0 siblings, 1 reply; 23+ messages in thread
From: Jussi Kivilinna @ 2008-01-02 20:13 UTC (permalink / raw)
  To: David Brownell
  Cc: bjd-a1rhEgazXTw, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

Hello,

Bjorge was not comfortable with double probing rndis_wext requires,
wireless RNDIS devices have same device id as the rest of RNDIS. Our
module version checks OID_GEN_PHYSICAL_MEDIUM in generic_rndis_bind,
with rndis_host bind fails if OID is supported and wireless media type
is returned, with rndis_wext if OID isn't supported or type isn't
wireless. Should this be ok?

Should separate rndis_wext be located in drivers/net/wireless instead of
drivers/net/usb?

 - Jussi Kivilinna

On Sat, 2007-12-22 at 17:17 -0800, David Brownell wrote:
> > From: Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
> > Subject: [PATCH 6/8] [PATCH] Split up rndis_host.c
> > Date: Sat, 22 Dec 2007 22:51:32 +0100
> >
> > Split up rndis_host.c into rndis_host.h and rndis_base.c and
> > change Makefile accordingly. This is done so we can add extra
> > source files to the rndis_host module later on.
> 
> I'm fine with splitting out a header file and the EXPORT_SYMBOL_GPL.
> But why not just have a separate "rndis_wext" module?
> 
> 
> > ---
> >  drivers/net/usb/Makefile     |    1 +
> >  drivers/net/usb/rndis_base.c |  548 ++++++++++++++++++++++++++++++
> >  drivers/net/usb/rndis_host.c |  763 ------------------------------------------
> >  drivers/net/usb/rndis_host.h |  256 ++++++++++++++
> >  4 files changed, 805 insertions(+), 763 deletions(-)
> >  create mode 100644 drivers/net/usb/rndis_base.c
> >  delete mode 100644 drivers/net/usb/rndis_host.c
> >  create mode 100644 drivers/net/usb/rndis_host.h
> -
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 6/8] [PATCH] Split up rndis_host.c
  2008-01-02 20:13       ` Jussi Kivilinna
@ 2008-01-08 11:19         ` David Brownell
       [not found]           ` <200801080319.23299.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 23+ messages in thread
From: David Brownell @ 2008-01-08 11:19 UTC (permalink / raw)
  To: Jussi Kivilinna; +Cc: bjd, netdev, linux-wireless

On Wednesday 02 January 2008, Jussi Kivilinna wrote:
> Hello,
> 
> Bjorge was not comfortable with double probing rndis_wext requires,
> wireless RNDIS devices have same device id as the rest of RNDIS.

I should have known Microsoft wouldn't start by assuming systems
software module boundaries should be natural and obvious ones.  :(

I see that the rndis_wext.c Kconfig won't kick in unless the
802.11 stuff is available ... what additional dependencies
does that imply for a fatter rndis_host module?  If it doesn't
cause extra modules to be loaded (even for non-WLAN devices),
then I suppose I could just live with the RNDIS module being
bigger than it needs to be.  It was pretty thin to start with!

What would bother me would be seeing that systems with this
WLAN support option configured could no longer load the basic
RNDIS code without also loading a bunch of extra modules
that weren't needed for all RNDIS devices.


> Our 
> module version checks OID_GEN_PHYSICAL_MEDIUM in generic_rndis_bind,
> with rndis_host bind fails if OID is supported and wireless media type
> is returned, with rndis_wext if OID isn't supported or type isn't
> wireless. Should this be ok?

Sounds like if you can go the different-modules route, then rndis_bind()
should maybe accept a parameter giving it that option.  Then

	rndis_wlan_bind(...) == rndis_bind(..., check_wlan_media)
	rndis_generic_bind(...) == rndis_bind(..., non_wlan_media)

That would be getting pretty complicated, since as you say the same
USB-level binding (hence hotplugging) would need to kick in ... but
having different modules for different RNDIS flavors would need a 
new hotplug mechanism keying on media type.  Ugh.


> Should separate rndis_wext be located in drivers/net/wireless instead of
> drivers/net/usb?

If that doesn't complicate maintaining that software, I can't
see why it shouldn't go there.  It might imply moving the usbnet
interface declarations to someplace linux/include though.  (And
that presumes there's actually a sane way to split that stuff
into its own module, too...)

- Dave


>  - Jussi Kivilinna
> 
> On Sat, 2007-12-22 at 17:17 -0800, David Brownell wrote:
> > > From: Bjorge Dijkstra <bjd@jooz.net>
> > > Subject: [PATCH 6/8] [PATCH] Split up rndis_host.c
> > > Date: Sat, 22 Dec 2007 22:51:32 +0100
> > >
> > > Split up rndis_host.c into rndis_host.h and rndis_base.c and
> > > change Makefile accordingly. This is done so we can add extra
> > > source files to the rndis_host module later on.
> > 
> > I'm fine with splitting out a header file and the EXPORT_SYMBOL_GPL.
> > But why not just have a separate "rndis_wext" module?
> > 
> > 
> > > ---
> > >  drivers/net/usb/Makefile     |    1 +
> > >  drivers/net/usb/rndis_base.c |  548 ++++++++++++++++++++++++++++++
> > >  drivers/net/usb/rndis_host.c |  763 ------------------------------------------
> > >  drivers/net/usb/rndis_host.h |  256 ++++++++++++++
> > >  4 files changed, 805 insertions(+), 763 deletions(-)
> > >  create mode 100644 drivers/net/usb/rndis_base.c
> > >  delete mode 100644 drivers/net/usb/rndis_host.c
> > >  create mode 100644 drivers/net/usb/rndis_host.h
> > 
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 6/8] [PATCH] Split up rndis_host.c
       [not found]           ` <200801080319.23299.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2008-01-08 17:32             ` Johannes Berg
       [not found]               ` <1199813561.6762.13.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
  2008-01-17 20:40             ` Jussi Kivilinna
  1 sibling, 1 reply; 23+ messages in thread
From: Johannes Berg @ 2008-01-08 17:32 UTC (permalink / raw)
  To: David Brownell
  Cc: Jussi Kivilinna, bjd-a1rhEgazXTw, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 422 bytes --]


> I see that the rndis_wext.c Kconfig won't kick in unless the
> 802.11 stuff is available ... what additional dependencies
> does that imply for a fatter rndis_host module?

No extra modules are currently required for just plain wext [1]. In the
future, we hope to migrate stuff to cfg80211 though, which is a separate
module.

johannes

[1] mostly because all the code is simply compiled into the kernel...

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 6/8] [PATCH] Split up rndis_host.c
       [not found]               ` <1199813561.6762.13.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
@ 2008-01-08 18:49                 ` David Brownell
  0 siblings, 0 replies; 23+ messages in thread
From: David Brownell @ 2008-01-08 18:49 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Jussi Kivilinna, bjd-a1rhEgazXTw, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

On Tuesday 08 January 2008, Johannes Berg wrote:
> 
> > I see that the rndis_wext.c Kconfig won't kick in unless the
> > 802.11 stuff is available ... what additional dependencies
> > does that imply for a fatter rndis_host module?
> 
> No extra modules are currently required for just plain wext [1]. In the
> future, we hope to migrate stuff to cfg80211 though, which is a separate
> module.

Hmmm.  Well, go the current route then ... but come up with a good
plan to split it up later, so that connecting a typical non-wireless
RNDIS device won't incur such needless extra modules.

- Dave


> johannes
> 
> [1] mostly because all the code is simply compiled into the kernel...
> 

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH 6/8] [PATCH] Split up rndis_host.c
       [not found]           ` <200801080319.23299.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2008-01-08 17:32             ` Johannes Berg
@ 2008-01-17 20:40             ` Jussi Kivilinna
  1 sibling, 0 replies; 23+ messages in thread
From: Jussi Kivilinna @ 2008-01-17 20:40 UTC (permalink / raw)
  To: David Brownell
  Cc: bjd-a1rhEgazXTw, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

On Tue, 2008-01-08 at 03:19 -0800, David Brownell wrote:
> > Our 
> > module version checks OID_GEN_PHYSICAL_MEDIUM in generic_rndis_bind,
> > with rndis_host bind fails if OID is supported and wireless media
type
> > is returned, with rndis_wext if OID isn't supported or type isn't
> > wireless. Should this be ok?
> 
> Sounds like if you can go the different-modules route, then
rndis_bind()
> should maybe accept a parameter giving it that option.  Then
> 
>       rndis_wlan_bind(...) == rndis_bind(..., check_wlan_media)
>       rndis_generic_bind(...) == rndis_bind(..., non_wlan_media)
> 
> That would be getting pretty complicated, since as you say the same
> USB-level binding (hence hotplugging) would need to kick in ... but
> having different modules for different RNDIS flavors would need a 
> new hotplug mechanism keying on media type.  Ugh.

Different modules works with rndis_generic_bind(..., media type option).
dmesg shows following:

[11214.496773] usb 3-3: new high speed USB device using ehci_hcd and
address 7
[11214.622861] usb 3-3: configuration #1 chosen from 1 choice
[11214.784874] usbcore: registered new interface driver cdc_ether
[11214.805700] rndis_host 3-3:1.0: driver requires wired physical
medium, but device is wireless.
[11214.806823] usbcore: registered new interface driver rndis_host
[11215.190824] wlan0: register 'rndis_wext' at usb-0000:00:10.3-3,
Wireless RNDIS device, 00:xx:xx:xx:xx:xx
[11215.190853] usbcore: registered new interface driver rndis_wext
[11215.398850] rndis_wext 3-3:1.0: rndis media disconnect

I assume that rndis_host gets always bind first as it's first in
alphabetic, so rndis_wext is only loaded if RNDIS device has wireless
media type.

I have new patchset that prepares rndis_host/usbnet for separate
rndis_wext but not sure should I submit (I haven't managed to get into
contact with Bjorge Dijkstra for 3,5 weeks now to have him decide how to
proceed). 

 - Jussi Kivilinna

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2008-01-17 20:40 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-12-22 21:51 [PATCH 0/8] RFC: Wireless Extensions for rndis_host Bjorge Dijkstra
2007-12-22 21:51 ` [PATCH 2/8] [PATCH] Hardwire CDC descriptors when missing Bjorge Dijkstra
2007-12-23  0:49   ` David Brownell
2007-12-22 21:51 ` [PATCH 3/8] [PATCH] Use 1KB buffer in rndis_unbind Bjorge Dijkstra
     [not found]   ` <11983602953865-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
2007-12-23  0:51     ` David Brownell
     [not found] ` <11983602942818-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
2007-12-22 21:51   ` [PATCH 1/8] Fix sparse warning: returning void-valued expression Bjorge Dijkstra
     [not found]     ` <11983602952341-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
2007-12-23  0:42       ` David Brownell
2007-12-22 21:51   ` [PATCH 4/8] [PATCH] Halt device if rndis_bind fails Bjorge Dijkstra
2007-12-23  0:54     ` David Brownell
2007-12-22 21:51   ` [PATCH 5/8] Fix rndis packet filter flags Bjorge Dijkstra
2007-12-23  1:06     ` David Brownell
2007-12-22 21:51   ` [PATCH 8/8] [PATCH] Use wlan device name for RNDIS wireless devices Bjorge Dijkstra
     [not found]     ` <11983602952006-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
2007-12-23  1:30       ` David Brownell
2007-12-23  1:49   ` [PATCH 0/8] RFC: Wireless Extensions for rndis_host David Brownell
2007-12-22 21:51 ` [PATCH 6/8] [PATCH] Split up rndis_host.c Bjorge Dijkstra
2007-12-23  1:17   ` David Brownell
     [not found]     ` <20071223011707.1798C2360C6-ZcXrCSuhvln6VZ3dlLfH/g4gEjPzgfUyLrfjE7I9kuVHxeISYlDBzl6hYfS7NtTn@public.gmane.org>
2008-01-02 20:13       ` Jussi Kivilinna
2008-01-08 11:19         ` David Brownell
     [not found]           ` <200801080319.23299.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-01-08 17:32             ` Johannes Berg
     [not found]               ` <1199813561.6762.13.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
2008-01-08 18:49                 ` David Brownell
2008-01-17 20:40             ` Jussi Kivilinna
2007-12-22 21:51 ` [PATCH 7/8] Add Wireless Extensions to rndis_host Bjorge Dijkstra
     [not found]   ` <11983602951628-git-send-email-bjd-a1rhEgazXTw@public.gmane.org>
2007-12-23  1:27     ` David Brownell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).