linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop
@ 2009-07-30 16:41 Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 02/10] rndis_wlan: stop workers on rndis_wlan_stop() and restore on rndis_wlan_reset() Jussi Kivilinna
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna, David Brownell

rndis_wlan devices freeze after running usbnet_stop several times. It appears
that firmware freezes in state where it does not respond to any RNDIS commands
and device have to be physically unplugged/replugged. This patch lets
minidrivers to disable unlink_urbs on usbnet_stop through new info flag.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Cc: David Brownell <dbrownell@users.sourceforge.net>
---

 drivers/net/usb/usbnet.c          |   32 ++++++++++++++++++--------------
 drivers/net/wireless/rndis_wlan.c |    9 ++++++---
 include/linux/usb/usbnet.h        |    1 +
 3 files changed, 25 insertions(+), 17 deletions(-)

diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 25e435c..af1fe46 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -601,21 +601,25 @@ int usbnet_stop (struct net_device *net)
 				info->description);
 	}
 
-	// ensure there are no more active urbs
-	add_wait_queue (&unlink_wakeup, &wait);
-	dev->wait = &unlink_wakeup;
-	temp = unlink_urbs (dev, &dev->txq) + unlink_urbs (dev, &dev->rxq);
-
-	// maybe wait for deletions to finish.
-	while (!skb_queue_empty(&dev->rxq)
-			&& !skb_queue_empty(&dev->txq)
-			&& !skb_queue_empty(&dev->done)) {
-		msleep(UNLINK_TIMEOUT_MS);
-		if (netif_msg_ifdown (dev))
-			devdbg (dev, "waited for %d urb completions", temp);
+	if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
+		/* ensure there are no more active urbs */
+		add_wait_queue(&unlink_wakeup, &wait);
+		dev->wait = &unlink_wakeup;
+		temp = unlink_urbs(dev, &dev->txq) +
+			unlink_urbs(dev, &dev->rxq);
+
+		/* maybe wait for deletions to finish. */
+		while (!skb_queue_empty(&dev->rxq)
+				&& !skb_queue_empty(&dev->txq)
+				&& !skb_queue_empty(&dev->done)) {
+			msleep(UNLINK_TIMEOUT_MS);
+			if (netif_msg_ifdown(dev))
+				devdbg(dev, "waited for %d urb completions",
+					temp);
+		}
+		dev->wait = NULL;
+		remove_wait_queue(&unlink_wakeup, &wait);
 	}
-	dev->wait = NULL;
-	remove_wait_queue (&unlink_wakeup, &wait);
 
 	usb_kill_urb(dev->interrupt);
 
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 09c0702..76c5ec6 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2513,7 +2513,8 @@ static int rndis_wlan_stop(struct usbnet *usbdev)
 
 static const struct driver_info	bcm4320b_info = {
 	.description =	"Wireless RNDIS device, BCM4320b based",
-	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
+				FLAG_AVOID_UNLINK_URBS,
 	.bind =		rndis_wlan_bind,
 	.unbind =	rndis_wlan_unbind,
 	.status =	rndis_status,
@@ -2527,7 +2528,8 @@ static const struct driver_info	bcm4320b_info = {
 
 static const struct driver_info	bcm4320a_info = {
 	.description =	"Wireless RNDIS device, BCM4320a based",
-	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
+				FLAG_AVOID_UNLINK_URBS,
 	.bind =		rndis_wlan_bind,
 	.unbind =	rndis_wlan_unbind,
 	.status =	rndis_status,
@@ -2541,7 +2543,8 @@ static const struct driver_info	bcm4320a_info = {
 
 static const struct driver_info rndis_wlan_info = {
 	.description =	"Wireless RNDIS device",
-	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
+				FLAG_AVOID_UNLINK_URBS,
 	.bind =		rndis_wlan_bind,
 	.unbind =	rndis_wlan_unbind,
 	.status =	rndis_status,
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 7c17b2e..c642f78 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -86,6 +86,7 @@ struct driver_info {
 
 #define FLAG_FRAMING_AX 0x0040		/* AX88772/178 packets */
 #define FLAG_WLAN	0x0080		/* use "wlan%d" names */
+#define FLAG_AVOID_UNLINK_URBS 0x0100	/* don't unlink urbs at usbnet_stop() */
 
 
 	/* init device ... can sleep, or cause probe() failure */


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

* [PATCH 02/10] rndis_wlan: stop workers on rndis_wlan_stop() and restore on rndis_wlan_reset()
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
@ 2009-07-30 16:41 ` Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 03/10] rndis_wlan: clear cfg80211 scan on rndis_wlan_stop() Jussi Kivilinna
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna

Driver doesn't need to poll statistics/link status when stopped.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---

 drivers/net/wireless/rndis_wlan.c |   23 +++++++++++++++++++----
 1 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 76c5ec6..3c7c620 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2457,9 +2457,6 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf)
 	disassociate(usbdev, 1);
 	netif_carrier_off(usbdev->net);
 
-	queue_delayed_work(priv->workqueue, &priv->stats_work,
-		round_jiffies_relative(STATS_UPDATE_JIFFIES));
-
 	return 0;
 
 fail:
@@ -2499,15 +2496,33 @@ static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf)
 
 static int rndis_wlan_reset(struct usbnet *usbdev)
 {
+	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+
 	devdbg(usbdev, "rndis_wlan_reset");
+
+	queue_delayed_work(priv->workqueue, &priv->stats_work,
+		round_jiffies_relative(STATS_UPDATE_JIFFIES));
+
 	return deauthenticate(usbdev);
 }
 
 
 static int rndis_wlan_stop(struct usbnet *usbdev)
 {
+	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+	int retval;
+
 	devdbg(usbdev, "rndis_wlan_stop");
-	return disassociate(usbdev, 0);
+
+	retval = disassociate(usbdev, 0);
+
+	priv->work_pending = 0;
+	cancel_delayed_work_sync(&priv->stats_work);
+	cancel_delayed_work_sync(&priv->scan_work);
+	cancel_work_sync(&priv->work);
+	flush_workqueue(priv->workqueue);
+
+	return retval;
 }
 
 


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

* [PATCH 03/10] rndis_wlan: clear cfg80211 scan on rndis_wlan_stop()
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 02/10] rndis_wlan: stop workers on rndis_wlan_stop() and restore on rndis_wlan_reset() Jussi Kivilinna
@ 2009-07-30 16:41 ` Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 04/10] rndis_wlan: reset device and restore multicast list on rndis_wlan_reset() Jussi Kivilinna
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna

Scanning gets stuck if device is stopped when scan is active. Fix by
clearing/aborting cfg80211 scan on rndis_wlan_stop().

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---

 drivers/net/wireless/rndis_wlan.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 3c7c620..dee0551 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -1484,6 +1484,9 @@ static void rndis_get_scan_results(struct work_struct *work)
 
 	devdbg(usbdev, "get_scan_results");
 
+	if (!priv->scan_request)
+		return;
+
 	ret = rndis_check_bssid_list(usbdev);
 
 	cfg80211_scan_done(priv->scan_request, ret < 0);
@@ -2522,6 +2525,11 @@ static int rndis_wlan_stop(struct usbnet *usbdev)
 	cancel_work_sync(&priv->work);
 	flush_workqueue(priv->workqueue);
 
+	if (priv->scan_request) {
+		cfg80211_scan_done(priv->scan_request, true);
+		priv->scan_request = NULL;
+	}
+
 	return retval;
 }
 


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

* [PATCH 04/10] rndis_wlan: reset device and restore multicast list on rndis_wlan_reset()
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 02/10] rndis_wlan: stop workers on rndis_wlan_stop() and restore on rndis_wlan_reset() Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 03/10] rndis_wlan: clear cfg80211 scan on rndis_wlan_stop() Jussi Kivilinna
@ 2009-07-30 16:41 ` Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 05/10] rndis_wlan: set current packet filter to zero on stop Jussi Kivilinna
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna

Reset device properly with RNDIS_MSG_RESET in rndis_wlan_reset() and restore
multicast list afterwards.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---

 drivers/net/wireless/rndis_wlan.c |   30 ++++++++++++++++++++++++++++++
 1 files changed, 30 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index dee0551..bfb9861 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -594,6 +594,28 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
 }
 
 
+static int rndis_reset(struct usbnet *usbdev)
+{
+	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+	struct rndis_reset *reset;
+	int ret;
+
+	mutex_lock(&priv->command_lock);
+
+	reset = (void *)priv->command_buffer;
+	memset(reset, 0, sizeof(*reset));
+	reset->msg_type = RNDIS_MSG_RESET;
+	reset->msg_len = cpu_to_le32(sizeof(*reset));
+	ret = rndis_command(usbdev, (void *)reset, CONTROL_BUFFER_SIZE);
+
+	mutex_unlock(&priv->command_lock);
+
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+
 /*
  * Specs say that we can only set config parameters only soon after device
  * initialization.
@@ -2500,9 +2522,17 @@ static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf)
 static int rndis_wlan_reset(struct usbnet *usbdev)
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+	int retval;
 
 	devdbg(usbdev, "rndis_wlan_reset");
 
+	retval = rndis_reset(usbdev);
+	if (retval)
+		devwarn(usbdev, "rndis_reset() failed: %d", retval);
+
+	/* rndis_reset cleared multicast list, so restore here. */
+	set_multicast_list(usbdev);
+
 	queue_delayed_work(priv->workqueue, &priv->stats_work,
 		round_jiffies_relative(STATS_UPDATE_JIFFIES));
 


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

* [PATCH 05/10] rndis_wlan: set current packet filter to zero on stop
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
                   ` (2 preceding siblings ...)
  2009-07-30 16:41 ` [PATCH 04/10] rndis_wlan: reset device and restore multicast list on rndis_wlan_reset() Jussi Kivilinna
@ 2009-07-30 16:41 ` Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 06/10] rndis_wlan: add rndis_set/query_oid debugging Jussi Kivilinna
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna

Set current packet filter to zero to block receiving data packets from
device.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---

 drivers/net/wireless/rndis_wlan.c |   10 +++++++++-
 1 files changed, 9 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index bfb9861..974f724 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2530,7 +2530,8 @@ static int rndis_wlan_reset(struct usbnet *usbdev)
 	if (retval)
 		devwarn(usbdev, "rndis_reset() failed: %d", retval);
 
-	/* rndis_reset cleared multicast list, so restore here. */
+	/* rndis_reset cleared multicast list, so restore here.
+	   (set_multicast_list() also turns on current packet filter) */
 	set_multicast_list(usbdev);
 
 	queue_delayed_work(priv->workqueue, &priv->stats_work,
@@ -2544,6 +2545,7 @@ static int rndis_wlan_stop(struct usbnet *usbdev)
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
 	int retval;
+	__le32 filter;
 
 	devdbg(usbdev, "rndis_wlan_stop");
 
@@ -2560,6 +2562,12 @@ static int rndis_wlan_stop(struct usbnet *usbdev)
 		priv->scan_request = NULL;
 	}
 
+	/* Set current packet filter zero to block receiving data packets from
+	   device. */
+	filter = 0;
+	rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter,
+								sizeof(filter));
+
 	return retval;
 }
 


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

* [PATCH 06/10] rndis_wlan: add rndis_set/query_oid debugging
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
                   ` (3 preceding siblings ...)
  2009-07-30 16:41 ` [PATCH 05/10] rndis_wlan: set current packet filter to zero on stop Jussi Kivilinna
@ 2009-07-30 16:41 ` Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 07/10] rndis_host: allow rndis_wlan to see all indications Jussi Kivilinna
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna

Add better debugging for failed OID queries.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---

 drivers/net/wireless/rndis_wlan.c |   86 ++++++++++++++++++++++++++++++++++++-
 1 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 974f724..f6dcbb1 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -100,7 +100,6 @@ MODULE_PARM_DESC(workaround_interval,
 #define OID_GEN_RCV_ERROR			cpu_to_le32(0x00020104)
 #define OID_GEN_RCV_NO_BUFFER			cpu_to_le32(0x00020105)
 
-#define OID_802_3_PERMANENT_ADDRESS		cpu_to_le32(0x01010101)
 #define OID_802_3_CURRENT_ADDRESS		cpu_to_le32(0x01010102)
 #define OID_802_3_MULTICAST_LIST		cpu_to_le32(0x01010103)
 #define OID_802_3_MAXIMUM_LIST_SIZE		cpu_to_le32(0x01010104)
@@ -478,6 +477,68 @@ static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv)
 }
 
 
+#ifdef DEBUG
+static const char *oid_to_string(__le32 oid)
+{
+	switch (oid) {
+#define OID_STR(oid) case oid: return(#oid)
+		/* from rndis_host.h */
+		OID_STR(OID_802_3_PERMANENT_ADDRESS);
+		OID_STR(OID_GEN_MAXIMUM_FRAME_SIZE);
+		OID_STR(OID_GEN_CURRENT_PACKET_FILTER);
+		OID_STR(OID_GEN_PHYSICAL_MEDIUM);
+
+		/* from rndis_wlan.c */
+		OID_STR(OID_GEN_LINK_SPEED);
+		OID_STR(OID_GEN_RNDIS_CONFIG_PARAMETER);
+
+		OID_STR(OID_GEN_XMIT_OK);
+		OID_STR(OID_GEN_RCV_OK);
+		OID_STR(OID_GEN_XMIT_ERROR);
+		OID_STR(OID_GEN_RCV_ERROR);
+		OID_STR(OID_GEN_RCV_NO_BUFFER);
+
+		OID_STR(OID_802_3_CURRENT_ADDRESS);
+		OID_STR(OID_802_3_MULTICAST_LIST);
+		OID_STR(OID_802_3_MAXIMUM_LIST_SIZE);
+
+		OID_STR(OID_802_11_BSSID);
+		OID_STR(OID_802_11_SSID);
+		OID_STR(OID_802_11_INFRASTRUCTURE_MODE);
+		OID_STR(OID_802_11_ADD_WEP);
+		OID_STR(OID_802_11_REMOVE_WEP);
+		OID_STR(OID_802_11_DISASSOCIATE);
+		OID_STR(OID_802_11_AUTHENTICATION_MODE);
+		OID_STR(OID_802_11_PRIVACY_FILTER);
+		OID_STR(OID_802_11_BSSID_LIST_SCAN);
+		OID_STR(OID_802_11_ENCRYPTION_STATUS);
+		OID_STR(OID_802_11_ADD_KEY);
+		OID_STR(OID_802_11_REMOVE_KEY);
+		OID_STR(OID_802_11_ASSOCIATION_INFORMATION);
+		OID_STR(OID_802_11_PMKID);
+		OID_STR(OID_802_11_NETWORK_TYPES_SUPPORTED);
+		OID_STR(OID_802_11_NETWORK_TYPE_IN_USE);
+		OID_STR(OID_802_11_TX_POWER_LEVEL);
+		OID_STR(OID_802_11_RSSI);
+		OID_STR(OID_802_11_RSSI_TRIGGER);
+		OID_STR(OID_802_11_FRAGMENTATION_THRESHOLD);
+		OID_STR(OID_802_11_RTS_THRESHOLD);
+		OID_STR(OID_802_11_SUPPORTED_RATES);
+		OID_STR(OID_802_11_CONFIGURATION);
+		OID_STR(OID_802_11_BSSID_LIST);
+#undef OID_STR
+	}
+
+	return "?";
+}
+#else
+static const char *oid_to_string(__le32 oid)
+{
+	return "?";
+}
+#endif
+
+
 /* translate error code */
 static int rndis_error_status(__le32 rndis_status)
 {
@@ -533,11 +594,21 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
 	u.get->oid = oid;
 
 	ret = rndis_command(dev, u.header, buflen);
+	if (ret < 0)
+		devdbg(dev, "rndis_query_oid(%s): rndis_command() failed, %d "
+			"(%08x)", oid_to_string(oid), ret,
+			le32_to_cpu(u.get_c->status));
+
 	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);
+
+		if (ret < 0)
+			devdbg(dev, "rndis_query_oid(%s): device returned "
+				"error,  0x%08x (%d)", oid_to_string(oid),
+				le32_to_cpu(u.get_c->status), ret);
 	}
 
 	mutex_unlock(&priv->command_lock);
@@ -583,9 +654,20 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
 	memcpy(u.buf + sizeof(*u.set), data, len);
 
 	ret = rndis_command(dev, u.header, buflen);
-	if (ret == 0)
+	if (ret < 0)
+		devdbg(dev, "rndis_set_oid(%s): rndis_command() failed, %d "
+			"(%08x)", oid_to_string(oid), ret,
+			le32_to_cpu(u.set_c->status));
+
+	if (ret == 0) {
 		ret = rndis_error_status(u.set_c->status);
 
+		if (ret < 0)
+			devdbg(dev, "rndis_set_oid(%s): device returned error, "
+				"0x%08x (%d)", oid_to_string(oid),
+				le32_to_cpu(u.set_c->status), ret);
+	}
+
 	mutex_unlock(&priv->command_lock);
 
 	if (u.buf != priv->command_buffer)


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

* [PATCH 07/10] rndis_host: allow rndis_wlan to see all indications
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
                   ` (4 preceding siblings ...)
  2009-07-30 16:41 ` [PATCH 06/10] rndis_wlan: add rndis_set/query_oid debugging Jussi Kivilinna
@ 2009-07-30 16:41 ` Jussi Kivilinna
  2009-07-30 16:41 ` [PATCH 08/10] rndis_wlan: handle 802.11 indications from device Jussi Kivilinna
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna, David Brownell

Allow rndis_wlan to see all indications. Currently rndis_host lets rndis_wlan to
know about link state changes only, but there is whole set of other
802.11-specific indications that rndis_wlan should handle properly. So rename
link_change() to indication() and convert rndis_wlan to use it.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Cc: David Brownell <dbrownell@users.sourceforge.net>
---

 drivers/net/usb/rndis_host.c      |   50 +++++++++++++++++++++----------------
 drivers/net/wireless/rndis_wlan.c |   31 +++++++++++++++++++----
 include/linux/usb/usbnet.h        |    5 +---
 3 files changed, 56 insertions(+), 30 deletions(-)

diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index 2232232..d032bba 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -65,6 +65,32 @@ void rndis_status(struct usbnet *dev, struct urb *urb)
 EXPORT_SYMBOL_GPL(rndis_status);
 
 /*
+ * RNDIS indicate messages.
+ */
+static void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg,
+				int buflen)
+{
+	struct cdc_state *info = (void *)&dev->data;
+	struct device *udev = &info->control->dev;
+
+	if (dev->driver_info->indication) {
+		dev->driver_info->indication(dev, msg, buflen);
+	} else {
+		switch (msg->status) {
+		case RNDIS_STATUS_MEDIA_CONNECT:
+			dev_info(udev, "rndis media connect\n");
+			break;
+		case RNDIS_STATUS_MEDIA_DISCONNECT:
+			dev_info(udev, "rndis media disconnect\n");
+			break;
+		default:
+			dev_info(udev, "rndis indication: 0x%08x\n",
+					le32_to_cpu(msg->status));
+		}
+	}
+}
+
+/*
  * RPC done RNDIS-style.  Caller guarantees:
  * - message is properly byteswapped
  * - there's no other request pending
@@ -143,27 +169,9 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)
 					request_id, xid);
 				/* then likely retry */
 			} else switch (buf->msg_type) {
-			case RNDIS_MSG_INDICATE: {	/* fault/event */
-				struct rndis_indicate *msg = (void *)buf;
-				int state = 0;
-
-				switch (msg->status) {
-				case RNDIS_STATUS_MEDIA_CONNECT:
-					state = 1;
-				case RNDIS_STATUS_MEDIA_DISCONNECT:
-					dev_info(&info->control->dev,
-						"rndis media %sconnect\n",
-						!state?"dis":"");
-					if (dev->driver_info->link_change)
-						dev->driver_info->link_change(
-							dev, state);
-					break;
-				default:
-					dev_info(&info->control->dev,
-						"rndis indication: 0x%08x\n",
-						le32_to_cpu(msg->status));
-				}
-				}
+			case RNDIS_MSG_INDICATE:	/* fault/event */
+				rndis_msg_indicate(dev, (void *)buf, buflen);
+
 				break;
 			case RNDIS_MSG_KEEPALIVE: {	/* ping */
 				struct rndis_keepalive_c *msg = (void *)buf;
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index f6dcbb1..6b6452b 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2211,13 +2211,32 @@ static void rndis_wlan_set_multicast_list(struct net_device *dev)
 	queue_work(priv->workqueue, &priv->work);
 }
 
-static void rndis_wlan_link_change(struct usbnet *usbdev, int state)
+static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen)
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+	struct rndis_indicate *msg = ind;
 
 	/* queue work to avoid recursive calls into rndis_command */
-	set_bit(state ? WORK_LINK_UP : WORK_LINK_DOWN, &priv->work_pending);
-	queue_work(priv->workqueue, &priv->work);
+	switch (msg->status) {
+	case RNDIS_STATUS_MEDIA_CONNECT:
+		devinfo(usbdev, "media connect");
+
+		set_bit(WORK_LINK_UP, &priv->work_pending);
+		queue_work(priv->workqueue, &priv->work);
+		break;
+
+	case RNDIS_STATUS_MEDIA_DISCONNECT:
+		devinfo(usbdev, "media disconnect");
+
+		set_bit(WORK_LINK_DOWN, &priv->work_pending);
+		queue_work(priv->workqueue, &priv->work);
+		break;
+
+	default:
+		devinfo(usbdev, "indication: 0x%08x",
+				le32_to_cpu(msg->status));
+		break;
+	}
 }
 
 
@@ -2666,7 +2685,7 @@ static const struct driver_info	bcm4320b_info = {
 	.reset =	rndis_wlan_reset,
 	.stop =		rndis_wlan_stop,
 	.early_init =	bcm4320b_early_init,
-	.link_change =	rndis_wlan_link_change,
+	.indication =	rndis_wlan_indication,
 };
 
 static const struct driver_info	bcm4320a_info = {
@@ -2681,7 +2700,7 @@ static const struct driver_info	bcm4320a_info = {
 	.reset =	rndis_wlan_reset,
 	.stop =		rndis_wlan_stop,
 	.early_init =	bcm4320a_early_init,
-	.link_change =	rndis_wlan_link_change,
+	.indication =	rndis_wlan_indication,
 };
 
 static const struct driver_info rndis_wlan_info = {
@@ -2696,7 +2715,7 @@ static const struct driver_info rndis_wlan_info = {
 	.reset =	rndis_wlan_reset,
 	.stop =		rndis_wlan_stop,
 	.early_init =	bcm4320a_early_init,
-	.link_change =	rndis_wlan_link_change,
+	.indication =	rndis_wlan_indication,
 };
 
 /*-------------------------------------------------------------------------*/
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index c642f78..de8b4b1 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -122,9 +122,8 @@ struct driver_info {
 	 * right after minidriver have initialized hardware. */
 	int	(*early_init)(struct usbnet *dev);
 
-	/* called by minidriver when link state changes, state: 0=disconnect,
-	 * 1=connect */
-	void	(*link_change)(struct usbnet *dev, int state);
+	/* called by minidriver when receiving indication */
+	void	(*indication)(struct usbnet *dev, void *ind, int indlen);
 
 	/* for new devices, use the descriptor-reading code instead */
 	int		in;		/* rx endpoint */


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

* [PATCH 08/10] rndis_wlan: handle 802.11 indications from device
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
                   ` (5 preceding siblings ...)
  2009-07-30 16:41 ` [PATCH 07/10] rndis_host: allow rndis_wlan to see all indications Jussi Kivilinna
@ 2009-07-30 16:41 ` Jussi Kivilinna
  2009-07-30 16:42 ` [PATCH 09/10] rndis_wlan: add missing padding to struct rndis_80211_remove_key Jussi Kivilinna
  2009-07-30 16:42 ` [PATCH 10/10] rndis_wlan: rework key handling Jussi Kivilinna
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna

Add handling for 802.11 specific rndis indications.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---

 drivers/net/wireless/rndis_wlan.c |  233 +++++++++++++++++++++++++++++++++++++
 include/linux/usb/rndis_host.h    |   13 +-
 2 files changed, 239 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 6b6452b..7a50cfa 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -201,6 +201,24 @@ enum ndis_80211_priv_filter {
 	NDIS_80211_PRIV_8021X_WEP
 };
 
+enum ndis_80211_status_type {
+	NDIS_80211_STATUSTYPE_AUTHENTICATION,
+	NDIS_80211_STATUSTYPE_MEDIASTREAMMODE,
+	NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST,
+	NDIS_80211_STATUSTYPE_RADIOSTATE,
+};
+
+enum ndis_80211_media_stream_mode {
+	NDIS_80211_MEDIA_STREAM_OFF,
+	NDIS_80211_MEDIA_STREAM_ON
+};
+
+enum ndis_80211_radio_status {
+	NDIS_80211_RADIO_STATUS_ON,
+	NDIS_80211_RADIO_STATUS_HARDWARE_OFF,
+	NDIS_80211_RADIO_STATUS_SOFTWARE_OFF,
+};
+
 enum ndis_80211_addkey_bits {
 	NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28),
 	NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29),
@@ -213,6 +231,35 @@ enum ndis_80211_addwep_bits {
 	NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31)
 };
 
+struct ndis_80211_auth_request {
+	__le32 length;
+	u8 bssid[6];
+	u8 padding[2];
+	__le32 flags;
+} __attribute__((packed));
+
+struct ndis_80211_pmkid_candidate {
+	u8 bssid[6];
+	u8 padding[2];
+	__le32 flags;
+} __attribute__((packed));
+
+struct ndis_80211_pmkid_cand_list {
+	__le32 version;
+	__le32 num_candidates;
+	struct ndis_80211_pmkid_candidate candidate_list[0];
+} __attribute__((packed));
+
+struct ndis_80211_status_indication {
+	__le32 status_type;
+	union {
+		enum ndis_80211_media_stream_mode	media_stream_mode;
+		enum ndis_80211_radio_status		radio_status;
+		struct ndis_80211_auth_request		auth_request[0];
+		struct ndis_80211_pmkid_cand_list	cand_list;
+	} u;
+} __attribute__((packed));
+
 struct ndis_80211_ssid {
 	__le32 length;
 	u8 essid[NDIS_802_11_LENGTH_SSID];
@@ -2211,16 +2258,195 @@ static void rndis_wlan_set_multicast_list(struct net_device *dev)
 	queue_work(priv->workqueue, &priv->work);
 }
 
+
+static void rndis_wlan_auth_indication(struct usbnet *usbdev,
+				struct ndis_80211_status_indication *indication,
+				int len)
+{
+	u8 *buf;
+	const char *type;
+	int flags, buflen;
+	bool pairwise_error, group_error;
+	struct ndis_80211_auth_request *auth_req;
+
+	/* must have at least one array entry */
+	if (len < offsetof(struct ndis_80211_status_indication, u) +
+				sizeof(struct ndis_80211_auth_request)) {
+		devinfo(usbdev, "authentication indication: "
+				"too short message (%i)", len);
+		return;
+	}
+
+	buf = (void *)&indication->u.auth_request[0];
+	buflen = len - offsetof(struct ndis_80211_status_indication, u);
+
+	while (buflen >= sizeof(*auth_req)) {
+		auth_req = (void *)buf;
+		type = "unknown";
+		flags = le32_to_cpu(auth_req->flags);
+		pairwise_error = false;
+		group_error = false;
+
+		if (flags & 0x1)
+			type = "reauth request";
+		if (flags & 0x2)
+			type = "key update request";
+		if (flags & 0x6) {
+			pairwise_error = true;
+			type = "pairwise_error";
+		}
+		if (flags & 0xe) {
+			group_error = true;
+			type = "group_error";
+		}
+
+		devinfo(usbdev, "authentication indication: %s (0x%08x)", type,
+				le32_to_cpu(auth_req->flags));
+
+		if (pairwise_error || group_error) {
+			union iwreq_data wrqu;
+			struct iw_michaelmicfailure micfailure;
+
+			memset(&micfailure, 0, sizeof(micfailure));
+			if (pairwise_error)
+				micfailure.flags |= IW_MICFAILURE_PAIRWISE;
+			if (group_error)
+				micfailure.flags |= IW_MICFAILURE_GROUP;
+
+			memcpy(micfailure.src_addr.sa_data, auth_req->bssid,
+				ETH_ALEN);
+
+			memset(&wrqu, 0, sizeof(wrqu));
+			wrqu.data.length = sizeof(micfailure);
+			wireless_send_event(usbdev->net, IWEVMICHAELMICFAILURE,
+						&wrqu, (u8 *)&micfailure);
+		}
+
+		buflen -= le32_to_cpu(auth_req->length);
+		buf += le32_to_cpu(auth_req->length);
+	}
+}
+
+static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev,
+				struct ndis_80211_status_indication *indication,
+				int len)
+{
+	struct ndis_80211_pmkid_cand_list *cand_list;
+	int list_len, expected_len, i;
+
+	if (len < offsetof(struct ndis_80211_status_indication, u) +
+				sizeof(struct ndis_80211_pmkid_cand_list)) {
+		devinfo(usbdev, "pmkid candidate list indication: "
+				"too short message (%i)", len);
+		return;
+	}
+
+	list_len = le32_to_cpu(indication->u.cand_list.num_candidates) *
+			sizeof(struct ndis_80211_pmkid_candidate);
+	expected_len = sizeof(struct ndis_80211_pmkid_cand_list) + list_len +
+			offsetof(struct ndis_80211_status_indication, u);
+
+	if (len < expected_len) {
+		devinfo(usbdev, "pmkid candidate list indication: "
+				"list larger than buffer (%i < %i)",
+				len, expected_len);
+		return;
+	}
+
+	cand_list = &indication->u.cand_list;
+
+	devinfo(usbdev, "pmkid candidate list indication: "
+			"version %i, candidates %i",
+			le32_to_cpu(cand_list->version),
+			le32_to_cpu(cand_list->num_candidates));
+
+	if (le32_to_cpu(cand_list->version) != 1)
+		return;
+
+	for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) {
+		struct iw_pmkid_cand pcand;
+		union iwreq_data wrqu;
+		struct ndis_80211_pmkid_candidate *cand =
+						&cand_list->candidate_list[i];
+
+		devdbg(usbdev, "cand[%i]: flags: 0x%08x, bssid: %pM",
+				i, le32_to_cpu(cand->flags), cand->bssid);
+
+		memset(&pcand, 0, sizeof(pcand));
+		if (le32_to_cpu(cand->flags) & 0x01)
+			pcand.flags |= IW_PMKID_CAND_PREAUTH;
+		pcand.index = i;
+		memcpy(pcand.bssid.sa_data, cand->bssid, ETH_ALEN);
+
+		memset(&wrqu, 0, sizeof(wrqu));
+		wrqu.data.length = sizeof(pcand);
+		wireless_send_event(usbdev->net, IWEVPMKIDCAND, &wrqu,
+								(u8 *)&pcand);
+	}
+}
+
+static void rndis_wlan_media_specific_indication(struct usbnet *usbdev,
+			struct rndis_indicate *msg, int buflen)
+{
+	struct ndis_80211_status_indication *indication;
+	int len, offset;
+
+	offset = offsetof(struct rndis_indicate, status) +
+			le32_to_cpu(msg->offset);
+	len = le32_to_cpu(msg->length);
+
+	if (len < 8) {
+		devinfo(usbdev, "media specific indication, "
+				"ignore too short message (%i < 8)", len);
+		return;
+	}
+
+	if (offset + len > buflen) {
+		devinfo(usbdev, "media specific indication, "
+				"too large to fit to buffer (%i > %i)",
+				offset + len, buflen);
+		return;
+	}
+
+	indication = (void *)((u8 *)msg + offset);
+
+	switch (le32_to_cpu(indication->status_type)) {
+	case NDIS_80211_STATUSTYPE_RADIOSTATE:
+		devinfo(usbdev, "radio state indication: %i",
+			le32_to_cpu(indication->u.radio_status));
+		return;
+
+	case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE:
+		devinfo(usbdev, "media stream mode indication: %i",
+			le32_to_cpu(indication->u.media_stream_mode));
+		return;
+
+	case NDIS_80211_STATUSTYPE_AUTHENTICATION:
+		rndis_wlan_auth_indication(usbdev, indication, len);
+		return;
+
+	case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST:
+		rndis_wlan_pmkid_cand_list_indication(usbdev, indication, len);
+		return;
+
+	default:
+		devinfo(usbdev, "media specific indication: "
+				"unknown status type 0x%08x",
+				le32_to_cpu(indication->status_type));
+	}
+}
+
+
 static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen)
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
 	struct rndis_indicate *msg = ind;
 
-	/* queue work to avoid recursive calls into rndis_command */
 	switch (msg->status) {
 	case RNDIS_STATUS_MEDIA_CONNECT:
 		devinfo(usbdev, "media connect");
 
+		/* queue work to avoid recursive calls into rndis_command */
 		set_bit(WORK_LINK_UP, &priv->work_pending);
 		queue_work(priv->workqueue, &priv->work);
 		break;
@@ -2228,10 +2454,15 @@ static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen)
 	case RNDIS_STATUS_MEDIA_DISCONNECT:
 		devinfo(usbdev, "media disconnect");
 
+		/* queue work to avoid recursive calls into rndis_command */
 		set_bit(WORK_LINK_DOWN, &priv->work_pending);
 		queue_work(priv->workqueue, &priv->work);
 		break;
 
+	case RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION:
+		rndis_wlan_media_specific_indication(usbdev, msg, buflen);
+		break;
+
 	default:
 		devinfo(usbdev, "indication: 0x%08x",
 				le32_to_cpu(msg->status));
diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h
index 37836b9..1ef1ebc 100644
--- a/include/linux/usb/rndis_host.h
+++ b/include/linux/usb/rndis_host.h
@@ -70,12 +70,13 @@ struct rndis_msg_hdr {
 #define RNDIS_MSG_KEEPALIVE_C	(RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION)
 
 /* codes for "status" field of completion messages */
-#define	RNDIS_STATUS_SUCCESS		cpu_to_le32(0x00000000)
-#define	RNDIS_STATUS_FAILURE		cpu_to_le32(0xc0000001)
-#define	RNDIS_STATUS_INVALID_DATA	cpu_to_le32(0xc0010015)
-#define	RNDIS_STATUS_NOT_SUPPORTED	cpu_to_le32(0xc00000bb)
-#define	RNDIS_STATUS_MEDIA_CONNECT	cpu_to_le32(0x4001000b)
-#define	RNDIS_STATUS_MEDIA_DISCONNECT	cpu_to_le32(0x4001000c)
+#define	RNDIS_STATUS_SUCCESS			cpu_to_le32(0x00000000)
+#define	RNDIS_STATUS_FAILURE			cpu_to_le32(0xc0000001)
+#define	RNDIS_STATUS_INVALID_DATA		cpu_to_le32(0xc0010015)
+#define	RNDIS_STATUS_NOT_SUPPORTED		cpu_to_le32(0xc00000bb)
+#define	RNDIS_STATUS_MEDIA_CONNECT		cpu_to_le32(0x4001000b)
+#define	RNDIS_STATUS_MEDIA_DISCONNECT		cpu_to_le32(0x4001000c)
+#define	RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION	cpu_to_le32(0x40010012)
 
 /* codes for OID_GEN_PHYSICAL_MEDIUM */
 #define	RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED	cpu_to_le32(0x00000000)


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

* [PATCH 09/10] rndis_wlan: add missing padding to struct rndis_80211_remove_key
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
                   ` (6 preceding siblings ...)
  2009-07-30 16:41 ` [PATCH 08/10] rndis_wlan: handle 802.11 indications from device Jussi Kivilinna
@ 2009-07-30 16:42 ` Jussi Kivilinna
  2009-07-30 16:42 ` [PATCH 10/10] rndis_wlan: rework key handling Jussi Kivilinna
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:42 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna

OID_802_11_REMOVE_KEY failed with invalid length error, add missing padding to
structure fix this.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---

 drivers/net/wireless/rndis_wlan.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 7a50cfa..3d92b77 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -327,6 +327,7 @@ struct ndis_80211_remove_key {
 	__le32 size;
 	__le32 index;
 	u8 bssid[6];
+	u8 padding[2];
 } __attribute__((packed));
 
 struct ndis_config_param {


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

* [PATCH 10/10] rndis_wlan: rework key handling
  2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
                   ` (7 preceding siblings ...)
  2009-07-30 16:42 ` [PATCH 09/10] rndis_wlan: add missing padding to struct rndis_80211_remove_key Jussi Kivilinna
@ 2009-07-30 16:42 ` Jussi Kivilinna
  8 siblings, 0 replies; 10+ messages in thread
From: Jussi Kivilinna @ 2009-07-30 16:42 UTC (permalink / raw)
  To: linux-wireless; +Cc: linville, Jussi Kivilinna

Organize key data in private structure better and store WPA keys, so
they can be restored as WEP keys.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---

 drivers/net/wireless/rndis_wlan.c |  188 ++++++++++++++++++++++++++++---------
 1 files changed, 142 insertions(+), 46 deletions(-)

diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 3d92b77..828dc18 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -413,6 +413,15 @@ static const struct ieee80211_rate rndis_rates[] = {
 	{ .bitrate = 540 }
 };
 
+struct rndis_wlan_encr_key {
+	int len;
+	int cipher;
+	u8 material[32];
+	u8 bssid[ETH_ALEN];
+	bool pairwise;
+	bool tx_key;
+};
+
 /* RNDIS device private data */
 struct rndis_wlan_private {
 	struct usbnet *usbdev;
@@ -456,9 +465,7 @@ struct rndis_wlan_private {
 
 	/* encryption stuff */
 	int  encr_tx_key_index;
-	char encr_keys[4][32];
-	int  encr_key_len[4];
-	char encr_key_wpa[4];
+	struct rndis_wlan_encr_key encr_keys[4];
 	int  wpa_version;
 	int  wpa_keymgmt;
 	int  wpa_authalg;
@@ -525,6 +532,15 @@ static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv)
 }
 
 
+static bool is_wpa_key(struct rndis_wlan_private *priv, int idx)
+{
+	int cipher = priv->encr_keys[idx].cipher;
+
+	return (cipher == WLAN_CIPHER_SUITE_CCMP ||
+		cipher == WLAN_CIPHER_SUITE_TKIP);
+}
+
+
 #ifdef DEBUG
 static const char *oid_to_string(__le32 oid)
 {
@@ -895,8 +911,7 @@ static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)
 /*
  * common functions
  */
-static int
-add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index);
+static void restore_keys(struct usbnet *usbdev);
 
 static int get_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid)
 {
@@ -1115,7 +1130,7 @@ static int set_infra_mode(struct usbnet *usbdev, int mode)
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
 	__le32 tmp;
-	int ret, i;
+	int ret;
 
 	devdbg(usbdev, "set_infra_mode: infra_mode=0x%x", priv->infra_mode);
 
@@ -1130,14 +1145,7 @@ static int set_infra_mode(struct usbnet *usbdev, int mode)
 	/* 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->encr_key_len[i] > 0 && !priv->encr_key_wpa[i])
-				add_wep_key(usbdev, priv->encr_keys[i],
-						priv->encr_key_len[i], i);
-		}
-	}
+	restore_keys(usbdev);
 
 	priv->infra_mode = mode;
 	return 0;
@@ -1204,11 +1212,16 @@ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index)
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
 	struct ndis_80211_wep_key ndis_key;
-	int ret;
+	int cipher, ret;
 
-	if (key_len <= 0 || key_len > 32 || index < 0 || index >= 4)
+	if ((key_len != 5 || key_len != 13) || index < 0 || index > 3)
 		return -EINVAL;
 
+	if (key_len == 5)
+		cipher = WLAN_CIPHER_SUITE_WEP40;
+	else
+		cipher = WLAN_CIPHER_SUITE_WEP104;
+
 	memset(&ndis_key, 0, sizeof(ndis_key));
 
 	ndis_key.size = cpu_to_le32(sizeof(ndis_key));
@@ -1233,30 +1246,44 @@ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index)
 		return ret;
 	}
 
-	priv->encr_key_len[index] = key_len;
-	priv->encr_key_wpa[index] = 0;
-	memcpy(&priv->encr_keys[index], key, key_len);
+	priv->encr_keys[index].len = key_len;
+	priv->encr_keys[index].cipher = cipher;
+	memcpy(&priv->encr_keys[index].material, key, key_len);
+	memset(&priv->encr_keys[index].bssid, 0xff, ETH_ALEN);
 
 	return 0;
 }
 
 
 static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
-			int index, const struct sockaddr *addr,
-			const u8 *rx_seq, int alg, int flags)
+			int index, const u8 *addr, const u8 *rx_seq, int cipher,
+			int flags)
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
 	struct ndis_80211_key ndis_key;
+	bool is_addr_ok;
 	int ret;
 
-	if (index < 0 || index >= 4)
+	if (index < 0 || index >= 4) {
+		devdbg(usbdev, "add_wpa_key: index out of range (%i)", index);
 		return -EINVAL;
-	if (key_len > sizeof(ndis_key.material) || key_len < 0)
+	}
+	if (key_len > sizeof(ndis_key.material) || key_len < 0) {
+		devdbg(usbdev, "add_wpa_key: key length out of range (%i)",
+			key_len);
 		return -EINVAL;
-	if ((flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) && !rx_seq)
+	}
+	if ((flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) && !rx_seq) {
+		devdbg(usbdev, "add_wpa_key: recv seq flag without buffer");
 		return -EINVAL;
-	if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !addr)
+	}
+	is_addr_ok = addr && memcmp(addr, zero_bssid, ETH_ALEN) != 0 &&
+			memcmp(addr, ffff_bssid, ETH_ALEN) != 0;
+	if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !is_addr_ok) {
+		devdbg(usbdev, "add_wpa_key: pairwise but bssid invalid (%pM)",
+			addr);
 		return -EINVAL;
+	}
 
 	devdbg(usbdev, "add_wpa_key(%i): flags:%i%i%i", index,
 			!!(flags & NDIS_80211_ADDKEY_TRANSMIT_KEY),
@@ -1270,7 +1297,7 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
 	ndis_key.length = cpu_to_le32(key_len);
 	ndis_key.index = cpu_to_le32(index) | flags;
 
-	if (alg == IW_ENCODE_ALG_TKIP && key_len == 32) {
+	if (cipher == WLAN_CIPHER_SUITE_TKIP && key_len == 32) {
 		/* wpa_supplicant gives us the Michael MIC RX/TX keys in
 		 * different order than NDIS spec, so swap the order here. */
 		memcpy(ndis_key.material, key, 16);
@@ -1284,7 +1311,7 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
 
 	if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) {
 		/* pairwise key */
-		memcpy(ndis_key.bssid, addr->sa_data, ETH_ALEN);
+		memcpy(ndis_key.bssid, addr, ETH_ALEN);
 	} else {
 		/* group key */
 		if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
@@ -1299,8 +1326,14 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
 	if (ret != 0)
 		return ret;
 
-	priv->encr_key_len[index] = key_len;
-	priv->encr_key_wpa[index] = 1;
+	memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index]));
+	priv->encr_keys[index].len = key_len;
+	priv->encr_keys[index].cipher = cipher;
+	memcpy(&priv->encr_keys[index].material, key, key_len);
+	if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY)
+		memcpy(&priv->encr_keys[index].bssid, ndis_key.bssid, ETH_ALEN);
+	else
+		memset(&priv->encr_keys[index].bssid, 0xff, ETH_ALEN);
 
 	if (flags & NDIS_80211_ADDKEY_TRANSMIT_KEY)
 		priv->encr_tx_key_index = index;
@@ -1309,25 +1342,74 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
 }
 
 
+static int restore_key(struct usbnet *usbdev, int key_idx)
+{
+	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+	struct rndis_wlan_encr_key key;
+	int flags;
+
+	key = priv->encr_keys[key_idx];
+
+	devdbg(usbdev, "restore_key: %i:%s:%i", key_idx,
+		is_wpa_key(priv, key_idx) ? "wpa" : "wep",
+		key.len);
+
+	if (key.len == 0)
+		return 0;
+
+	if (is_wpa_key(priv, key_idx)) {
+		flags = 0;
+
+		/*if (priv->encr_tx_key_index == key_idx)
+			flags |= NDIS_80211_ADDKEY_TRANSMIT_KEY;*/
+
+		if (memcmp(key.bssid, zero_bssid, ETH_ALEN) != 0 &&
+				memcmp(key.bssid, ffff_bssid, ETH_ALEN) != 0)
+			flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY;
+
+		return add_wpa_key(usbdev, key.material, key.len, key_idx,
+					key.bssid, NULL, key.cipher, flags);
+	}
+
+	return add_wep_key(usbdev, key.material, key.len, key_idx);
+}
+
+
+static void restore_keys(struct usbnet *usbdev)
+{
+	int i;
+
+	for (i = 0; i < 4; i++)
+		restore_key(usbdev, i);
+}
+
+
+static void clear_key(struct rndis_wlan_private *priv, int idx)
+{
+	memset(&priv->encr_keys[idx], 0, sizeof(priv->encr_keys[idx]));
+}
+
+
 /* remove_key is for both wep and wpa */
 static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
 	struct ndis_80211_remove_key remove_key;
 	__le32 keyindex;
+	bool is_wpa;
 	int ret;
 
-	if (priv->encr_key_len[index] == 0)
+	if (priv->encr_keys[index].len == 0)
 		return 0;
 
-	priv->encr_key_len[index] = 0;
-	priv->encr_key_wpa[index] = 0;
-	memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index]));
+	is_wpa = is_wpa_key(priv, index);
 
-	if (priv->wpa_cipher_pair == IW_AUTH_CIPHER_TKIP ||
-	    priv->wpa_cipher_pair == IW_AUTH_CIPHER_CCMP ||
-	    priv->wpa_cipher_group == IW_AUTH_CIPHER_TKIP ||
-	    priv->wpa_cipher_group == IW_AUTH_CIPHER_CCMP) {
+	devdbg(usbdev, "remove_key: %i:%s:%i", index, is_wpa ? "wpa" : "wep",
+		priv->encr_keys[index].len);
+
+	clear_key(priv, index);
+
+	if (is_wpa) {
 		remove_key.size = cpu_to_le32(sizeof(remove_key));
 		remove_key.index = cpu_to_le32(index);
 		if (bssid) {
@@ -1871,8 +1953,9 @@ static int rndis_iw_set_encode(struct net_device *dev,
 {
 	struct usbnet *usbdev = netdev_priv(dev);
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+	struct rndis_wlan_encr_key key;
 	int ret, index, key_len;
-	u8 *key;
+	u8 *keybuf;
 
 	index = (wrqu->encoding.flags & IW_ENCODE_INDEX);
 
@@ -1907,17 +1990,18 @@ static int rndis_iw_set_encode(struct net_device *dev,
 
 	if (wrqu->data.length > 0) {
 		key_len = wrqu->data.length;
-		key = extra;
+		keybuf = extra;
 	} else {
 		/* must be set as tx key */
-		if (priv->encr_key_len[index] == 0)
+		if (priv->encr_keys[index].len == 0)
 			return -EINVAL;
-		key_len = priv->encr_key_len[index];
 		key = priv->encr_keys[index];
+		key_len = key.len;
+		keybuf = key.material;
 		priv->encr_tx_key_index = index;
 	}
 
-	if (add_wep_key(usbdev, key, key_len, index) != 0)
+	if (add_wep_key(usbdev, keybuf, key_len, index) != 0)
 		return -EINVAL;
 
 	if (index == priv->encr_tx_key_index)
@@ -1934,7 +2018,7 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 	struct usbnet *usbdev = netdev_priv(dev);
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
-	int keyidx, flags;
+	int keyidx, flags, cipher;
 
 	keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX;
 
@@ -1944,8 +2028,10 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
 	else
 		keyidx = priv->encr_tx_key_index;
 
-	if (keyidx < 0 || keyidx >= 4)
+	if (keyidx < 0 || keyidx >= 4) {
+		devwarn(usbdev, "encryption index out of range (%u)", keyidx);
 		return -EINVAL;
+	}
 
 	if (ext->alg == WPA_ALG_WEP) {
 		if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
@@ -1953,10 +2039,19 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
 		return add_wep_key(usbdev, ext->key, ext->key_len, keyidx);
 	}
 
+	cipher = -1;
+	if (ext->alg == IW_ENCODE_ALG_TKIP)
+		cipher = WLAN_CIPHER_SUITE_TKIP;
+	else if (ext->alg == IW_ENCODE_ALG_CCMP)
+		cipher = WLAN_CIPHER_SUITE_CCMP;
+
 	if ((wrqu->encoding.flags & IW_ENCODE_DISABLED) ||
 	    ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0)
 		return remove_key(usbdev, keyidx, NULL);
 
+	if (cipher == -1)
+		return -EOPNOTSUPP;
+
 	flags = 0;
 	if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
 		flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ;
@@ -1965,8 +2060,9 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
 	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
 		flags |= NDIS_80211_ADDKEY_TRANSMIT_KEY;
 
-	return add_wpa_key(usbdev, ext->key, ext->key_len, keyidx, &ext->addr,
-				ext->rx_seq, ext->alg, flags);
+	return add_wpa_key(usbdev, ext->key, ext->key_len, keyidx,
+				(u8 *)&ext->addr.sa_data, ext->rx_seq, cipher,
+				flags);
 }
 
 


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

end of thread, other threads:[~2009-07-30 16:51 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-07-30 16:41 [PATCH 01/10] usbnet: allow "minidriver" to prevent urb unlinking on usbnet_stop Jussi Kivilinna
2009-07-30 16:41 ` [PATCH 02/10] rndis_wlan: stop workers on rndis_wlan_stop() and restore on rndis_wlan_reset() Jussi Kivilinna
2009-07-30 16:41 ` [PATCH 03/10] rndis_wlan: clear cfg80211 scan on rndis_wlan_stop() Jussi Kivilinna
2009-07-30 16:41 ` [PATCH 04/10] rndis_wlan: reset device and restore multicast list on rndis_wlan_reset() Jussi Kivilinna
2009-07-30 16:41 ` [PATCH 05/10] rndis_wlan: set current packet filter to zero on stop Jussi Kivilinna
2009-07-30 16:41 ` [PATCH 06/10] rndis_wlan: add rndis_set/query_oid debugging Jussi Kivilinna
2009-07-30 16:41 ` [PATCH 07/10] rndis_host: allow rndis_wlan to see all indications Jussi Kivilinna
2009-07-30 16:41 ` [PATCH 08/10] rndis_wlan: handle 802.11 indications from device Jussi Kivilinna
2009-07-30 16:42 ` [PATCH 09/10] rndis_wlan: add missing padding to struct rndis_80211_remove_key Jussi Kivilinna
2009-07-30 16:42 ` [PATCH 10/10] rndis_wlan: rework key handling Jussi Kivilinna

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