Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH] mv643xx_eth: fix byte order when checksum offload is enabled
From: Byron Bradley @ 2008-01-20 15:05 UTC (permalink / raw)
  To: Al Viro
  Cc: Byron Bradley, netdev, hvr, akpm, Dale Farnsworth,
	Manish Lachwani, Tzachi Perelstein
In-Reply-To: <20080119200900.GE27894@ZenIV.linux.org.uk>

The Marvell Orion system on chips have an integrated mv643xx MAC.
On these little endian ARM devices mv643xx will oops when checksum
offload is enabled. Swapping the byte order of the protocol and
checksum solves this problem.

Signed-off-by: Byron Bradley <byron.bbradley@gmail.com>
Cc: Dale Farnsworth <dale@farnsworth.org>
Cc: Manish Lachwani <mlachwani@mvista.com>
---

Resubmitted after comments from Al Viro. udp_hdr(skb)->check returns
a __sum16 so I have called the function sum16_as_be(). Again lightly
tested only on two Marvell Orion boards.

 drivers/net/mv643xx_eth.c |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index 651c269..b528ce7 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -1652,6 +1652,11 @@ static void eth_tx_fill_frag_descs(struct mv643xx_private *mp,
 	}
 }

+static inline __be16 sum16_as_be(__sum16 sum)
+{
+	return (__force __be16)sum;
+}
+
 /**
  * eth_tx_submit_descs_for_skb - submit data from an skb to the tx hw
  *
@@ -1689,7 +1694,7 @@ static void eth_tx_submit_descs_for_skb(struct mv643xx_private *mp,
 	desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);

 	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		BUG_ON(skb->protocol != ETH_P_IP);
+		BUG_ON(skb->protocol != htons(ETH_P_IP));

 		cmd_sts |= ETH_GEN_TCP_UDP_CHECKSUM |
 			   ETH_GEN_IP_V_4_CHECKSUM  |
@@ -1698,10 +1703,10 @@ static void eth_tx_submit_descs_for_skb(struct mv643xx_private *mp,
 		switch (ip_hdr(skb)->protocol) {
 		case IPPROTO_UDP:
 			cmd_sts |= ETH_UDP_FRAME;
-			desc->l4i_chk = udp_hdr(skb)->check;
+			desc->l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check));
 			break;
 		case IPPROTO_TCP:
-			desc->l4i_chk = tcp_hdr(skb)->check;
+			desc->l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check));
 			break;
 		default:
 			BUG();
-- 
1.5.4.rc2.38.gd6da3



^ permalink raw reply related

* [NET_SCHED]: kill obsolete NET_CLS_POLICE option
From: Patrick McHardy @ 2008-01-20 14:48 UTC (permalink / raw)
  To: David S. Miller; +Cc: Linux Netdev List

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



[-- Attachment #2: x --]
[-- Type: text/plain, Size: 1177 bytes --]

commit 2af10cb220dd73b91323d052f7ca4cb808906e78
Author: Patrick McHardy <kaber@trash.net>
Date:   Wed Jan 16 12:22:06 2008 +0100

    [NET_SCHED]: kill obsolete NET_CLS_POLICE option
    
    The code is already gone for about half a year, the config option
    has been kept around to select the replacement options for easier
    upgrades. This seems long enough, people upgrading from older
    kernels will have to reconfigure a lot anyway.
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 63882c5..4eb73ec 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -481,15 +481,6 @@ config NET_ACT_SIMP
 	  To compile this code as a module, choose M here: the
 	  module will be called simple.
 
-config NET_CLS_POLICE
-	bool "Traffic Policing (obsolete)"
-	select NET_CLS_ACT
-	select NET_ACT_POLICE
-	---help---
-	  Say Y here if you want to do traffic policing, i.e. strict
-	  bandwidth limiting. This option is obsolete and just selects
-	  the option replacing it. It will be removed in the future.
-
 config NET_CLS_IND
 	bool "Incoming device classification"
 	depends on NET_CLS_U32 || NET_CLS_FW

^ permalink raw reply related

* [MACVLAN]: Fix thinko in macvlan_transfer_operstate()
From: Patrick McHardy @ 2008-01-20 14:47 UTC (permalink / raw)
  To: David S. Miller; +Cc: Linux Netdev List

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

Fix a minor thinko in macvlan. The bug is uncritical, so this
doesn't need to go in 2.6.24.


[-- Attachment #2: x --]
[-- Type: text/plain, Size: 966 bytes --]

commit b40b16bb72ae1ad192daef55a3e9f81d76cd97ab
Author: Patrick McHardy <kaber@trash.net>
Date:   Thu Jan 17 09:18:11 2008 +0100

    [MACVLAN]: Fix thinko in macvlan_transfer_operstate()
    
    When the lower device's carrier is off, the macvlan devices's
    carrier state should be checked to decide whether it needs to
    be turned off. Currently the lower device's state is checked
    a second time.
    
    This still works, but unnecessarily tries to turn off the
    carrier when its already off.
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 3acf8cd..312526b 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -375,7 +375,7 @@ static void macvlan_transfer_operstate(struct net_device *dev)
 		if (!netif_carrier_ok(dev))
 			netif_carrier_on(dev);
 	} else {
-		if (netif_carrier_ok(lowerdev))
+		if (netif_carrier_ok(dev))
 			netif_carrier_off(dev);
 	}
 }

^ permalink raw reply related

* [NET_SCHED 09/09]: sch_ingress: remove useless printk
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit f8d51e131177c26c1033dfc804f11d9cac7637f4
Author: Patrick McHardy <kaber@trash.net>
Date:   Thu Jan 17 09:17:59 2008 +0100

    [NET_SCHED]: sch_ingress: remove useless printk
    
    The printk about ingress qdisc registration error can't be triggered under
    normal circumstances. Since register_qdisc only fails for two identical
    registrations, the only way to trigger it is by loading the sch_ingress
    modules multiple times under different names, in which case we already
    return -EEXIST to userspace.
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index 4c7f7e7..7252571 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -221,14 +221,7 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
 
 static int __init ingress_module_init(void)
 {
-	int ret = 0;
-
-	if ((ret = register_qdisc(&ingress_qdisc_ops)) < 0) {
-		printk("Unable to register Ingress qdisc\n");
-		return ret;
-	}
-
-	return ret;
+	return register_qdisc(&ingress_qdisc_ops);
 }
 
 static void __exit ingress_module_exit(void)

^ permalink raw reply related

* [NET_SCHED 08/09]: sch_ingress: avoid a few
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit 3320bc3c5d9a6c1dd96d515b7b6a4dee9cd31fd7
Author: Patrick McHardy <kaber@trash.net>
Date:   Thu Jan 17 09:17:58 2008 +0100

    [NET_SCHED]: sch_ingress: avoid a few #ifdefs
    
    Move the repeating "ifndef CONFIG_NET_CLS_ACT/ifdef CONFIG_NETFILTER" ifdefs
    into a single condition.
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index b30ca01..4c7f7e7 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -20,11 +20,9 @@
 
 
 /* Thanks to Doron Oz for this hack */
-#ifndef CONFIG_NET_CLS_ACT
-#ifdef CONFIG_NETFILTER
+#if !defined(CONFIG_NET_CLS_ACT) && defined(CONFIG_NETFILTER)
 static int nf_registered;
 #endif
-#endif
 
 struct ingress_qdisc_data {
 	struct tcf_proto	*filter_list;
@@ -118,8 +116,7 @@ static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 	return result;
 }
 
-#ifndef CONFIG_NET_CLS_ACT
-#ifdef CONFIG_NETFILTER
+#if !defined(CONFIG_NET_CLS_ACT) && defined(CONFIG_NETFILTER)
 static unsigned int ing_hook(unsigned int hook, struct sk_buff *skb,
 			     const struct net_device *indev,
 			     const struct net_device *outdev,
@@ -158,12 +155,10 @@ static struct nf_hook_ops ing_ops[] __read_mostly = {
 	},
 };
 #endif
-#endif
 
 static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 {
-#ifndef CONFIG_NET_CLS_ACT
-#ifdef CONFIG_NETFILTER
+#if !defined(CONFIG_NET_CLS_ACT) && defined(CONFIG_NETFILTER)
 	printk("Ingress scheduler: Classifier actions prefered over netfilter\n");
 
 	if (!nf_registered) {
@@ -174,7 +169,6 @@ static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 		nf_registered++;
 	}
 #endif
-#endif
 	return 0;
 }
 
@@ -240,12 +234,10 @@ static int __init ingress_module_init(void)
 static void __exit ingress_module_exit(void)
 {
 	unregister_qdisc(&ingress_qdisc_ops);
-#ifndef CONFIG_NET_CLS_ACT
-#ifdef CONFIG_NETFILTER
+#if !defined(CONFIG_NET_CLS_ACT) && defined(CONFIG_NETFILTER)
 	if (nf_registered)
 		nf_unregister_hooks(ing_ops, ARRAY_SIZE(ing_ops));
 #endif
-#endif
 }
 
 module_init(ingress_module_init)

^ permalink raw reply related

* [NET_SCHED 07/09]: sch_ingress: move dependencies to Kconfig
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit 4c939213974319d9457dbbf038d38ef1fcf57ef9
Author: Patrick McHardy <kaber@trash.net>
Date:   Wed Jan 16 12:22:08 2008 +0100

    [NET_SCHED]: sch_ingress: move dependencies to Kconfig
    
    Instead of complaining at scheduler initialization time, check the dependencies
    in Kconfig.
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 4eb73ec..a047cd5 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -204,6 +204,7 @@ config NET_SCH_NETEM
 
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
+	depends on NET_CLS_ACT || NETFILTER
 	---help---
 	  Say Y here if you want to use classifiers for incoming packets.
 	  If unsure, say Y.
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index c69e7bc..b30ca01 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -162,19 +162,10 @@ static struct nf_hook_ops ing_ops[] __read_mostly = {
 
 static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 {
-	/* Make sure either netfilter or preferably CLS_ACT is
-	 * compiled in */
 #ifndef CONFIG_NET_CLS_ACT
-#ifndef CONFIG_NETFILTER
-	printk("You MUST compile classifier actions into the kernel\n");
-	return -EINVAL;
-#else
+#ifdef CONFIG_NETFILTER
 	printk("Ingress scheduler: Classifier actions prefered over netfilter\n");
-#endif
-#endif
 
-#ifndef CONFIG_NET_CLS_ACT
-#ifdef CONFIG_NETFILTER
 	if (!nf_registered) {
 		if (nf_register_hooks(ing_ops, ARRAY_SIZE(ing_ops)) < 0) {
 			printk("ingress qdisc registration error \n");

^ permalink raw reply related

* [NET_SCHED 06/09]: sch_ingress: remove unnecessary ops
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit c96158287d0c87fc3e4d005ce8bf2c778a5c2cb6
Author: Patrick McHardy <kaber@trash.net>
Date:   Wed Jan 16 12:22:07 2008 +0100

    [NET_SCHED]: sch_ingress: remove unnecessary ops
    
    - ->reset is optional
    - sch_api provides identical defaults for ->dequeue/->requeue
    - ->drop can't happen since ingress never has a parent qdisc
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index 1bbc648..c69e7bc 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -118,21 +118,6 @@ static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 	return result;
 }
 
-static struct sk_buff *ingress_dequeue(struct Qdisc *sch)
-{
-	return NULL;
-}
-
-static int ingress_requeue(struct sk_buff *skb, struct Qdisc *sch)
-{
-	return 0;
-}
-
-static unsigned int ingress_drop(struct Qdisc *sch)
-{
-	return 0;
-}
-
 #ifndef CONFIG_NET_CLS_ACT
 #ifdef CONFIG_NETFILTER
 static unsigned int ing_hook(unsigned int hook, struct sk_buff *skb,
@@ -202,11 +187,6 @@ static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 	return 0;
 }
 
-static void ingress_reset(struct Qdisc *sch)
-{
-	return;
-}
-
 /* ------------------------------------------------------------- */
 
 static void ingress_destroy(struct Qdisc *sch)
@@ -248,11 +228,7 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
 	.id		=	"ingress",
 	.priv_size	=	sizeof(struct ingress_qdisc_data),
 	.enqueue	=	ingress_enqueue,
-	.dequeue	=	ingress_dequeue,
-	.requeue	=	ingress_requeue,
-	.drop		=	ingress_drop,
 	.init		=	ingress_init,
-	.reset		=	ingress_reset,
 	.destroy	=	ingress_destroy,
 	.dump		=	ingress_dump,
 	.owner		=	THIS_MODULE,

^ permalink raw reply related

* [NET_SCHED 05/09]: sch_ingress: return proper error code in ingress_graft()
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit e039da57d244f15d4b920de63a20ab516a76ed5b
Author: Patrick McHardy <kaber@trash.net>
Date:   Wed Jan 16 12:22:07 2008 +0100

    [NET_SCHED]: sch_ingress: return proper error code in ingress_graft()
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index d803cd1..1bbc648 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -35,7 +35,7 @@ struct ingress_qdisc_data {
 static int ingress_graft(struct Qdisc *sch, unsigned long arg,
 			 struct Qdisc *new, struct Qdisc **old)
 {
-	return 1;
+	return -EOPNOTSUPP;
 }
 
 static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)

^ permalink raw reply related

* [NET_SCHED 04/09]: sch_ingress: remove unused inner qdisc
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit bb582f29b1f403d0df8a4a54daca89eafd2bfb69
Author: Patrick McHardy <kaber@trash.net>
Date:   Wed Jan 16 12:22:07 2008 +0100

    [NET_SCHED]: sch_ingress: remove unused inner qdisc
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index cb8ba8b..d803cd1 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -27,7 +27,6 @@ static int nf_registered;
 #endif
 
 struct ingress_qdisc_data {
-	struct Qdisc		*q;
 	struct tcf_proto	*filter_list;
 };
 
@@ -178,8 +177,6 @@ static struct nf_hook_ops ing_ops[] __read_mostly = {
 
 static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 {
-	struct ingress_qdisc_data *p = qdisc_priv(sch);
-
 	/* Make sure either netfilter or preferably CLS_ACT is
 	 * compiled in */
 #ifndef CONFIG_NET_CLS_ACT
@@ -202,7 +199,6 @@ static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 	}
 #endif
 #endif
-	p->q = &noop_qdisc;
 	return 0;
 }
 

^ permalink raw reply related

* [NET_SCHED 03/09]: sch_ingress: remove qdisc_priv() wrapper
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit ec46354d4ba87fe98b8510f2d91fff4e106022ec
Author: Patrick McHardy <kaber@trash.net>
Date:   Wed Jan 16 12:22:07 2008 +0100

    [NET_SCHED]: sch_ingress: remove qdisc_priv() wrapper
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index bdda28d..cb8ba8b 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -19,8 +19,6 @@
 #include <net/pkt_sched.h>
 
 
-#define PRIV(sch) qdisc_priv(sch)
-
 /* Thanks to Doron Oz for this hack */
 #ifndef CONFIG_NET_CLS_ACT
 #ifdef CONFIG_NETFILTER
@@ -74,7 +72,7 @@ static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker)
 
 static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch, unsigned long cl)
 {
-	struct ingress_qdisc_data *p = PRIV(sch);
+	struct ingress_qdisc_data *p = qdisc_priv(sch);
 
 	return &p->filter_list;
 }
@@ -83,7 +81,7 @@ static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch, unsigned long cl)
 
 static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 {
-	struct ingress_qdisc_data *p = PRIV(sch);
+	struct ingress_qdisc_data *p = qdisc_priv(sch);
 	struct tcf_result res;
 	int result;
 
@@ -180,7 +178,7 @@ static struct nf_hook_ops ing_ops[] __read_mostly = {
 
 static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 {
-	struct ingress_qdisc_data *p = PRIV(sch);
+	struct ingress_qdisc_data *p = qdisc_priv(sch);
 
 	/* Make sure either netfilter or preferably CLS_ACT is
 	 * compiled in */
@@ -217,7 +215,7 @@ static void ingress_reset(struct Qdisc *sch)
 
 static void ingress_destroy(struct Qdisc *sch)
 {
-	struct ingress_qdisc_data *p = PRIV(sch);
+	struct ingress_qdisc_data *p = qdisc_priv(sch);
 
 	tcf_destroy_chain(p->filter_list);
 }

^ permalink raw reply related

* [NET_SCHED 00/09]: sch_ingress cleanups
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi

These patches clean up sch_ingress by reformatting the code to match the
usual kernel style, removing lots of debugging statements that seem
unnecessary after years of stable operation, removing some "future use"
stuff that doesn't seem likely to ever get used and some minor additional
cleanups.


 net/sched/Kconfig       |    1 +
 net/sched/sch_ingress.c |  232 ++++++++---------------------------------------
 2 files changed, 41 insertions(+), 192 deletions(-)

Patrick McHardy (9):
      [NET_SCHED]: sch_ingress: formatting fixes
      [NET_SCHED]: sch_ingress: remove excessive debugging
      [NET_SCHED]: sch_ingress: remove qdisc_priv() wrapper
      [NET_SCHED]: sch_ingress: remove unused inner qdisc
      [NET_SCHED]: sch_ingress: return proper error code in ingress_graft()
      [NET_SCHED]: sch_ingress: remove unnecessary ops
      [NET_SCHED]: sch_ingress: move dependencies to Kconfig
      [NET_SCHED]: sch_ingress: avoid a few #ifdefs
      [NET_SCHED]: sch_ingress: remove useless printk

^ permalink raw reply

* [NET_SCHED 02/09]: sch_ingress: remove excessive debugging
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit fdb1b974189eeab782ba3595d1ca550a1a5ee79e
Author: Patrick McHardy <kaber@trash.net>
Date:   Wed Jan 16 12:22:06 2008 +0100

    [NET_SCHED]: sch_ingress: remove excessive debugging
    
    Remove excessive debugging statements and some "future use" stuff.
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index 4880d44..bdda28d 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -19,20 +19,6 @@
 #include <net/pkt_sched.h>
 
 
-#undef DEBUG_INGRESS
-
-#ifdef DEBUG_INGRESS  /* control */
-#define DPRINTK(format, args...) printk(KERN_DEBUG format,##args)
-#else
-#define DPRINTK(format, args...)
-#endif
-
-#if 0  /* data */
-#define D2PRINTK(format, args...) printk(KERN_DEBUG format,##args)
-#else
-#define D2PRINTK(format, args...)
-#endif
-
 #define PRIV(sch) qdisc_priv(sch)
 
 /* Thanks to Doron Oz for this hack */
@@ -52,13 +38,6 @@ struct ingress_qdisc_data {
 static int ingress_graft(struct Qdisc *sch, unsigned long arg,
 			 struct Qdisc *new, struct Qdisc **old)
 {
-#ifdef DEBUG_INGRESS
-	struct ingress_qdisc_data *p = PRIV(sch);
-#endif
-
-	DPRINTK("ingress_graft(sch %p,[qdisc %p],new %p,old %p)\n",
-		sch, p, new, old);
-	DPRINTK("\n ingress_graft: You cannot add qdiscs to classes");
 	return 1;
 }
 
@@ -69,11 +48,6 @@ static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)
 
 static unsigned long ingress_get(struct Qdisc *sch, u32 classid)
 {
-#ifdef DEBUG_INGRESS
-	struct ingress_qdisc_data *p = PRIV(sch);
-#endif
-	DPRINTK("ingress_get(sch %p,[qdisc %p],classid %x)\n",
-		sch, p, classid);
 	return TC_H_MIN(classid) + 1;
 }
 
@@ -90,22 +64,12 @@ static void ingress_put(struct Qdisc *sch, unsigned long cl)
 static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent,
 			  struct rtattr **tca, unsigned long *arg)
 {
-#ifdef DEBUG_INGRESS
-	struct ingress_qdisc_data *p = PRIV(sch);
-#endif
-	DPRINTK("ingress_change(sch %p,[qdisc %p],classid %x,parent %x),"
-		"arg 0x%lx\n", sch, p, classid, parent, *arg);
-	DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment");
 	return 0;
 }
 
 static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker)
 {
-#ifdef DEBUG_INGRESS
-	struct ingress_qdisc_data *p = PRIV(sch);
-#endif
-	DPRINTK("ingress_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker);
-	DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment");
+	return;
 }
 
 static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch, unsigned long cl)
@@ -123,9 +87,8 @@ static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 	struct tcf_result res;
 	int result;
 
-	D2PRINTK("ingress_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
 	result = tc_classify(skb, p->filter_list, &res);
-	D2PRINTK("result %d class 0x%04x\n", result, res.classid);
+
 	/*
 	 * Unlike normal "enqueue" functions, ingress_enqueue returns a
 	 * firewall FW_* code.
@@ -150,7 +113,6 @@ static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 		break;
 	}
 #else
-	D2PRINTK("Overriding result to ACCEPT\n");
 	result = NF_ACCEPT;
 	sch->bstats.packets++;
 	sch->bstats.bytes += skb->len;
@@ -161,28 +123,16 @@ static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 
 static struct sk_buff *ingress_dequeue(struct Qdisc *sch)
 {
-/*
-	struct ingress_qdisc_data *p = PRIV(sch);
-	D2PRINTK("ingress_dequeue(sch %p,[qdisc %p])\n",sch,PRIV(p));
-*/
 	return NULL;
 }
 
 static int ingress_requeue(struct sk_buff *skb, struct Qdisc *sch)
 {
-/*
-	struct ingress_qdisc_data *p = PRIV(sch);
-	D2PRINTK("ingress_requeue(skb %p,sch %p,[qdisc %p])\n",skb,sch,PRIV(p));
-*/
 	return 0;
 }
 
 static unsigned int ingress_drop(struct Qdisc *sch)
 {
-#ifdef DEBUG_INGRESS
-	struct ingress_qdisc_data *p = PRIV(sch);
-#endif
-	DPRINTK("ingress_drop(sch %p,[qdisc %p])\n", sch, p);
 	return 0;
 }
 
@@ -198,11 +148,6 @@ static unsigned int ing_hook(unsigned int hook, struct sk_buff *skb,
 	struct net_device *dev = skb->dev;
 	int fwres = NF_ACCEPT;
 
-	DPRINTK("ing_hook: skb %s dev=%s len=%u\n",
-		skb->sk ? "(owned)" : "(unowned)",
-		skb->dev ? skb->dev->name : "(no dev)",
-		skb->len);
-
 	if (dev->qdisc_ingress) {
 		spin_lock(&dev->ingress_lock);
 		if ((q = dev->qdisc_ingress) != NULL)
@@ -259,26 +204,13 @@ static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 	}
 #endif
 #endif
-
-	DPRINTK("ingress_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt);
 	p->q = &noop_qdisc;
 	return 0;
 }
 
 static void ingress_reset(struct Qdisc *sch)
 {
-	struct ingress_qdisc_data *p = PRIV(sch);
-
-	DPRINTK("ingress_reset(sch %p,[qdisc %p])\n", sch, p);
-
-/*
-#if 0
-*/
-/* for future use */
-	qdisc_reset(p->q);
-/*
-#endif
-*/
+	return;
 }
 
 /* ------------------------------------------------------------- */
@@ -287,12 +219,7 @@ static void ingress_destroy(struct Qdisc *sch)
 {
 	struct ingress_qdisc_data *p = PRIV(sch);
 
-	DPRINTK("ingress_destroy(sch %p,[qdisc %p])\n", sch, p);
 	tcf_destroy_chain(p->filter_list);
-#if 0
-/* for future use */
-	qdisc_destroy(p->q);
-#endif
 }
 
 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)

^ permalink raw reply related

* [NET_SCHED 01/09]: sch_ingress: formatting fixes
From: Patrick McHardy @ 2008-01-20 14:32 UTC (permalink / raw)
  To: davem; +Cc: netdev, Patrick McHardy, hadi
In-Reply-To: <20080120143130.27801.94188.sendpatchset@localhost.localdomain>

commit e237be7bc5b3009d1021e0764d1745e9a4c3cefb
Author: Patrick McHardy <kaber@trash.net>
Date:   Wed Jan 16 12:22:06 2008 +0100

    [NET_SCHED]: sch_ingress: formatting fixes
    
    Signed-off-by: Patrick McHardy <kaber@trash.net>

diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index 89c32a9..4880d44 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -22,23 +22,20 @@
 #undef DEBUG_INGRESS
 
 #ifdef DEBUG_INGRESS  /* control */
-#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#define DPRINTK(format, args...) printk(KERN_DEBUG format,##args)
 #else
-#define DPRINTK(format,args...)
+#define DPRINTK(format, args...)
 #endif
 
 #if 0  /* data */
-#define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#define D2PRINTK(format, args...) printk(KERN_DEBUG format,##args)
 #else
-#define D2PRINTK(format,args...)
+#define D2PRINTK(format, args...)
 #endif
 
-
 #define PRIV(sch) qdisc_priv(sch)
 
-
-/* Thanks to Doron Oz for this hack
-*/
+/* Thanks to Doron Oz for this hack */
 #ifndef CONFIG_NET_CLS_ACT
 #ifdef CONFIG_NETFILTER
 static int nf_registered;
@@ -50,12 +47,10 @@ struct ingress_qdisc_data {
 	struct tcf_proto	*filter_list;
 };
 
-
 /* ------------------------- Class/flow operations ------------------------- */
 
-
-static int ingress_graft(struct Qdisc *sch,unsigned long arg,
-    struct Qdisc *new,struct Qdisc **old)
+static int ingress_graft(struct Qdisc *sch, unsigned long arg,
+			 struct Qdisc *new, struct Qdisc **old)
 {
 #ifdef DEBUG_INGRESS
 	struct ingress_qdisc_data *p = PRIV(sch);
@@ -67,37 +62,33 @@ static int ingress_graft(struct Qdisc *sch,unsigned long arg,
 	return 1;
 }
 
-
 static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)
 {
 	return NULL;
 }
 
-
-static unsigned long ingress_get(struct Qdisc *sch,u32 classid)
+static unsigned long ingress_get(struct Qdisc *sch, u32 classid)
 {
 #ifdef DEBUG_INGRESS
 	struct ingress_qdisc_data *p = PRIV(sch);
 #endif
-	DPRINTK("ingress_get(sch %p,[qdisc %p],classid %x)\n", sch, p, classid);
+	DPRINTK("ingress_get(sch %p,[qdisc %p],classid %x)\n",
+		sch, p, classid);
 	return TC_H_MIN(classid) + 1;
 }
 
-
 static unsigned long ingress_bind_filter(struct Qdisc *sch,
-    unsigned long parent, u32 classid)
+					 unsigned long parent, u32 classid)
 {
 	return ingress_get(sch, classid);
 }
 
-
 static void ingress_put(struct Qdisc *sch, unsigned long cl)
 {
 }
 
-
 static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent,
-    struct rtattr **tca, unsigned long *arg)
+			  struct rtattr **tca, unsigned long *arg)
 {
 #ifdef DEBUG_INGRESS
 	struct ingress_qdisc_data *p = PRIV(sch);
@@ -108,9 +99,7 @@ static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent,
 	return 0;
 }
 
-
-
-static void ingress_walk(struct Qdisc *sch,struct qdisc_walker *walker)
+static void ingress_walk(struct Qdisc *sch, struct qdisc_walker *walker)
 {
 #ifdef DEBUG_INGRESS
 	struct ingress_qdisc_data *p = PRIV(sch);
@@ -119,19 +108,16 @@ static void ingress_walk(struct Qdisc *sch,struct qdisc_walker *walker)
 	DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment");
 }
 
-
-static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch,unsigned long cl)
+static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch, unsigned long cl)
 {
 	struct ingress_qdisc_data *p = PRIV(sch);
 
 	return &p->filter_list;
 }
 
-
 /* --------------------------- Qdisc operations ---------------------------- */
 
-
-static int ingress_enqueue(struct sk_buff *skb,struct Qdisc *sch)
+static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 {
 	struct ingress_qdisc_data *p = PRIV(sch);
 	struct tcf_result res;
@@ -148,20 +134,20 @@ static int ingress_enqueue(struct sk_buff *skb,struct Qdisc *sch)
 	sch->bstats.packets++;
 	sch->bstats.bytes += skb->len;
 	switch (result) {
-		case TC_ACT_SHOT:
-			result = TC_ACT_SHOT;
-			sch->qstats.drops++;
-			break;
-		case TC_ACT_STOLEN:
-		case TC_ACT_QUEUED:
-			result = TC_ACT_STOLEN;
-			break;
-		case TC_ACT_RECLASSIFY:
-		case TC_ACT_OK:
-			skb->tc_index = TC_H_MIN(res.classid);
-		default:
-			result = TC_ACT_OK;
-			break;
+	case TC_ACT_SHOT:
+		result = TC_ACT_SHOT;
+		sch->qstats.drops++;
+		break;
+	case TC_ACT_STOLEN:
+	case TC_ACT_QUEUED:
+		result = TC_ACT_STOLEN;
+		break;
+	case TC_ACT_RECLASSIFY:
+	case TC_ACT_OK:
+		skb->tc_index = TC_H_MIN(res.classid);
+	default:
+		result = TC_ACT_OK;
+		break;
 	}
 #else
 	D2PRINTK("Overriding result to ACCEPT\n");
@@ -173,7 +159,6 @@ static int ingress_enqueue(struct sk_buff *skb,struct Qdisc *sch)
 	return result;
 }
 
-
 static struct sk_buff *ingress_dequeue(struct Qdisc *sch)
 {
 /*
@@ -183,8 +168,7 @@ static struct sk_buff *ingress_dequeue(struct Qdisc *sch)
 	return NULL;
 }
 
-
-static int ingress_requeue(struct sk_buff *skb,struct Qdisc *sch)
+static int ingress_requeue(struct sk_buff *skb, struct Qdisc *sch)
 {
 /*
 	struct ingress_qdisc_data *p = PRIV(sch);
@@ -204,8 +188,7 @@ static unsigned int ingress_drop(struct Qdisc *sch)
 
 #ifndef CONFIG_NET_CLS_ACT
 #ifdef CONFIG_NETFILTER
-static unsigned int
-ing_hook(unsigned int hook, struct sk_buff *skb,
+static unsigned int ing_hook(unsigned int hook, struct sk_buff *skb,
 			     const struct net_device *indev,
 			     const struct net_device *outdev,
 			     int (*okfn)(struct sk_buff *))
@@ -213,7 +196,7 @@ ing_hook(unsigned int hook, struct sk_buff *skb,
 
 	struct Qdisc *q;
 	struct net_device *dev = skb->dev;
-	int fwres=NF_ACCEPT;
+	int fwres = NF_ACCEPT;
 
 	DPRINTK("ing_hook: skb %s dev=%s len=%u\n",
 		skb->sk ? "(owned)" : "(unowned)",
@@ -247,16 +230,15 @@ static struct nf_hook_ops ing_ops[] __read_mostly = {
 		.priority       = NF_IP6_PRI_FILTER + 1,
 	},
 };
-
 #endif
 #endif
 
-static int ingress_init(struct Qdisc *sch,struct rtattr *opt)
+static int ingress_init(struct Qdisc *sch, struct rtattr *opt)
 {
 	struct ingress_qdisc_data *p = PRIV(sch);
 
-/* Make sure either netfilter or preferably CLS_ACT is
-* compiled in */
+	/* Make sure either netfilter or preferably CLS_ACT is
+	 * compiled in */
 #ifndef CONFIG_NET_CLS_ACT
 #ifndef CONFIG_NETFILTER
 	printk("You MUST compile classifier actions into the kernel\n");
@@ -278,12 +260,11 @@ static int ingress_init(struct Qdisc *sch,struct rtattr *opt)
 #endif
 #endif
 
-	DPRINTK("ingress_init(sch %p,[qdisc %p],opt %p)\n",sch,p,opt);
+	DPRINTK("ingress_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt);
 	p->q = &noop_qdisc;
 	return 0;
 }
 
-
 static void ingress_reset(struct Qdisc *sch)
 {
 	struct ingress_qdisc_data *p = PRIV(sch);
@@ -302,9 +283,6 @@ static void ingress_reset(struct Qdisc *sch)
 
 /* ------------------------------------------------------------- */
 
-
-/* ------------------------------------------------------------- */
-
 static void ingress_destroy(struct Qdisc *sch)
 {
 	struct ingress_qdisc_data *p = PRIV(sch);
@@ -317,13 +295,12 @@ static void ingress_destroy(struct Qdisc *sch)
 #endif
 }
 
-
 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	unsigned char *b = skb_tail_pointer(skb);
 	struct rtattr *rta;
 
-	rta = (struct rtattr *) b;
+	rta = (struct rtattr *)b;
 	RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
 	rta->rta_len = skb_tail_pointer(skb) - b;
 	return skb->len;
@@ -339,16 +316,13 @@ static const struct Qdisc_class_ops ingress_class_ops = {
 	.get		=	ingress_get,
 	.put		=	ingress_put,
 	.change		=	ingress_change,
-	.delete		=	NULL,
 	.walk		=	ingress_walk,
 	.tcf_chain	=	ingress_find_tcf,
 	.bind_tcf	=	ingress_bind_filter,
 	.unbind_tcf	=	ingress_put,
-	.dump		=	NULL,
 };
 
 static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
-	.next		=	NULL,
 	.cl_ops		=	&ingress_class_ops,
 	.id		=	"ingress",
 	.priv_size	=	sizeof(struct ingress_qdisc_data),
@@ -359,7 +333,6 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
 	.init		=	ingress_init,
 	.reset		=	ingress_reset,
 	.destroy	=	ingress_destroy,
-	.change		=	NULL,
 	.dump		=	ingress_dump,
 	.owner		=	THIS_MODULE,
 };
@@ -375,6 +348,7 @@ static int __init ingress_module_init(void)
 
 	return ret;
 }
+
 static void __exit ingress_module_exit(void)
 {
 	unregister_qdisc(&ingress_qdisc_ops);
@@ -385,6 +359,7 @@ static void __exit ingress_module_exit(void)
 #endif
 #endif
 }
+
 module_init(ingress_module_init)
 module_exit(ingress_module_exit)
 MODULE_LICENSE("GPL");

^ permalink raw reply related

* Re: sctp use-uninitialized warning in net-2.6.25
From: David Miller @ 2008-01-20 14:11 UTC (permalink / raw)
  To: vyasevich; +Cc: akpm, netdev
In-Reply-To: <479326B1.3090009@verizon.net>

From: Vlad Yasevich <vyasevich@verizon.net>
Date: Sun, 20 Jan 2008 05:47:13 -0500

> Anyway, here is a patch that sets the correct value.

Applied, but I had to fix up the trailing whitespace on
line 29 of your patch. :-/

Thanks.

^ permalink raw reply

* net 2.6.25 is broken when building with wireless/iwlwifi
From: Ian Brown @ 2008-01-20 14:01 UTC (permalink / raw)
  To: netdev

Hello,
net 2.6.25 is broken when building with the wireless/iwlwifi
subtree;  we get the following errors (we noticed that iwl_down() and
iwl_resume()
implementation do exist in garzik tree, but there are more differences
so a simple
patch with only adding these two methods might be risky)

...
...

  CHK     include/linux/version.h
  CHK     include/linux/utsrelease.h
  CALL    scripts/checksyscalls.sh
  CHK     include/linux/compile.h
  CC [M]  drivers/net/wireless/iwlwifi/iwl3945-base.o
drivers/net/wireless/iwlwifi/iwl3945-base.c: In function 'iwl3945_pci_remove':
drivers/net/wireless/iwlwifi/iwl3945-base.c:8639: error: implicit
declaration of function 'iwl_down'
drivers/net/wireless/iwlwifi/iwl3945-base.c: In function 'iwl3945_pci_resume':
drivers/net/wireless/iwlwifi/iwl3945-base.c:8779: error: implicit
declaration of function 'iwl_resume'
make[4]: *** [drivers/net/wireless/iwlwifi/iwl3945-base.o] Error 1
make[3]: *** [drivers/net/wireless/iwlwifi] Error 2
make[2]: *** [drivers/net/wireless] Error 2
make[1]: *** [drivers/net] Error 2
make: *** [drivers] Error 2
...
...
IB

^ permalink raw reply

* Re: [Bugme-new] [Bug 9758] New: net_device refcnt bug when NFQUEUEing bridged packets
From: Patrick McHardy @ 2008-01-20 13:58 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev
In-Reply-To: <20080116105632.2532812c@speedy>

Stephen Hemminger wrote:
> This looks good, but you could use a structure assignment rather memcpy
> (just a personal style preference because assignment is typed and memcpy
> is not).

That doesn't work in this case since nf_bridge_info contains

         unsigned long data[32 / sizeof(unsigned long)];

and I don't want to assign each array element seperately.

^ permalink raw reply

* Re: sctp use-uninitialized warning in net-2.6.25
From: Vlad Yasevich @ 2008-01-20 10:47 UTC (permalink / raw)
  To: David Miller; +Cc: akpm, netdev
In-Reply-To: <20080118.203723.184002414.davem@davemloft.net>

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

David Miller wrote:
> From: Vlad Yasevich <vladislav.yasevich@hp.com>
> Date: Fri, 18 Jan 2008 21:17:56 -0500
> 
>> Hmm... in the code I am looking at, it's set in both zero and
>> non-zero cases so it does solve the issue.
>>
>> So does initializing it to NO_ERROR like you did.
> 
> Here is the code block in question in net-2.6.25:
> 
> 	/* Verify the INIT chunk before processing it. */
> 	err_chunk = NULL;
> 	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
> 			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
> 			      &err_chunk)) {
>  ...
> 		if (err_chunk) {
>  ...
> 			if (packet) {
> 				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
> 						SCTP_PACKET(packet));
> 				SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
> 				error = SCTP_ERROR_INV_PARAM;
> 			} else {
> 				error = SCTP_ERROR_NO_RESOURCE;
> 			}
> 		}
>  ...
> 		return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED,
> 						asoc, chunk->transport);
> 
> If err_chunk == NULL at the "if (err_chunk)" test, error
> will be left uninitialized, even after being moved as you
> have suggested (right after the sctp_verify_init() call).
> 
> Thanks.
> 

Hi David

Thanks for beating into my thick scull that this is in 2.6.25.  I missed that
initially.

Anyway, here is a patch that sets the correct value.

-vlad

[-- Attachment #2: 0001-SCTP-Correctly-initialize-error-when-parameter-val.patch --]
[-- Type: text/x-patch, Size: 1821 bytes --]

>From 4788563632fae22023fc0d75b525d2d5f8e0735b Mon Sep 17 00:00:00 2001
From: Vlad Yasevich <vladislav.yasevich@hp.com>
Date: Sun, 20 Jan 2008 00:22:06 -0500
Subject: [PATCH] [SCTP] Correctly initialize error when parameter validation failed.

When parameter validation fails, there should be error causes
that specify what type of failure we've encountered.  If the
causes are not there, we lacked memory to allocated them.  Thus
make that the default value for the error.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
---
 net/sctp/sm_statefuns.c |    7 +++----
 1 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 6e12757..da5497e 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -481,7 +481,6 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
 	sctp_init_chunk_t *initchunk;
 	struct sctp_chunk *err_chunk;
 	struct sctp_packet *packet;
-	sctp_error_t error = SCTP_ERROR_NO_ERROR;
 
 	if (!sctp_vtag_verify(chunk, asoc))
 		return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
@@ -506,6 +505,8 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
 			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
 			      &err_chunk)) {
 
+		sctp_error_t error = SCTP_ERROR_NO_RESOURCE;
+
 		/* This chunk contains fatal error. It is to be discarded.
 		 * Send an ABORT, with causes.  If there are no causes,
 		 * then there wasn't enough memory.  Just terminate
@@ -525,9 +526,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
 						SCTP_PACKET(packet));
 				SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
 				error = SCTP_ERROR_INV_PARAM;
-			} else {
-				error = SCTP_ERROR_NO_RESOURCE;
-			}
+			} 
 		}
 
 		/* SCTP-AUTH, Section 6.3:
-- 
1.5.2.5


^ permalink raw reply related

* Re: [Bugme-new] [Bug 9778] New: unregister_netdevice: waiting for [device] to become free
From: David Miller @ 2008-01-20 10:30 UTC (permalink / raw)
  To: akpm; +Cc: xemul, bugme-daemon, nigel, netdev
In-Reply-To: <20080119165802.2846a28e.akpm@linux-foundation.org>

From: Andrew Morton <akpm@linux-foundation.org>
Date: Sat, 19 Jan 2008 16:58:02 -0800

> ouch.

Yep, several people are hitting this it seems.

If Pavel doesn't provide a fix or direction soon I'll just revert.

^ permalink raw reply

* Re: [REGRESSION] 2.6.24-rc7: e1000: Detected Tx Unit Hang
From: Andrey Rahmatullin @ 2008-01-20  9:28 UTC (permalink / raw)
  To: Brandeburg, Jesse
  Cc: David Miller, Robert.Olsson, elendil, slavon, netdev,
	linux-kernel
In-Reply-To: <36D9DB17C6DE9E40B059440DB8D95F52043A156F@orsmsx418.amr.corp.intel.com>

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

On Sun, Jan 20, 2008 at 01:20:11AM -0800, Brandeburg, Jesse wrote:
> I continually get the
>  kernel: unregister_netdevice: waiting for eth2 to become free. Usage
> count = 1
http://bugzilla.kernel.org/show_bug.cgi?id=9778

-- 
WBR, wRAR (ALT Linux Team)

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* RE: [REGRESSION] 2.6.24-rc7: e1000: Detected Tx Unit Hang
From: Brandeburg, Jesse @ 2008-01-20  9:20 UTC (permalink / raw)
  To: David Miller, Robert.Olsson; +Cc: elendil, slavon, netdev, linux-kernel
In-Reply-To: <20080118.053712.195382841.davem@davemloft.net>

David Miller wrote:
> From: Robert Olsson <Robert.Olsson@data.slu.se>
> Date: Fri, 18 Jan 2008 14:00:57 +0100
> 
>>  I don't understand the idea with semaphore for enabling/disabling
>>  irq's either the overall logic must safer/better without it.
> 
> They must have had code paths where they didn't know if IRQs were
> enabled or not already, so they tried to create something which
> approximates the:
> 
> 	local_irq_save(flags);
> 	local_irq_restore(flags);
> 
> constructs we have for CPU interrupts, so they could go:
> 
> 	e1000_irq_disable();
> 	/* ... */
> 	e1000_irq_enable();
> 
> and this would work even if the caller was running
> with e1000 interrupts disabled already.
> 
> Or, something like that... it is indeed confusing.
> 
> Anyways, yes it's totally bogus and should be removed.

I agree, bogus, and in fact I've already removed it from our development
version of ixgbe.  Right now I wanted to report I can't remove e1000 at
all on 2.6.24-rc8+git

I continually get the
 kernel: unregister_netdevice: waiting for eth2 to become free. Usage
count = 1

Where 2.6.24-rc5 e1000 is okay still.  Seems like maybe we are still
missing a netif_rx_complete or a napi_disable somewhere.

I don't think this problem has anything to do with the irq_sem right
now.  Something is still badly broken.  I am just using the interface
regularly (no heavy load) and I can't unload the module.

Jesse

^ permalink raw reply

* Re: ESFQ (or SFQ updates) mainline status?
From: Stephen Hemminger @ 2008-01-20  4:50 UTC (permalink / raw)
  To: netdev
In-Reply-To: <1199474475.3820.65.camel@sally>

On Fri, 04 Jan 2008 11:21:15 -0800
Jeff Gustafson <ncjeffgus@zimage.com> wrote:

> Hi all,
> 	I have a question about the status of the spiffy updates to SFQ.  I
> *really* like the ESFQ idea.  I appears to be exactly what I'm looking
> for.  From what I can tell from this mailing list, SFQ is getting some
> or all of the ESFQ features.  Although the ESFQ web site gives detailed
> information about the status for inclusion in mainline, I am having
> trouble finding out exactly what (if any) parts of ESFQ got into SFQ in
> mainline.
> 	Did any of the ESFQ-like enhancements for SFQ reach .23 or do I still
> need to patch?  I am running a stripped-down Fedora 8 box and it would
> be very convenient if the patches were already in .23 or added by the
> Fedora team.
> 	I tried to run the ESFQ command against SFQ, but the command was
> rejected:
> 
> /sbin/tc qdisc add dev eth1 parent 1:12 handle 12: sfq perturb 10 hash
> ctnatchg
> 
> 	It appears the even if the kernel does have the patches, the shipped
> version tc does not.
> 
> 				..Jeff

The mainline kernel doesn't have ESFQ, so the distribute version of iproute2
does not. If your distro chooses to add ESFQ to their kernel, then they
need to extend tc as well.


-- 
Stephen Hemminger <stephen.hemminger@vyatta.com>


^ permalink raw reply

* Re: [Bugme-new] [Bug 9778] New: unregister_netdevice: waiting for [device] to become free
From: Andrew Morton @ 2008-01-20  0:58 UTC (permalink / raw)
  To: David S. Miller, Pavel Emelyanov
  Cc: bugme-daemon@kernel-bugs.osdl.org, nigel, netdev
In-Reply-To: <bug-9778-10286@http.bugzilla.kernel.org/>

On Sat, 19 Jan 2008 12:20:28 -0800 (PST) bugme-daemon@bugzilla.kernel.org wrote:

> http://bugzilla.kernel.org/show_bug.cgi?id=9778
> 
>            Summary: unregister_netdevice: waiting for [device] to become
>                     free
>            Product: Networking
>            Version: 2.5
>      KernelVersion: 2.6.24-rc8
>           Platform: All
>         OS/Version: Linux
>               Tree: Mainline
>             Status: NEW
>           Severity: blocking
>           Priority: P1
>          Component: Other
>         AssignedTo: acme@ghostprotocols.net
>         ReportedBy: nigel@suspend2.net
> 
> 
> Latest working kernel version: 2.6.24-rc7
> Earliest failing kernel version: 2.6.24-rc8
> Distribution: Ubuntu Gutsy
> Hardware Environment: AMD64, Atheros 5212 chipset
> Software Environment: Fairly recent madwifi svn.
> Problem Description: Patch "[PATCH][NEIGH] Fix race between neigh_parms_release
> and neightbl_fill_parms" (9cd40029423701c376391da59d2c6469672b4bed) causes the
> error described in the subject line to occur when seeking to unload a device.
> Also seen by some of my TuxOnIce users with e1000 and ppp42.
> 
> Steps to reproduce: Run a kernel with the above patch, bring up a (nonloopback)
> interface, seek to take it down.
> 

ouch.

^ permalink raw reply

* Re: [PATCH] [IrDA] af_irda memory leak fixes
From: Jesper Juhl @ 2008-01-20  0:23 UTC (permalink / raw)
  To: Samuel Ortiz; +Cc: David S. Miller, irda-users, netdev
In-Reply-To: <20080118075322.GB3063@sortiz.org>

On 18/01/2008, Samuel Ortiz <samuel@sortiz.org> wrote:
> Hi Dave,
>
> Here goes an IrDA patch against your latest net-2.6 tree.
>
> This patch fixes some af_irda memory leaks.
> It also checks for irias_new_obect() return value.
>
> Signed-off-by: Jesper Juhl <jesper.juhl@gmail.com>
> Signed-off-by: Samuel Ortiz <samuel@sortiz.org>
...

Looks good Samuel.
Thank you for improving on my original suggested patch.

Now let's hope someone queues it up for mainline :)

-- 
Jesper Juhl <jesper.juhl@gmail.com>
Don't top-post  http://www.catb.org/~esr/jargon/html/T/top-post.html
Plain text mails only, please      http://www.expita.com/nomime.html

^ permalink raw reply

* [PATCH 14/14] Add new driver 'rndis_wext' for wireless RNDIS devices.
From: Jussi Kivilinna @ 2008-01-20  0:15 UTC (permalink / raw)
  To: linux-wireless-u79uwXL29TY76Z2rM5mHXA
  Cc: bjd-a1rhEgazXTw, dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f,
	netdev-u79uwXL29TY76Z2rM5mHXA

New driver for wireless RNDIS devices. So far only known chip that uses
wireless RNDIS is Broadcom 4320.

List of devices detected by driver:

  Buffalo WLI-U2-KG125S
  U.S. Robotics USR5421
  Belkin F5D7051
  Linksys WUSB54GSv2
  Linksys WUSB54GSC
  Asus WL169gE
  Eminent EM4045
  BT Voyager 1055
  Linksys WUSB54GSv1
  U.S. Robotics USR5420
  BUFFALO WLI-USB-G54

Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
---

 MAINTAINERS                       |    6 
 drivers/net/wireless/Kconfig      |   28 
 drivers/net/wireless/Makefile     |    2 
 drivers/net/wireless/rndis_wext.c | 2570 +++++++++++++++++++++++++++++++++++++
 4 files changed, 2606 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2340cfb..93998ee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4031,6 +4031,12 @@ L:	video4linux-list-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org
 W:	http://www.linux-projects.org
 S:	Maintained
 
+USB WIRELESS RNDIS DRIVER (rndis_wext)
+P:	Jussi Kivilinna
+M:	jussi.kivilinna-E01nCVcF24I@public.gmane.org
+L:	linux-wireless-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S:	Maintained
+
 USB ZC0301 DRIVER
 P:	Luca Risolia
 M:	luca.risolia-CtsJT0KQ3bsYBs+V2TNivA@public.gmane.org
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index c98fc62..8702ca7 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -545,6 +545,34 @@ config USB_ZD1201
 	  To compile this driver as a module, choose M here: the
 	  module will be called zd1201.
 
+config USB_NET_RNDIS_WEXT
+	tristate "Wireless RNDIS USB support"
+	depends on USB && WLAN_80211 && EXPERIMENTAL
+	select USB_USBNET
+	select USB_NET_CDCETHER
+	select USB_NET_RNDIS_HOST
+	select WIRELESS_EXT
+	---help---
+	  This is a driver for wireless RNDIS devices.
+	  These are USB based adapters found in devices such as:
+
+	  Buffalo WLI-U2-KG125S
+	  U.S. Robotics USR5421
+	  Belkin F5D7051
+	  Linksys WUSB54GSv2
+	  Linksys WUSB54GSC
+	  Asus WL169gE
+	  Eminent EM4045
+	  BT Voyager 1055
+	  Linksys WUSB54GSv1
+	  U.S. Robotics USR5420
+	  BUFFALO WLI-USB-G54
+
+	  All of these devices are based on Broadcom 4320 chip which is the
+	  only wireless RNDIS chip known to date.
+
+	  If you choose to build a module, it'll be called rndis_wext.
+
 config RTL8187
 	tristate "Realtek 8187 USB support"
 	depends on MAC80211 && USB && WLAN_80211 && EXPERIMENTAL
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 6f32b53..8504dcb 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -44,6 +44,8 @@ obj-$(CONFIG_ZD1211RW)		+= zd1211rw/
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
 obj-$(CONFIG_PCMCIA_WL3501)	+= wl3501_cs.o
 
+obj-$(CONFIG_USB_NET_RNDIS_WEXT)	+= rndis_wext.o
+
 obj-$(CONFIG_USB_ZD1201)	+= zd1201.o
 obj-$(CONFIG_LIBERTAS)		+= libertas/
 
diff --git a/drivers/net/wireless/rndis_wext.c b/drivers/net/wireless/rndis_wext.c
new file mode 100644
index 0000000..2c3d5c4
--- /dev/null
+++ b/drivers/net/wireless/rndis_wext.c
@@ -0,0 +1,2570 @@
+/*
+ * Driver for RNDIS based wireless USB devices.
+ *
+ * Copyright (C) 2007 by Bjorge Dijkstra <bjd-a1rhEgazXTw@public.gmane.org>
+ * Copyright (C) 2008 by Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
+ *
+ * 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 <linux/ctype.h>
+#include <linux/spinlock.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+#include <linux/usb/usbnet.h>
+#include <linux/usb/rndis_host.h>
+
+
+/* NOTE: All these are settings for Broadcom chipset */
+static char modparam_country[4] = "EU";
+module_param_string(country, modparam_country, 4, 0444);
+MODULE_PARM_DESC(country, "Country code (ISO 3166-1 alpha-2), default: EU");
+
+static int modparam_frameburst = 1;
+module_param_named(frameburst, modparam_frameburst, int, 0444);
+MODULE_PARM_DESC(frameburst, "enable frame bursting (default: on)");
+
+static int modparam_afterburner = 0;
+module_param_named(afterburner, modparam_afterburner, int, 0444);
+MODULE_PARM_DESC(afterburner,
+	"enable afterburner aka '125 High Speed Mode' (default: off)");
+
+static int modparam_power_save = 0;
+module_param_named(power_save, modparam_power_save, int, 0444);
+MODULE_PARM_DESC(power_save,
+	"set power save mode: 0=off, 1=on, 2=fast (default: off)");
+
+static int modparam_power_output = 3;
+module_param_named(power_output, modparam_power_output, int, 0444);
+MODULE_PARM_DESC(power_output,
+	"set power output: 0=25%, 1=50%, 2=75%, 3=100% (default: 100%)");
+
+static int modparam_roamtrigger = -70;
+module_param_named(roamtrigger, modparam_roamtrigger, int, 0444);
+MODULE_PARM_DESC(roamtrigger,
+	"set roaming dBm trigger: -80=optimize for distance, "
+				"-60=bandwidth (default: -70)");
+
+static int modparam_roamdelta = 1;
+module_param_named(roamdelta, modparam_roamdelta, int, 0444);
+MODULE_PARM_DESC(roamdelta,
+	"set roaming tendency: 0=aggressive, 1=moderate, "
+				"2=conservative (default: moderate)");
+
+static int modparam_workaround_interval = 500;
+module_param_named(workaround_interval, modparam_workaround_interval,
+							int, 0444);
+MODULE_PARM_DESC(workaround_interval,
+	"set stall workaround interval in msecs (default: 500)");
+
+
+/* various RNDIS OID defs */
+#define OID_GEN_LINK_SPEED			ccpu2(0x00010107)
+#define OID_GEN_RNDIS_CONFIG_PARAMETER		ccpu2(0x0001021b)
+
+#define OID_GEN_XMIT_OK				ccpu2(0x00020101)
+#define OID_GEN_RCV_OK				ccpu2(0x00020102)
+#define OID_GEN_XMIT_ERROR			ccpu2(0x00020103)
+#define OID_GEN_RCV_ERROR			ccpu2(0x00020104)
+#define OID_GEN_RCV_NO_BUFFER			ccpu2(0x00020105)
+
+#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_3_MAXIMUM_LIST_SIZE		ccpu2(0x01010104)
+
+#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)
+
+
+/* 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.
+ * 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;
+};
+
+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
+
+#define WORK_CONNECTION_EVENT	(1<<0)
+#define WORK_SET_MULTICAST_LIST	(1<<1)
+
+/* 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;
+	spinlock_t stats_lock;
+	unsigned long work_pending;
+
+	struct iw_statistics iwstats;
+	struct iw_statistics privstats;
+	int  nick_len;
+	char nick[32];
+
+	int caps;
+	int mode;
+	int radio_on;
+	int multicast_size;
+
+	/* module parameters */
+	char param_country[4];
+	int param_frameburst;
+	int param_afterburner;
+	int param_power_save;
+	int param_power_output;
+	int param_roamtrigger;
+	int param_roamdelta;
+	u32 param_workaround_interval;
+
+	/* 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 struct rndis_wext_private *get_rndis_wext_priv(struct usbnet *dev)
+{
+	return (struct rndis_wext_private *)dev->driver_priv;
+}
+
+
+static u32 get_bcm4320_power(struct rndis_wext_private *priv)
+{
+	return BCM4320_DEFAULT_TXPOWER *
+		bcm4320_power_output[priv->param_power_output] / 100;
+}
+
+
+/* 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;
+
+#ifdef DEBUG
+	info_len += 12;
+#endif
+	infobuf = kmalloc(info_len, GFP_KERNEL);
+	if (!infobuf)
+		return -ENOMEM;
+
+#ifdef DEBUG
+	info_len -= 12;
+	/* 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 paramater failed, %d.", ret);
+
+	kfree(infobuf);
+	return ret;
+}
+
+static int rndis_set_config_parameter_str(struct usbnet *dev,
+						char *param, char *value)
+{
+	return(rndis_set_config_parameter(dev, param, 2, value));
+}
+
+/*static 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 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 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 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
+ */
+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 random ssid 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] = 0x1;
+		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;
+}
+
+
+static void set_multicast_list(struct usbnet *usbdev)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	struct dev_mc_list *mclist;
+	__le32 filter;
+	int ret, i, size;
+	char *buf;
+
+	filter = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST;
+
+	if (usbdev->net->flags & IFF_PROMISC) {
+		filter |= RNDIS_PACKET_TYPE_PROMISCUOUS |
+			RNDIS_PACKET_TYPE_ALL_LOCAL;
+	} else if (usbdev->net->flags & IFF_ALLMULTI ||
+		   usbdev->net->mc_count > priv->multicast_size) {
+		filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
+	} else if (usbdev->net->mc_count > 0) {
+		size = min(priv->multicast_size, usbdev->net->mc_count);
+		buf = kmalloc(size * ETH_ALEN, GFP_KERNEL);
+		if (!buf) {
+			devwarn(usbdev,
+				"couldn't alloc %d bytes of memory",
+				size * ETH_ALEN);
+			return;
+		}
+
+		mclist = usbdev->net->mc_list;
+		for (i = 0; i < size && mclist; mclist = mclist->next) {
+			if (mclist->dmi_addrlen != ETH_ALEN)
+				continue;
+
+			memcpy(buf + i * ETH_ALEN, mclist->dmi_addr, ETH_ALEN);
+			i++;
+		}
+
+		ret = rndis_set_oid(usbdev, OID_802_3_MULTICAST_LIST, buf,
+								i * ETH_ALEN);
+		if (ret == 0 && i > 0)
+			filter |= RNDIS_PACKET_TYPE_MULTICAST;
+		else
+			filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
+
+		devdbg(usbdev, "OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d",
+						i, priv->multicast_size, ret);
+
+		kfree(buf);
+	}
+
+	ret = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter,
+							sizeof(filter));
+	if (ret < 0) {
+		devwarn(usbdev, "couldn't set packet filter: %08x",
+							le32_to_cpu(filter));
+	}
+
+	devdbg(usbdev, "OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d",
+						le32_to_cpu(filter), 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;
+
+		/* ~35%	more with afterburner */
+		if (priv->param_afterburner)
+			range->throughput = range->throughput / 100 * 135;
+	} 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;
+
+	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;
+	DECLARE_MAC_BUF(mac);
+
+	ret = get_bssid(usbdev, bssid);
+
+	if (ret == 0)
+		devdbg(usbdev, "SIOCGIWAP: %s", print_mac(mac, bssid));
+	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;
+	DECLARE_MAC_BUF(mac);
+
+	devdbg(usbdev, "SIOCSIWAP: %s", print_mac(mac, bssid));
+
+	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];
+	DECLARE_MAC_BUF(mac);
+
+	bssid_len = le32_to_cpu(bssid->Length);
+
+	devdbg(usbdev, "BSSID %s", print_mac(mac, bssid->MacAddress));
+	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 = 0;
+	int ret = 0;
+
+	if (!wrqu->txpower.disabled) {
+		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 rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+	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, priv->radio_on);
+	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);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->stats_lock, flags);
+	memcpy(&priv->iwstats, &priv->privstats, sizeof(priv->iwstats));
+	spin_unlock_irqrestore(&priv->stats_lock, flags);
+
+	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_worker(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;
+
+	if (test_and_clear_bit(WORK_CONNECTION_EVENT, &priv->work_pending)) {
+		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);
+		}
+	}
+
+	if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending))
+		set_multicast_list(usbdev);
+}
+
+static void rndis_wext_set_multicast_list(struct net_device *dev)
+{
+	struct usbnet *usbdev = dev->priv;
+	struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+	set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending);
+	queue_work(priv->workqueue, &priv->work);
+}
+
+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 */
+		set_bit(WORK_CONNECTION_EVENT, &priv->work_pending);
+		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)
+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 iw_statistics iwstats;
+	__le32 rssi, tmp;
+	int len, ret, bitrate, j;
+	unsigned long flags;
+	int update_jiffies = STATS_UPDATE_JIFFIES;
+	void *buf;
+
+	spin_lock_irqsave(&priv->stats_lock, flags);
+	memcpy(&iwstats, &priv->privstats, sizeof(iwstats));
+	spin_unlock_irqrestore(&priv->stats_lock, flags);
+
+	/* 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;
+	}
+
+	memset(&iwstats.discard, 0, sizeof(iwstats.discard));
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_GEN_XMIT_ERROR, &tmp, &len);
+	if (ret == 0)
+		iwstats.discard.misc += le32_to_cpu(tmp);
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_GEN_RCV_ERROR, &tmp, &len);
+	if (ret == 0)
+		iwstats.discard.misc += le32_to_cpu(tmp);
+
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_GEN_RCV_NO_BUFFER, &tmp, &len);
+	if (ret == 0)
+		iwstats.discard.misc += le32_to_cpu(tmp);
+
+	/* Workaround transfer stalls on poor quality links. */
+	len = sizeof(tmp);
+	ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &tmp, &len);
+	if (ret == 0) {
+		bitrate = le32_to_cpu(tmp) * 100;
+		if (bitrate > 11000000)
+			goto end;
+
+		/* Decrease stats worker interval to catch stalls.
+		 * faster. Faster than 400-500ms causes packet loss,
+		 * Slower doesn't catch stalls fast enough.
+		 */
+		j = msecs_to_jiffies(priv->param_workaround_interval);
+		if (j > STATS_UPDATE_JIFFIES)
+			j = STATS_UPDATE_JIFFIES;
+		else if (j <= 0)
+			j = 1;
+		update_jiffies = j;
+
+		/* Send scan OID. Use of both OIDs is required to get device
+		 * working.
+		 */
+		tmp = ccpu2(1);
+		rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
+								sizeof(tmp));
+
+		len = CONTROL_BUFFER_SIZE;
+		buf = kmalloc(len, GFP_KERNEL);
+		if (!buf)
+			goto end;
+
+		rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
+		kfree(buf);
+	}
+end:
+	spin_lock_irqsave(&priv->stats_lock, flags);
+	memcpy(&priv->privstats, &iwstats, sizeof(iwstats));
+	spin_unlock_irqrestore(&priv->stats_lock, flags);
+
+	if (update_jiffies >= HZ)
+		update_jiffies = round_jiffies_relative(update_jiffies);
+	else {
+		j = round_jiffies_relative(update_jiffies);
+		if (abs(j - update_jiffies) <= 10)
+			update_jiffies = j;
+	}
+
+	queue_delayed_work(priv->workqueue, &priv->stats_work, update_jiffies);
+}
+
+
+static int bcm4320_early_init(struct usbnet *dev)
+{
+	struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+	char buf[8];
+
+	/* Early initialization settings, setting these won't have effect
+	 * if called after generic_rndis_bind().
+	 */
+
+	priv->param_country[0] = modparam_country[0];
+	priv->param_country[1] = modparam_country[1];
+	priv->param_country[2] = 0;
+	priv->param_frameburst   = modparam_frameburst;
+	priv->param_afterburner  = modparam_afterburner;
+	priv->param_power_save   = modparam_power_save;
+	priv->param_power_output = modparam_power_output;
+	priv->param_roamtrigger  = modparam_roamtrigger;
+	priv->param_roamdelta    = modparam_roamdelta;
+	priv->param_workaround_interval = modparam_workaround_interval;
+
+	priv->param_country[0] = toupper(priv->param_country[0]);
+	priv->param_country[1] = toupper(priv->param_country[1]);
+	/* doesn't support EU as country code, use FI instead */
+	if (!strcmp(priv->param_country, "EU"))
+		strcpy(priv->param_country, "FI");
+
+	if (priv->param_power_save < 0)
+		priv->param_power_save = 0;
+	else if (priv->param_power_save > 2)
+		priv->param_power_save = 2;
+
+	if (priv->param_roamtrigger < -80)
+		priv->param_roamtrigger = -80;
+	else if (priv->param_roamtrigger > -60)
+		priv->param_roamtrigger = -60;
+
+	if (priv->param_roamdelta < 0)
+		priv->param_roamdelta = 0;
+	else if (priv->param_roamdelta > 2)
+		priv->param_roamdelta = 2;
+
+	if (priv->param_workaround_interval < 0)
+		priv->param_workaround_interval = 500;
+
+	rndis_set_config_parameter_str(dev, "Country", priv->param_country);
+	rndis_set_config_parameter_str(dev, "FrameBursting",
+					priv->param_frameburst ? "1" : "0");
+	rndis_set_config_parameter_str(dev, "Afterburner",
+					priv->param_afterburner ? "1" : "0");
+	sprintf(buf, "%d", priv->param_power_save);
+	rndis_set_config_parameter_str(dev, "PowerSaveMode", buf);
+	sprintf(buf, "%d", priv->param_power_output);
+	rndis_set_config_parameter_str(dev, "PwrOut", buf);
+	sprintf(buf, "%d", priv->param_roamtrigger);
+	rndis_set_config_parameter_str(dev, "RoamTrigger", buf);
+	sprintf(buf, "%d", priv->param_roamdelta);
+	rndis_set_config_parameter_str(dev, "RoamDelta", buf);
+
+	return 0;
+}
+
+
+static int rndis_wext_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct net_device *net = dev->net;
+	struct rndis_wext_private *priv;
+	int retval, len;
+	__le32 tmp;
+
+	/* allocate rndis private data */
+	priv = kmalloc(sizeof(struct rndis_wext_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* These have to be initialized before calling generic_rndis_bind().
+	 * Otherwise we'll be in big trouble in rndis_wext_early_init().
+	 */
+	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);
+	spin_lock_init(&priv->stats_lock);
+
+	/* try bind rndis_host */
+	retval = generic_rndis_bind(dev, intf);
+	if (retval < 0)
+		goto fail;
+
+	/* generic_rndis_bind set packet filter to multicast_all+
+	 * promisc mode which doesn't work well for our devices (device
+	 * picks up rssi to closest station instead of to access point).
+	 *
+	 * rndis_host wants to avoid all OID as much as possible
+	 * so do promisc/multicast handling in rndis_wext.
+	 */
+	dev->net->set_multicast_list = rndis_wext_set_multicast_list;
+	tmp = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST;
+	retval = rndis_set_oid(dev, OID_GEN_CURRENT_PACKET_FILTER, &tmp,
+								sizeof(tmp));
+
+	len = sizeof(tmp);
+	retval = rndis_query_oid(dev, OID_802_3_MAXIMUM_LIST_SIZE, &tmp, &len);
+	priv->multicast_size = le32_to_cpu(tmp);
+	if (retval < 0 || priv->multicast_size < 0)
+		priv->multicast_size = 0;
+	if (priv->multicast_size > 0)
+		dev->net->flags |= IFF_MULTICAST;
+	else
+		dev->net->flags &= ~IFF_MULTICAST;
+
+	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_worker);
+
+	return 0;
+
+fail:
+	kfree(priv);
+	return retval;
+}
+
+
+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);
+}
+
+
+static const struct driver_info	bcm4320_info = {
+	.description =	"Wireless RNDIS device, BCM4320 based",
+	.flags =	FLAG_WLAN | 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,
+	.rndis_early_init =	bcm4320_early_init,
+	.rndis_link_change =	rndis_wext_link_change,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static const struct usb_device_id products [] = {
+#define	RNDIS_MASTER_INTERFACE \
+	.bInterfaceClass	= USB_CLASS_COMM, \
+	.bInterfaceSubClass	= 2 /* ACM */, \
+	.bInterfaceProtocol	= 0x0ff
+
+/* When adding new entries, remember to add blacklist entry to rndis_host. */
+
+/* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom
+ * parameters available. Chipset marked as 'BCM4320SKFBG' in NDISwrapper-wiki.
+ */
+{
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0411,
+	.idProduct		= 0x00bc,	/* Buffalo WLI-U2-KG125S */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0baf,
+	.idProduct		= 0x011b,	/* U.S. Robotics USR5421 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x050d,
+	.idProduct		= 0x011b,	/* Belkin F5D7051 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x1799,	/* Belkin has two vendor ids */
+	.idProduct		= 0x011b,	/* Belkin F5D7051 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x13b1,
+	.idProduct		= 0x0014,	/* Linksys WUSB54GSv2 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x13b1,
+	.idProduct		= 0x0026,	/* Linksys WUSB54GSC */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0b05,
+	.idProduct		= 0x1717,	/* Asus WL169gE */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0a5c,
+	.idProduct		= 0xd11b,	/* Eminent EM4045 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x1690,
+	.idProduct		= 0x0715,	/* BT Voyager 1055 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+},
+/* These devices have DriverVer < 4.xx.xx.xx and do not have any custom
+ * parameters available, hardware probably contain older firmware version with
+ * no way of updating. Chipset marked as 'BCM4320????' in NDISwrapper-wiki.
+ */
+{
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x13b1,
+	.idProduct		= 0x000e,	/* Linksys WUSB54GSv1 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0baf,
+	.idProduct		= 0x0111,	/* U.S. Robotics USR5420 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0411,
+	.idProduct		= 0x004b,	/* BUFFALO WLI-USB-G54 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= (unsigned long) &bcm4320_info,
+},
+	{ },		// END
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver rndis_wext_driver = {
+	.name =		"rndis_wext",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.disconnect =	usbnet_disconnect,
+	.suspend =	usbnet_suspend,
+	.resume =	usbnet_resume,
+};
+
+static int __init rndis_wext_init(void)
+{
+	return usb_register(&rndis_wext_driver);
+}
+module_init(rndis_wext_init);
+
+static void __exit rndis_wext_exit(void)
+{
+	usb_deregister(&rndis_wext_driver);
+}
+module_exit(rndis_wext_exit);
+
+MODULE_AUTHOR("Bjorge Dijkstra");
+MODULE_AUTHOR("Jussi Kivilinna");
+MODULE_DESCRIPTION("Driver for RNDIS based USB Wireless adapters");
+MODULE_LICENSE("GPL");
+

^ permalink raw reply related

* [PATCH 13/14] [rndis_host] blacklist known wireless RNDIS devices
From: Jussi Kivilinna @ 2008-01-20  0:14 UTC (permalink / raw)
  To: linux-wireless-u79uwXL29TY76Z2rM5mHXA
  Cc: bjd-a1rhEgazXTw, dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f,
	netdev-u79uwXL29TY76Z2rM5mHXA

Blacklist known wireless RNDIS devices that will be handled by
rndis_wext module.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna-E01nCVcF24I@public.gmane.org>
---

 drivers/net/usb/rndis_host.c |  104 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 104 insertions(+), 0 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 1ffbbb3..cd1f953 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -530,6 +530,110 @@ static const struct driver_info	rndis_info = {
 /*-------------------------------------------------------------------------*/
 
 static const struct usb_device_id	products [] = {
+/*
+ * BLACKLIST !!
+ *
+ * Blacklist RNDIS devices that are handled in separate RNDIS modules.
+ */
+
+#define	RNDIS_MASTER_INTERFACE \
+	.bInterfaceClass	= USB_CLASS_COMM, \
+	.bInterfaceSubClass	= 2 /* ACM */, \
+	.bInterfaceProtocol	= 0x0ff
+
+/* Wireless RNDIS devices, rndis_wext module */
+{
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0411,
+	.idProduct		= 0x00bc,	/* Buffalo WLI-U2-KG125S */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0baf,
+	.idProduct		= 0x011b,	/* U.S. Robotics USR5421 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x050d,
+	.idProduct		= 0x011b,	/* Belkin F5D7051 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x1799,	/* Belkin has two vendor ids */
+	.idProduct		= 0x011b,	/* Belkin F5D7051 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x13b1,
+	.idProduct		= 0x0014,	/* Linksys WUSB54GSv2 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x13b1,
+	.idProduct		= 0x0026,	/* Linksys WUSB54GSC */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0b05,
+	.idProduct		= 0x1717,	/* Asus WL169gE */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0a5c,
+	.idProduct		= 0xd11b,	/* Eminent EM4045 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x1690,
+	.idProduct		= 0x0715,	/* BT Voyager 1055 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x13b1,
+	.idProduct		= 0x000e,	/* Linksys WUSB54GSv1 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0baf,
+	.idProduct		= 0x0111,	/* U.S. Robotics USR5420 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x0411,
+	.idProduct		= 0x004b,	/* BUFFALO WLI-USB-G54 */
+	RNDIS_MASTER_INTERFACE,
+	.driver_info		= 0,
+},
+
+/*
+ * WHITELIST!!!
+ *
+ * All other RNDIS devices that do not need special OID handling in order
+ * to work.
+ */
 {
 	/* RNDIS is MSFT's un-official variant of CDC ACM */
 	USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox