Skip to contentSkip to conversation

iwmc3200wifi updates

PATCH Superseded Samuel Ortiz 2 participants 15 messages Jun 15, 2009 Networking drivers (wireless)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover iwmc3200wifi updates

Hi John,

Here go some updates for the iwmc3200wifi driver.
Some of those patches are bug fixes and we'd like them to eventually make it to 2.6.31. The rest are targeted at wireless-testing inclusion. I tried to set an explicit subject line to let you clearly understand where we'd like each of them to land, please let me know if that's not the case.

Samuel Ortiz (5):
iwmc3200wifi: fix sdio initialisation race
iwmc3200wifi: invalidate keys when changing the BSSID
iwmc3200wifi: handling wifi_if_ntfy responses
iwmc3200wifi: cfg80211 key hooks implemetation
iwmc3200wifi: cache keys when interface is down

Zhu Yi (7):
iwmc3200wifi: check for iwm_priv_init error
iwmc3200wifi: add iwm_if_add and iwm_if_remove
iwmc3200wifi: fix potential kernel oops on module removal
iwmc3200wifi: add a mutex to protect iwm_reset_worker
iwmc3200wifi: change coexist periodic calibration flag
iwmc3200wifi: remove select LIB80211 from Kconfig
iwmc3200wifi: rfkill cleanup

 drivers/net/wireless/iwmc3200wifi/Kconfig    |    1 -
 drivers/net/wireless/iwmc3200wifi/cfg80211.c |  204 +++++++++++++++++
 drivers/net/wireless/iwmc3200wifi/commands.c |  116 +++++------
 drivers/net/wireless/iwmc3200wifi/commands.h |    7 +-
 drivers/net/wireless/iwmc3200wifi/iwm.h      |   39 ++--
 drivers/net/wireless/iwmc3200wifi/main.c     |   95 ++++++---
 drivers/net/wireless/iwmc3200wifi/netdev.c   |   68 ++++--
 drivers/net/wireless/iwmc3200wifi/rx.c       |   27 ++-
 drivers/net/wireless/iwmc3200wifi/sdio.c     |   21 ++-
 drivers/net/wireless/iwmc3200wifi/umac.h     |    2 +
 drivers/net/wireless/iwmc3200wifi/wext.c     |  305 +++-----------------------
 11 files changed, 460 insertions(+), 425 deletions(-)
#1 lore raw

PATCH 1/12 iwmc3200wifi: fix sdio initialisation race

From: Samuel Ortiz <[email protected]>

When modprobing this driver, the netdev interface is ready before the SDIO function is correctly set.
This can lead to userspace calling ndo_open() before the SDIO bus is ready. We fix that by returning an error from ndo_open() when the IWM_STATUS_BUS_READY bit is not set yet.

---
 drivers/net/wireless/iwmc3200wifi/iwm.h    |   11 ++++++-----
 drivers/net/wireless/iwmc3200wifi/main.c   |    6 +++++-
 drivers/net/wireless/iwmc3200wifi/netdev.c |   10 ++++++----
 drivers/net/wireless/iwmc3200wifi/sdio.c   |    4 ++++
 4 files changed, 21 insertions(+), 10 deletions(-)
drivers/net/wireless/iwmc3200wifi/iwm.h +6 -5
index 635c16e..3cc4e91 100644
@@ -180,11 +180,12 @@ struct iwm_key {
 
 #define IWM_SCAN_ID_MAX 0xff
 
-#define IWM_STATUS_READY		0
-#define IWM_STATUS_SCANNING		1
-#define IWM_STATUS_SCAN_ABORTING	2
-#define IWM_STATUS_ASSOCIATING		3
-#define IWM_STATUS_ASSOCIATED		4
+#define IWM_STATUS_BUS_READY		0
+#define IWM_STATUS_READY		1
+#define IWM_STATUS_SCANNING		2
+#define IWM_STATUS_SCAN_ABORTING	3
+#define IWM_STATUS_ASSOCIATING		4
+#define IWM_STATUS_ASSOCIATED		5
 
 #define IWM_RADIO_RFKILL_OFF		0
 #define IWM_RADIO_RFKILL_HW		1
drivers/net/wireless/iwmc3200wifi/main.c +5 -1
index 6a2640f..09ed247 100644
@@ -227,11 +227,15 @@ int iwm_priv_init(struct iwm_priv *iwm)
 void iwm_reset(struct iwm_priv *iwm)
 {
 	struct iwm_notif *notif, *next;
+	unsigned long status = 0;
 
 	if (test_bit(IWM_STATUS_READY, &iwm->status))
 		iwm_target_reset(iwm);
 
-	iwm->status = 0;
+	if (test_bit(IWM_STATUS_BUS_READY, &iwm->status))
+		set_bit(IWM_STATUS_BUS_READY, &status);
+
+	iwm->status = status;
 	iwm->scan_id = 1;
 
 	list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
drivers/net/wireless/iwmc3200wifi/netdev.c +6 -4
index 68e2c3b..5b7aa34 100644
@@ -54,9 +54,10 @@
 static int iwm_open(struct net_device *ndev)
 {
 	struct iwm_priv *iwm = ndev_to_iwm(ndev);
-	int ret = 0;
+	int ret = -ENODEV;
 
-	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)
+	    && test_bit(IWM_STATUS_BUS_READY, &iwm->status))
 		ret = iwm_up(iwm);
 
 	return ret;
@@ -65,9 +66,10 @@ static int iwm_open(struct net_device *ndev)
 static int iwm_stop(struct net_device *ndev)
 {
 	struct iwm_priv *iwm = ndev_to_iwm(ndev);
-	int ret = 0;
+	int ret = -ENODEV;
 
-	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)
+	    && test_bit(IWM_STATUS_BUS_READY, &iwm->status))
 		ret = iwm_down(iwm);
 
 	return ret;
drivers/net/wireless/iwmc3200wifi/sdio.c +4 -0
index b54da67..97e7da3 100644
@@ -454,6 +454,9 @@ static int iwm_sdio_probe(struct sdio_func *func,
 
 	INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
 
+	/* The SDIO bus is set and ready to be enabled */
+	set_bit(IWM_STATUS_BUS_READY, &iwm->status);
+
 	dev_info(dev, "IWM SDIO probe\n");
 
 	return 0;
@@ -476,6 +479,7 @@ static void iwm_sdio_remove(struct sdio_func *func)
 	destroy_workqueue(hw->isr_wq);
 
 	sdio_set_drvdata(func, NULL);
+	clear_bit(IWM_STATUS_BUS_READY, &iwm->status);
 
 	dev_info(dev, "IWM SDIO remove\n");
 
Signature · 1 line
1.6.3.1
#2 lore raw

PATCH 2/12 iwmc3200wifi: check for iwm_priv_init error

From: Zhu Yi <[email protected]>

We need to check for iwm_priv_init() errors and do proper cleanups. Otherwise we may fail to catch the create_singlethread_workqueue() error which will cause a kernel oops when destroy_workqueue() later.

---
 drivers/net/wireless/iwmc3200wifi/iwm.h    |    1 +
 drivers/net/wireless/iwmc3200wifi/main.c   |   10 ++++++++++
 drivers/net/wireless/iwmc3200wifi/netdev.c |   19 ++++++++++++-------
 3 files changed, 23 insertions(+), 7 deletions(-)
drivers/net/wireless/iwmc3200wifi/iwm.h +1 -0
index 3cc4e91..a0e7377 100644
@@ -318,6 +318,7 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
 void iwm_if_free(struct iwm_priv *iwm);
 int iwm_mode_to_nl80211_iftype(int mode);
 int iwm_priv_init(struct iwm_priv *iwm);
+void iwm_priv_deinit(struct iwm_priv *iwm);
 void iwm_reset(struct iwm_priv *iwm);
 void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
 			      struct iwm_umac_notif_alive *alive);
drivers/net/wireless/iwmc3200wifi/main.c +10 -0
index 09ed247..cfe1890 100644
@@ -219,6 +219,16 @@ int iwm_priv_init(struct iwm_priv *iwm)
 	return 0;
 }
 
+void iwm_priv_deinit(struct iwm_priv *iwm)
+{
+	int i;
+
+	for (i = 0; i < IWM_TX_QUEUES; i++)
+		destroy_workqueue(iwm->txq[i].wq);
+
+	destroy_workqueue(iwm->rx_wq);
+}
+
 /*
  * We reset all the structures, and we reset the UMAC.
  * After calling this routine, you're expected to reload
drivers/net/wireless/iwmc3200wifi/netdev.c +12 -7
index 5b7aa34..de16b2b 100644
@@ -116,14 +116,20 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
 	iwm = wdev_to_iwm(wdev);
 	iwm->bus_ops = if_ops;
 	iwm->wdev = wdev;
-	iwm_priv_init(iwm);
+
+	ret = iwm_priv_init(iwm);
+	if (ret) {
+		dev_err(dev, "failed to init iwm_priv\n");
+		goto out_wdev;
+	}
+
 	wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode);
 
 	ndev = alloc_netdev_mq(0, "wlan%d", ether_setup,
 			       IWM_TX_QUEUES);
 	if (!ndev) {
 		dev_err(dev, "no memory for network device instance\n");
-		goto out_wdev;
+		goto out_priv;
 	}
 
 	ndev->netdev_ops = &iwm_netdev_ops;
@@ -143,6 +149,9 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
  out_ndev:
 	free_netdev(ndev);
 
+ out_priv:
+	iwm_priv_deinit(iwm);
+
  out_wdev:
 	iwm_wdev_free(iwm);
 	return ERR_PTR(ret);
@@ -150,15 +159,11 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
 
 void iwm_if_free(struct iwm_priv *iwm)
 {
-	int i;
-
 	if (!iwm_to_ndev(iwm))
 		return;
 
 	unregister_netdev(iwm_to_ndev(iwm));
 	free_netdev(iwm_to_ndev(iwm));
 	iwm_wdev_free(iwm);
-	destroy_workqueue(iwm->rx_wq);
-	for (i = 0; i < IWM_TX_QUEUES; i++)
-		destroy_workqueue(iwm->txq[i].wq);
+	iwm_priv_deinit(iwm);
 }
Signature · 1 line
1.6.3.1
#3 lore raw

PATCH 3/12 iwmc3200wifi: add iwm_if_add and iwm_if_remove

From: Zhu Yi <[email protected]>

We used to do alloc_netdev and register_netdev at the same time in iwm_if_alloc. But some bus related structures will only be initialized after iwm_priv is allocated. This caused a race condition that the netdev might be registered earlier. The patch adds iwm_if_add and iwm_if_remove so that the bus layer could register the device after all initialization is done.

---
 drivers/net/wireless/iwmc3200wifi/iwm.h    |    2 +
 drivers/net/wireless/iwmc3200wifi/netdev.c |   32 +++++++++++++++++----------
 drivers/net/wireless/iwmc3200wifi/sdio.c   |    9 +++++++
 3 files changed, 31 insertions(+), 12 deletions(-)
drivers/net/wireless/iwmc3200wifi/iwm.h +2 -0
index a0e7377..73c1011 100644
@@ -316,6 +316,8 @@ extern const struct iw_handler_def iwm_iw_handler_def;
 void *iwm_if_alloc(int sizeof_bus, struct device *dev,
 		   struct iwm_if_ops *if_ops);
 void iwm_if_free(struct iwm_priv *iwm);
+int iwm_if_add(struct iwm_priv *iwm);
+void iwm_if_remove(struct iwm_priv *iwm);
 int iwm_mode_to_nl80211_iftype(int mode);
 int iwm_priv_init(struct iwm_priv *iwm);
 void iwm_priv_deinit(struct iwm_priv *iwm);
drivers/net/wireless/iwmc3200wifi/netdev.c +20 -12
index de16b2b..d1797b9 100644
@@ -125,8 +125,7 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
 
 	wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode);
 
-	ndev = alloc_netdev_mq(0, "wlan%d", ether_setup,
-			       IWM_TX_QUEUES);
+	ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES);
 	if (!ndev) {
 		dev_err(dev, "no memory for network device instance\n");
 		goto out_priv;
@@ -136,19 +135,10 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
 	ndev->wireless_handlers = &iwm_iw_handler_def;
 	ndev->ieee80211_ptr = wdev;
 	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
-	ret = register_netdev(ndev);
-	if (ret < 0) {
-		dev_err(dev, "Failed to register netdev: %d\n", ret);
-		goto out_ndev;
-	}
-
 	wdev->netdev = ndev;
 
 	return iwm;
 
- out_ndev:
-	free_netdev(ndev);
-
  out_priv:
 	iwm_priv_deinit(iwm);
 
@@ -162,8 +152,26 @@ void iwm_if_free(struct iwm_priv *iwm)
 	if (!iwm_to_ndev(iwm))
 		return;
 
-	unregister_netdev(iwm_to_ndev(iwm));
 	free_netdev(iwm_to_ndev(iwm));
 	iwm_wdev_free(iwm);
 	iwm_priv_deinit(iwm);
 }
+
+int iwm_if_add(struct iwm_priv *iwm)
+{
+	struct net_device *ndev = iwm_to_ndev(iwm);
+	int ret;
+
+	ret = register_netdev(ndev);
+	if (ret < 0) {
+		dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+void iwm_if_remove(struct iwm_priv *iwm)
+{
+	unregister_netdev(iwm_to_ndev(iwm));
+}
drivers/net/wireless/iwmc3200wifi/sdio.c +9 -0
index 97e7da3..057a588 100644
@@ -454,6 +454,12 @@ static int iwm_sdio_probe(struct sdio_func *func,
 
 	INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
 
+	ret = iwm_if_add(iwm);
+	if (ret) {
+		dev_err(dev, "add SDIO interface failed\n");
+		goto destroy_wq;
+	}
+
 	/* The SDIO bus is set and ready to be enabled */
 	set_bit(IWM_STATUS_BUS_READY, &iwm->status);
 
@@ -461,6 +467,8 @@ static int iwm_sdio_probe(struct sdio_func *func,
 
 	return 0;
 
+ destroy_wq:
+	destroy_workqueue(hw->isr_wq);
  debugfs_exit:
 	iwm_debugfs_exit(iwm);
  if_free:
@@ -475,6 +483,7 @@ static void iwm_sdio_remove(struct sdio_func *func)
 	struct device *dev = &func->dev;
 
 	iwm_debugfs_exit(iwm);
+	iwm_if_remove(iwm);
 	iwm_if_free(iwm);
 	destroy_workqueue(hw->isr_wq);
 
Signature · 1 line
1.6.3.1
#4 lore raw

PATCH 4/12 iwmc3200wifi: fix potential kernel oops on module removal

From: Zhu Yi <[email protected]>

The iwm_if_free() is called before destroy_workqueue for isr_wq on device remove method. But if there is still some pending work in the isr_wq, the required data structures are already freed at this point. This leeds a kernel oops. The patch fixes this problem by moving iwm_if_free after destroy_workqueue.

---
 drivers/net/wireless/iwmc3200wifi/sdio.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
drivers/net/wireless/iwmc3200wifi/sdio.c +2 -2
index 057a588..462bbf6 100644
@@ -482,10 +482,10 @@ static void iwm_sdio_remove(struct sdio_func *func)
 	struct iwm_priv *iwm = hw_to_iwm(hw);
 	struct device *dev = &func->dev;
 
-	iwm_debugfs_exit(iwm);
 	iwm_if_remove(iwm);
-	iwm_if_free(iwm);
 	destroy_workqueue(hw->isr_wq);
+	iwm_debugfs_exit(iwm);
+	iwm_if_free(iwm);
 
 	sdio_set_drvdata(func, NULL);
 	clear_bit(IWM_STATUS_BUS_READY, &iwm->status);
Signature · 1 line
1.6.3.1
#5 lore raw

PATCH 5/12 iwmc3200wifi: add a mutex to protect iwm_reset_worker

From: Zhu Yi <[email protected]>

The patch adds a mutex to protect the iwm_reset_worker against netdev ndo_open and ndo_stop because all of them call iwm_up and iwm_down in the implementation. Note the latter two are already protected by rtnl. So if iwm_reset_worker is not required in the future, the mutex can also be removed.

---
 drivers/net/wireless/iwmc3200wifi/iwm.h  |    1 +
 drivers/net/wireless/iwmc3200wifi/main.c |   54 ++++++++++++++++++++++++++---
 2 files changed, 49 insertions(+), 6 deletions(-)
drivers/net/wireless/iwmc3200wifi/iwm.h +1 -0
index 73c1011..8b22df3 100644
@@ -289,6 +289,7 @@ struct iwm_priv {
 	u8 *eeprom;
 	struct timer_list watchdog;
 	struct work_struct reset_worker;
+	struct mutex mutex;
 	struct rfkill *rfkill;
 
 	char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
drivers/net/wireless/iwmc3200wifi/main.c +48 -6

113 lines not on this page · raw message

Signature · 1 line
1.6.3.1
#6 lore raw

PATCH 6/12 iwmc3200wifi: invalidate keys when changing the BSSID

From: Samuel Ortiz <[email protected]>

While associated, we have to invalidate our key cache if we clear our BSSID through siwap.

---
 drivers/net/wireless/iwmc3200wifi/wext.c |   19 ++++++++++++++++++-
 1 files changed, 18 insertions(+), 1 deletions(-)
drivers/net/wireless/iwmc3200wifi/wext.c +18 -1
index 584c94d..8891949 100644
@@ -82,6 +82,7 @@ static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
 			  struct sockaddr *ap_addr, char *extra)
 {
 	struct iwm_priv *iwm = ndev_to_iwm(dev);
+	int ret;
 
 	if (iwm->conf.mode == UMAC_MODE_IBSS)
 		return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
@@ -104,10 +105,26 @@ static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
 	}
 
 	if (iwm->umac_profile_active) {
+		int i;
+
 		if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
 			return 0;
 
-		iwm_invalidate_mlme_profile(iwm);
+		/*
+		 * If we're clearing the BSSID, and we're associated,
+		 * we have to clear the keys as they're no longer valid.
+		 */
+		if (is_zero_ether_addr(ap_addr->sa_data)) {
+			for (i = 0; i < IWM_NUM_KEYS; i++)
+				iwm->keys[i].in_use = 0;
+
+		}
+
+		ret = iwm_invalidate_mlme_profile(iwm);
+		if (ret < 0) {
+			IWM_ERR(iwm, "Couldn't invalidate profile\n");
+			return ret;
+		}
 	}
 
 	if (iwm->umac_profile->ssid.ssid_len)
Signature · 1 line
1.6.3.1
#7 lore raw

PATCH 7/12 iwmc3200wifi: handling wifi_if_ntfy responses

From: Samuel Ortiz <[email protected]>

When we're calling iwm_send_wifi_if_cmd() with the resp flag set, we're currently waiting on the mlme queue, waiting for some flags here and there to show up.
This patch adds a wifi_ntfy bitmap, and when we're sending a wifi_if command expecting an answers, we wait synchronously for it to show up, on a dedicated queue. The wifi_ntfy bit is set when we receive the corresponding answer.

---
 drivers/net/wireless/iwmc3200wifi/commands.c |   31 +++++++++++++++-----------
 drivers/net/wireless/iwmc3200wifi/iwm.h      |    3 ++
 drivers/net/wireless/iwmc3200wifi/main.c     |    1 +
 drivers/net/wireless/iwmc3200wifi/rx.c       |    9 ++++++-
 drivers/net/wireless/iwmc3200wifi/umac.h     |    2 +
 5 files changed, 31 insertions(+), 15 deletions(-)
drivers/net/wireless/iwmc3200wifi/commands.c +18 -13
index 834a7f5..337a884 100644
@@ -70,14 +70,28 @@ static int iwm_send_lmac_ptrough_cmd(struct iwm_priv *iwm,
 int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
 			 bool resp)
 {
+	struct iwm_umac_wifi_if *hdr = (struct iwm_umac_wifi_if *)payload;
 	struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
 	struct iwm_umac_cmd umac_cmd;
+	int ret;
+	u8 oid = hdr->oid;
 
 	umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER;
 	umac_cmd.resp = resp;
 
-	return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
-				     payload, payload_size);
+	ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
+				    payload, payload_size);
+
+	if (resp) {
+		ret = wait_event_interruptible_timeout(iwm->wifi_ntfy_queue,
+				   test_and_clear_bit(oid, &iwm->wifi_ntfy[0]),
+				   3 * HZ);
+
+		if (!ret)
+			ret = -EBUSY;
+	}
+
+	return ret;
 }
 
 static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] =
@@ -746,14 +760,6 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm)
 		return ret;
 	}
 
-	/* Wait for the profile to be active */
-	ret = wait_event_interruptible_timeout(iwm->mlme_queue,
-					       iwm->umac_profile_active == 1,
-					       3 * HZ);
-	if (!ret)
-		return -EBUSY;
-
-
 	for (i = 0; i < IWM_NUM_KEYS; i++)
 		if (iwm->keys[i].in_use) {
 			int default_key = 0;
@@ -778,8 +784,8 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm)
 
 int iwm_invalidate_mlme_profile(struct iwm_priv *iwm)
 {
-	int ret;
 	struct iwm_umac_invalidate_profile invalid;
+	int ret;
 
 	invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE;
 	invalid.hdr.buf_size =
@@ -793,8 +799,7 @@ int iwm_invalidate_mlme_profile(struct iwm_priv *iwm)
 		return ret;
 
 	ret = wait_event_interruptible_timeout(iwm->mlme_queue,
-				 (iwm->umac_profile_active == 0),
-					       2 * HZ);
+				(iwm->umac_profile_active == 0), 2 * HZ);
 	if (!ret)
 		return -EBUSY;
 
drivers/net/wireless/iwmc3200wifi/iwm.h +3 -0
index 8b22df3..af11ea2 100644
@@ -279,6 +279,9 @@ struct iwm_priv {
 	struct iwm_key keys[IWM_NUM_KEYS];
 	struct iwm_key *default_key;
 
+	DECLARE_BITMAP(wifi_ntfy, WIFI_IF_NTFY_MAX);
+	wait_queue_head_t wifi_ntfy_queue;
+
 	wait_queue_head_t mlme_queue;
 
 	struct iw_statistics wstats;
drivers/net/wireless/iwmc3200wifi/main.c +1 -0
index fdac13a..f8e5d1b 100644
@@ -191,6 +191,7 @@ int iwm_priv_init(struct iwm_priv *iwm)
 	INIT_LIST_HEAD(&iwm->pending_notif);
 	init_waitqueue_head(&iwm->notif_queue);
 	init_waitqueue_head(&iwm->nonwifi_queue);
+	init_waitqueue_head(&iwm->wifi_ntfy_queue);
 	init_waitqueue_head(&iwm->mlme_queue);
 	memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf));
 	spin_lock_init(&iwm->tx_credit.lock);
drivers/net/wireless/iwmc3200wifi/rx.c +7 -2
index d73cf96..49a8be7 100644
@@ -993,12 +993,17 @@ static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
 			(struct iwm_umac_wifi_if *)cmd->buf.payload;
 
 	IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: "
-		    "oid is %d\n", hdr->oid);
+		    "oid is 0x%x\n", hdr->oid);
+
+	if (hdr->oid <= WIFI_IF_NTFY_MAX) {
+		set_bit(hdr->oid, &iwm->wifi_ntfy[0]);
+		wake_up_interruptible(&iwm->wifi_ntfy_queue);
+	} else
+		return -EINVAL;
 
 	switch (hdr->oid) {
 	case UMAC_WIFI_IF_CMD_SET_PROFILE:
 		iwm->umac_profile_active = 1;
-		wake_up_interruptible(&iwm->mlme_queue);
 		break;
 	default:
 		break;
drivers/net/wireless/iwmc3200wifi/umac.h +2 -0
index 4a95cce..0af2a3c 100644
@@ -495,6 +495,8 @@ struct iwm_fw_alive_hdr {
 #define WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP		0xE8
 #define WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP	0xE9
 
+#define WIFI_IF_NTFY_MAX 0xff
+
 /* Notification structures */
 struct iwm_umac_notif_wifi_if {
 	struct iwm_umac_wifi_in_hdr hdr;
Signature · 1 line
1.6.3.1
#8 lore raw

PATCH 8/12 iwmc3200wifi: cfg80211 key hooks implemetation

From: Samuel Ortiz <[email protected]>

This patch implements the new cfg80211 privacy related hooks: add/get/set_key and the set_default_key one.
With this implementation we can now call the wext-compat *encode* routines and reduce our own wext code.

---
 drivers/net/wireless/iwmc3200wifi/cfg80211.c |  172 ++++++++++++++++
 drivers/net/wireless/iwmc3200wifi/commands.c |   72 +++-----
 drivers/net/wireless/iwmc3200wifi/commands.h |    3 +-
 drivers/net/wireless/iwmc3200wifi/iwm.h      |   14 +-
 drivers/net/wireless/iwmc3200wifi/main.c     |    4 +-
 drivers/net/wireless/iwmc3200wifi/wext.c     |  286 ++------------------------
 6 files changed, 219 insertions(+), 332 deletions(-)
drivers/net/wireless/iwmc3200wifi/cfg80211.c +172 -0
index 96f714e..ad62b20 100644
@@ -23,6 +23,7 @@
 
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/wireless.h>
 #include <linux/ieee80211.h>
 #include <net/cfg80211.h>
@@ -130,6 +131,173 @@ static struct ieee80211_supported_band iwm_band_5ghz = {
 	.n_bitrates = iwm_a_rates_size,
 };
 
+static int iwm_key_init(struct iwm_key *key, u8 key_index,
+			const u8 *mac_addr, struct key_params *params)
+{
+	key->hdr.key_idx = key_index;
+	if (!mac_addr || is_broadcast_ether_addr(mac_addr)) {
+		key->hdr.multicast = 1;
+		memset(key->hdr.mac, 0xff, ETH_ALEN);
+	} else {
+		key->hdr.multicast = 0;
+		memcpy(key->hdr.mac, mac_addr, ETH_ALEN);
+	}
+
+	if (params) {
+		if (params->key_len > WLAN_MAX_KEY_LEN ||
+		    params->seq_len > IW_ENCODE_SEQ_MAX_SIZE)
+			return -EINVAL;
+
+		key->cipher = params->cipher;
+		key->key_len = params->key_len;
+		key->seq_len = params->seq_len;
+		memcpy(key->key, params->key, key->key_len);
+		memcpy(key->seq, params->seq, key->seq_len);
+	}
+
+	return 0;
+}
+
+static int iwm_reset_profile(struct iwm_priv *iwm)
+{
+	int ret;
+
+	if (!iwm->umac_profile_active)
+		return 0;
+
+	/*
+	 * If there is a current active profile, but no
+	 * default key, it's not worth trying to associate again.
+	 */
+	if (iwm->default_key < 0)
+		return 0;
+
+	/*
+	 * Here we have an active profile, but a key setting changed.
+	 * We thus have to invalidate the current profile, and push the
+	 * new one. Keys will be pushed when association takes place.
+	 */
+	ret = iwm_invalidate_mlme_profile(iwm);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Couldn't invalidate profile\n");
+		return ret;
+	}
+
+	return iwm_send_mlme_profile(iwm);
+}
+
+static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
+				u8 key_index, const u8 *mac_addr,
+				struct key_params *params)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(ndev);
+	struct iwm_key *key = &iwm->keys[key_index];
+	int ret;
+
+	IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr);
+
+	memset(key, 0, sizeof(struct iwm_key));
+	ret = iwm_key_init(key, key_index, mac_addr, params);
+	if (ret < 0) {
+		IWM_ERR(iwm, "Invalid key_params\n");
+		return ret;
+	}
+
+	/*
+	 * The WEP keys can be set before or after setting the essid.
+	 * We need to handle both cases by simply pushing the keys after
+	 * we send the profile.
+	 * If the profile is not set yet (i.e. we're pushing keys before
+	 * the essid), we set the cipher appropriately.
+	 * If the profile is set, we havent associated yet because our
+	 * cipher was incorrectly set. So we invalidate and send the
+	 * profile again.
+	 */
+	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+	    key->cipher == WLAN_CIPHER_SUITE_WEP104) {
+		u8 *ucast_cipher = &iwm->umac_profile->sec.ucast_cipher;
+		u8 *mcast_cipher = &iwm->umac_profile->sec.mcast_cipher;
+
+		IWM_DBG_WEXT(iwm, DBG, "WEP key\n");
+
+		if (key->cipher == WLAN_CIPHER_SUITE_WEP40)
+			*ucast_cipher = *mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
+		if (key->cipher == WLAN_CIPHER_SUITE_WEP104)
+			*ucast_cipher = *mcast_cipher =
+				UMAC_CIPHER_TYPE_WEP_104;
+
+		return iwm_reset_profile(iwm);
+	}
+
+	return iwm_set_key(iwm, 0, key);
+}
+
+static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
+				u8 key_index, const u8 *mac_addr, void *cookie,
+				void (*callback)(void *cookie,
+						 struct key_params*))
+{
+	struct iwm_priv *iwm = ndev_to_iwm(ndev);
+	struct iwm_key *key = &iwm->keys[key_index];
+	struct key_params params;
+
+	IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index);
+
+	memset(&params, 0, sizeof(params));
+
+	params.cipher = key->cipher;
+	params.key_len = key->key_len;
+	params.seq_len = key->seq_len;
+	params.seq = key->seq;
+	params.key = key->key;
+
+	callback(cookie, &params);
+
+	return key->key_len ? 0 : -ENOENT;
+}
+
+
+static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
+				u8 key_index, const u8 *mac_addr)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(ndev);
+	struct iwm_key *key = &iwm->keys[key_index];
+
+	if (!iwm->keys[key_index].key_len) {
+		IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index);
+		return 0;
+	}
+
+	if (key_index == iwm->default_key)
+		iwm->default_key = -1;
+
+	return iwm_set_key(iwm, 1, key);
+}
+
+static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
+					struct net_device *ndev,
+					u8 key_index)
+{
+	struct iwm_priv *iwm = ndev_to_iwm(ndev);
+	int ret;
+
+	IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index);
+
+	if (!iwm->keys[key_index].key_len) {
+		IWM_ERR(iwm, "Key %d not used\n", key_index);
+		return -EINVAL;
+	}
+
+	ret = iwm_set_tx_key(iwm, key_index);
+	if (ret < 0)
+		return ret;
+
+	iwm->default_key = key_index;
+
+	return iwm_reset_profile(iwm);
+}
+
+
 int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
 {
 	struct wiphy *wiphy = iwm_to_wiphy(iwm);
@@ -331,6 +499,10 @@ static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
 
 static struct cfg80211_ops iwm_cfg80211_ops = {
 	.change_virtual_intf = iwm_cfg80211_change_iface,
+	.add_key = iwm_cfg80211_add_key,
+	.get_key = iwm_cfg80211_get_key,
+	.del_key = iwm_cfg80211_del_key,
+	.set_default_key = iwm_cfg80211_set_default_key,
 	.scan = iwm_cfg80211_scan,
 	.set_wiphy_params = iwm_cfg80211_set_wiphy_params,
 	.join_ibss = iwm_cfg80211_join_ibss,
drivers/net/wireless/iwmc3200wifi/commands.c +25 -47

170 lines not on this page · raw message

drivers/net/wireless/iwmc3200wifi/commands.h +1 -2
index 36b13a1..3510df8 100644
@@ -406,8 +406,7 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm);
 int iwm_invalidate_mlme_profile(struct iwm_priv *iwm);
 int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id);
 int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx);
-int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
-		struct iwm_key *key);
+int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key);
 int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags);
 int iwm_send_umac_channel_list(struct iwm_priv *iwm);
 int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
drivers/net/wireless/iwmc3200wifi/iwm.h +6 -8
index af11ea2..a08c6e9 100644
@@ -162,13 +162,11 @@ struct iwm_umac_key_hdr {
 
 struct iwm_key {
 	struct iwm_umac_key_hdr hdr;
-	u8 in_use;
-	u8 alg;
-	u32 flags;
-	u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE];
-	u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE];
-	u8 key_len;
-	u8 key[32];
+	u32 cipher;
+	u8 key[WLAN_MAX_KEY_LEN];
+	u8 seq[IW_ENCODE_SEQ_MAX_SIZE];
+	int key_len;
+	int seq_len;
 };
 
 #define IWM_RX_ID_HASH  0xff
@@ -277,7 +275,7 @@ struct iwm_priv {
 	struct iwm_tx_queue txq[IWM_TX_QUEUES];
 
 	struct iwm_key keys[IWM_NUM_KEYS];
-	struct iwm_key *default_key;
+	s8 default_key;
 
 	DECLARE_BITMAP(wifi_ntfy, WIFI_IF_NTFY_MAX);
 	wait_queue_head_t wifi_ntfy_queue;
drivers/net/wireless/iwmc3200wifi/main.c +2 -2
index f8e5d1b..3ac5a16 100644
@@ -230,7 +230,7 @@ int iwm_priv_init(struct iwm_priv *iwm)
 	for (i = 0; i < IWM_NUM_KEYS; i++)
 		memset(&iwm->keys[i], 0, sizeof(struct iwm_key));
 
-	iwm->default_key = NULL;
+	iwm->default_key = -1;
 
 	init_timer(&iwm->watchdog);
 	iwm->watchdog.function = iwm_watchdog;
@@ -713,7 +713,7 @@ int __iwm_down(struct iwm_priv *iwm)
 	iwm->umac_profile = NULL;
 	iwm_bss_list_clean(iwm);
 
-	iwm->default_key = NULL;
+	iwm->default_key = -1;
 	iwm->core_enabled = 0;
 
 	ret = iwm_bus_disable(iwm);
drivers/net/wireless/iwmc3200wifi/wext.c +13 -273

352 lines not on this page · raw message

Signature · 1 line
1.6.3.1
#9 lore raw

PATCH 9/12 iwmc3200wifi: change coexist periodic calibration flag

From: Zhu Yi <[email protected]>

The patch changes coexist periodic calibration priority flag. It also set wireless mode to UMAC and set PM control flag to 0x1.

---
 drivers/net/wireless/iwmc3200wifi/commands.c |   13 +++++++++----
 drivers/net/wireless/iwmc3200wifi/commands.h |    4 ++--
 2 files changed, 11 insertions(+), 6 deletions(-)
drivers/net/wireless/iwmc3200wifi/commands.c +9 -4
index 145f6f5..0d35afe 100644
@@ -120,7 +120,7 @@ static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] =
 	{4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
 	{3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
 	{5, 5, 0, COEX_CALIBRATION_FLAGS},
-	{4, 4, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
+	{3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
 	{5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS},
 	{4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS},
 	{4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
@@ -345,8 +345,7 @@ int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
 	return ret;
 }
 
-int iwm_send_umac_config(struct iwm_priv *iwm,
-			 __le32 reset_flags)
+int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags)
 {
 	int ret;
 
@@ -374,6 +373,12 @@ int iwm_send_umac_config(struct iwm_priv *iwm,
 		return ret;
 
 	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+				      CFG_WIRELESS_MODE,
+				      iwm->conf.wireless_mode);
+	if (ret < 0)
+		return ret;
+
+	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
 				      CFG_COEX_MODE, iwm->conf.coexist_mode);
 	if (ret < 0)
 		return ret;
@@ -415,7 +420,7 @@ int iwm_send_umac_config(struct iwm_priv *iwm,
 		return ret;
 
 	ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
-				      CFG_PM_CTRL_FLAGS, 0x30001);
+				      CFG_PM_CTRL_FLAGS, 0x1);
 	if (ret < 0)
 		return ret;
 
drivers/net/wireless/iwmc3200wifi/commands.h +2 -2
index 3510df8..e24d5b6 100644
@@ -106,8 +106,7 @@ enum {
 	CFG_TLC_SPATIAL_STREAM_SUPPORTED,
 	CFG_TLC_RETRY_PER_RATE,
 	CFG_TLC_RETRY_PER_HT_RATE,
-	CFG_TLC_FIXED_RATE,
-	CFG_TLC_FIXED_RATE_FLAGS,
+	CFG_TLC_FIXED_MCS,
 	CFG_TLC_CONTROL_FLAGS,
 	CFG_TLC_SR_MIN_FAIL,
 	CFG_TLC_SR_MIN_PASS,
@@ -232,6 +231,7 @@ struct iwm_umac_cmd_get_channel_list {
 /* Wireless mode */
 #define WIRELESS_MODE_11A  0x1
 #define WIRELESS_MODE_11G  0x2
+#define WIRELESS_MODE_11N  0x4
 
 #define UMAC_PROFILE_EX_IE_REQUIRED	0x1
 #define UMAC_PROFILE_QOS_ALLOWED	0x2
Signature · 1 line
1.6.3.1
#10 lore raw

PATCH 10/12 iwmc3200wifi: cache keys when interface is down

From: Samuel Ortiz <[email protected]>

When the interface is down and one sets a WEP key from userspace, we should be able to simply cache it.
Since that implies setting part of the profile's security settings, we now alloc/free the umac_profile at probe/remove time, and no longer at interface bring up/down time. Simply resetting it during the latter is enough.

---
 drivers/net/wireless/iwmc3200wifi/cfg80211.c |   12 ++++++++++--
 drivers/net/wireless/iwmc3200wifi/main.c     |   20 +++-----------------
 drivers/net/wireless/iwmc3200wifi/netdev.c   |   15 +++++++++++++++
 3 files changed, 28 insertions(+), 19 deletions(-)
drivers/net/wireless/iwmc3200wifi/cfg80211.c +10 -2
index ad62b20..71efbc1 100644
@@ -271,6 +271,10 @@ static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
 	if (key_index == iwm->default_key)
 		iwm->default_key = -1;
 
+	/* If the interface is down, we just cache this */
+	if (!test_bit(IWM_STATUS_READY, &iwm->status))
+		return 0;
+
 	return iwm_set_key(iwm, 1, key);
 }
 
@@ -288,12 +292,16 @@ static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
 		return -EINVAL;
 	}
 
+	iwm->default_key = key_index;
+
+	/* If the interface is down, we just cache this */
+	if (!test_bit(IWM_STATUS_READY, &iwm->status))
+		return 0;
+
 	ret = iwm_set_tx_key(iwm, key_index);
 	if (ret < 0)
 		return ret;
 
-	iwm->default_key = key_index;
-
 	return iwm_reset_profile(iwm);
 }
 
drivers/net/wireless/iwmc3200wifi/main.c +3 -17
index 3ac5a16..bbc3c6b 100644
@@ -647,19 +647,10 @@ int __iwm_up(struct iwm_priv *iwm)
 		}
 	}
 
-	iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile),
-				    GFP_KERNEL);
-	if (!iwm->umac_profile) {
-		IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
-		goto err_fw;
-	}
-
-	iwm_init_default_profile(iwm, iwm->umac_profile);
-
 	ret = iwm_channels_init(iwm);
 	if (ret < 0) {
 		IWM_ERR(iwm, "Couldn't init channels\n");
-		goto err_profile;
+		goto err_fw;
 	}
 
 	/* Set the READY bit to indicate interface is brought up successfully */
@@ -667,10 +658,6 @@ int __iwm_up(struct iwm_priv *iwm)
 
 	return 0;
 
- err_profile:
-	kfree(iwm->umac_profile);
-	iwm->umac_profile = NULL;
-
  err_fw:
 	iwm_eeprom_exit(iwm);
 
@@ -709,10 +696,9 @@ int __iwm_down(struct iwm_priv *iwm)
 	clear_bit(IWM_STATUS_READY, &iwm->status);
 
 	iwm_eeprom_exit(iwm);
-	kfree(iwm->umac_profile);
-	iwm->umac_profile = NULL;
 	iwm_bss_list_clean(iwm);
-
+	iwm_init_default_profile(iwm, iwm->umac_profile);
+	iwm->umac_profile_active = false;
 	iwm->default_key = -1;
 	iwm->core_enabled = 0;
 
drivers/net/wireless/iwmc3200wifi/netdev.c +15 -0
index d1797b9..48e626f 100644
@@ -48,6 +48,7 @@
 #include <linux/netdevice.h>
 
 #include "iwm.h"
+#include "commands.h"
 #include "cfg80211.h"
 #include "debug.h"
 
@@ -137,8 +138,20 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
 	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
 	wdev->netdev = ndev;
 
+	iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile),
+				    GFP_KERNEL);
+	if (!iwm->umac_profile) {
+		dev_err(dev, "Couldn't alloc memory for profile\n");
+		goto out_profile;
+	}
+
+	iwm_init_default_profile(iwm, iwm->umac_profile);
+
 	return iwm;
 
+ out_profile:
+	free_netdev(ndev);
+
  out_priv:
 	iwm_priv_deinit(iwm);
 
@@ -155,6 +168,8 @@ void iwm_if_free(struct iwm_priv *iwm)
 	free_netdev(iwm_to_ndev(iwm));
 	iwm_wdev_free(iwm);
 	iwm_priv_deinit(iwm);
+	kfree(iwm->umac_profile);
+	iwm->umac_profile = NULL;
 }
 
 int iwm_if_add(struct iwm_priv *iwm)
Signature · 1 line
1.6.3.1
#11 lore raw

PATCH 11/12 iwmc3200wifi: remove select LIB80211 from Kconfig

From: Zhu Yi <[email protected]>

iwmc3200wifi currently doesn't link with lib80211. So remove the Kconfig selection.

---
 drivers/net/wireless/iwmc3200wifi/Kconfig |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)
drivers/net/wireless/iwmc3200wifi/Kconfig +0 -1
index 651c0f6..1eccb6d 100644
@@ -3,7 +3,6 @@ config IWM
 	depends on MMC && WLAN_80211 && EXPERIMENTAL
 	depends on CFG80211
 	select WIRELESS_EXT
-	select LIB80211
 	select FW_LOADER
 
 config IWM_DEBUG
Signature · 1 line
1.6.3.1
#12 lore raw

PATCH 12/12 iwmc3200wifi: rfkill cleanup

From: Zhu Yi <[email protected]>

The patch cleans up the unused rfkill related structures and flags. It also adds wext and cfg80211 handlers for txpower auto and off so that software rfkill could be issued by user space.

---
 drivers/net/wireless/iwmc3200wifi/cfg80211.c |   24 ++++++++++++++++++++++++
 drivers/net/wireless/iwmc3200wifi/iwm.h      |    7 +------
 drivers/net/wireless/iwmc3200wifi/netdev.c   |    6 ++----
 drivers/net/wireless/iwmc3200wifi/rx.c       |   18 +++++++++---------
 drivers/net/wireless/iwmc3200wifi/sdio.c     |    6 +-----
 drivers/net/wireless/iwmc3200wifi/wext.c     |    4 ++--
 6 files changed, 39 insertions(+), 26 deletions(-)
drivers/net/wireless/iwmc3200wifi/cfg80211.c +24 -0
index 71efbc1..e58026f 100644
@@ -505,6 +505,28 @@ static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
 	return 0;
 }
 
+static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
+				    enum tx_power_setting type, int dbm)
+{
+	switch (type) {
+	case TX_POWER_AUTOMATIC:
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
+{
+	struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+	*dbm = iwm->txpower;
+
+	return 0;
+}
+
 static struct cfg80211_ops iwm_cfg80211_ops = {
 	.change_virtual_intf = iwm_cfg80211_change_iface,
 	.add_key = iwm_cfg80211_add_key,
@@ -515,6 +537,8 @@ static struct cfg80211_ops iwm_cfg80211_ops = {
 	.set_wiphy_params = iwm_cfg80211_set_wiphy_params,
 	.join_ibss = iwm_cfg80211_join_ibss,
 	.leave_ibss = iwm_cfg80211_leave_ibss,
+	.set_tx_power = iwm_cfg80211_set_txpower,
+	.get_tx_power = iwm_cfg80211_get_txpower,
 };
 
 struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
drivers/net/wireless/iwmc3200wifi/iwm.h +1 -6
index a08c6e9..f187f34 100644
@@ -185,10 +185,6 @@ struct iwm_key {
 #define IWM_STATUS_ASSOCIATING		4
 #define IWM_STATUS_ASSOCIATED		5
 
-#define IWM_RADIO_RFKILL_OFF		0
-#define IWM_RADIO_RFKILL_HW		1
-#define IWM_RADIO_RFKILL_SW		2
-
 struct iwm_tx_queue {
 	int id;
 	struct sk_buff_head queue;
@@ -222,7 +218,6 @@ struct iwm_priv {
 	struct iwm_conf conf;
 
 	unsigned long status;
-	unsigned long radio;
 
 	struct list_head pending_notif;
 	wait_queue_head_t notif_queue;
@@ -241,6 +236,7 @@ struct iwm_priv {
 	u8 bssid[ETH_ALEN];
 	u8 channel;
 	u16 rate;
+	u32 txpower;
 
 	struct iwm_sta_info sta_table[IWM_STA_TABLE_NUM];
 	struct list_head bss_list;
@@ -291,7 +287,6 @@ struct iwm_priv {
 	struct timer_list watchdog;
 	struct work_struct reset_worker;
 	struct mutex mutex;
-	struct rfkill *rfkill;
 
 	char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
 };
drivers/net/wireless/iwmc3200wifi/netdev.c +2 -4
index 48e626f..5064a31 100644
@@ -57,8 +57,7 @@ static int iwm_open(struct net_device *ndev)
 	struct iwm_priv *iwm = ndev_to_iwm(ndev);
 	int ret = -ENODEV;
 
-	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)
-	    && test_bit(IWM_STATUS_BUS_READY, &iwm->status))
+	if (test_bit(IWM_STATUS_BUS_READY, &iwm->status))
 		ret = iwm_up(iwm);
 
 	return ret;
@@ -69,8 +68,7 @@ static int iwm_stop(struct net_device *ndev)
 	struct iwm_priv *iwm = ndev_to_iwm(ndev);
 	int ret = -ENODEV;
 
-	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)
-	    && test_bit(IWM_STATUS_BUS_READY, &iwm->status))
+	if (test_bit(IWM_STATUS_BUS_READY, &iwm->status))
 		ret = iwm_down(iwm);
 
 	return ret;
drivers/net/wireless/iwmc3200wifi/rx.c +9 -9
index 49a8be7..55871da 100644
@@ -143,17 +143,18 @@ static int iwm_ntf_init_complete(struct iwm_priv *iwm, u8 *buf,
 				 unsigned long buf_size,
 				 struct iwm_wifi_cmd *cmd)
 {
+	struct wiphy *wiphy = iwm_to_wiphy(iwm);
 	struct iwm_umac_notif_init_complete *init_complete =
 			(struct iwm_umac_notif_init_complete *)(buf);
 	u16 status = le16_to_cpu(init_complete->status);
+	bool blocked = (status == UMAC_NTFY_INIT_COMPLETE_STATUS_ERR);
 
-	if (status == UMAC_NTFY_INIT_COMPLETE_STATUS_ERR) {
+	if (blocked)
 		IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is on (radio off)\n");
-		set_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
-	} else {
+	else
 		IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is off (radio on)\n");
-		clear_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
-	}
+
+	wiphy_rfkill_set_hw_state(wiphy, blocked);
 
 	return 0;
 }
@@ -875,6 +876,7 @@ static int iwm_ntf_statistics(struct iwm_priv *iwm, u8 *buf,
 		/* UMAC passes rate info multiplies by 2 */
 		iwm->rate = max_rate >> 1;
 	}
+	iwm->txpower = le32_to_cpu(stats->tx_power);
 
 	wstats->status = 0;
 
@@ -1015,6 +1017,7 @@ static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
 static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf,
 			      unsigned long buf_size, struct iwm_wifi_cmd *cmd)
 {
+	struct wiphy *wiphy = iwm_to_wiphy(iwm);
 	struct iwm_lmac_card_state *state = (struct iwm_lmac_card_state *)
 				(buf + sizeof(struct iwm_umac_wifi_in_hdr));
 	u32 flags = le32_to_cpu(state->flags);
@@ -1023,10 +1026,7 @@ static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf,
 		 flags & IWM_CARD_STATE_HW_DISABLED ? "ON" : "OFF",
 		 flags & IWM_CARD_STATE_CTKILL_DISABLED ? "ON" : "OFF");
 
-	if (flags & IWM_CARD_STATE_HW_DISABLED)
-		set_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
-	else
-		clear_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
+	wiphy_rfkill_set_hw_state(wiphy, flags & IWM_CARD_STATE_HW_DISABLED);
 
 	return 0;
 }
drivers/net/wireless/iwmc3200wifi/sdio.c +1 -5
index 462bbf6..85aa2b1 100644
@@ -510,11 +510,7 @@ static struct sdio_driver iwm_sdio_driver = {
 
 static int __init iwm_sdio_init_module(void)
 {
-	int ret;
-
-	ret = sdio_register_driver(&iwm_sdio_driver);
-
-	return ret;
+	return sdio_register_driver(&iwm_sdio_driver);
 }
 
 static void __exit iwm_sdio_exit_module(void)
drivers/net/wireless/iwmc3200wifi/wext.c +2 -2
index 3062f37..9734573 100644
@@ -452,8 +452,8 @@ static const iw_handler iwm_handlers[] =
 	(iw_handler) cfg80211_wext_giwrts,		/* SIOCGIWRTS */
 	(iw_handler) cfg80211_wext_siwfrag,	        /* SIOCSIWFRAG */
 	(iw_handler) cfg80211_wext_giwfrag,		/* SIOCGIWFRAG */
-	(iw_handler) NULL,				/* SIOCSIWTXPOW */
-	(iw_handler) NULL,				/* SIOCGIWTXPOW */
+	(iw_handler) cfg80211_wext_siwtxpower,		/* SIOCSIWTXPOW */
+	(iw_handler) cfg80211_wext_giwtxpower,		/* SIOCGIWTXPOW */
 	(iw_handler) NULL,				/* SIOCSIWRETRY */
 	(iw_handler) NULL,				/* SIOCGIWRETRY */
 	(iw_handler) cfg80211_wext_siwencode,		/* SIOCSIWENCODE */
Signature · 1 line
1.6.3.1
maintainer
#13 lore raw

Re: [PATCH 01/12][2.6.31 and w-t] iwmc3200wifi: fix sdio initialisation race

In reply to Samuel Ortiz
Quoted 7 lines · Samuel Ortiz

From: Samuel Ortiz <[email protected]>

When modprobing this driver, the netdev interface is ready before the SDIO function is correctly set.
This can lead to userspace calling ndo_open() before the SDIO bus is ready. We fix that by returning an error from ndo_open() when the IWM_STATUS_BUS_READY bit is not set yet.

Can't you wait for it to be set instead?

johannes

Quoted 103 lines
---
 drivers/net/wireless/iwmc3200wifi/iwm.h    |   11 ++++++-----
 drivers/net/wireless/iwmc3200wifi/main.c   |    6 +++++-
 drivers/net/wireless/iwmc3200wifi/netdev.c |   10 ++++++----
 drivers/net/wireless/iwmc3200wifi/sdio.c   |    4 ++++
 4 files changed, 21 insertions(+), 10 deletions(-)
drivers/net/wireless/iwmc3200wifi/iwm.h +6 -5
index 635c16e..3cc4e91 100644
@@ -180,11 +180,12 @@ struct iwm_key {
 
 #define IWM_SCAN_ID_MAX 0xff
 
-#define IWM_STATUS_READY		0
-#define IWM_STATUS_SCANNING		1
-#define IWM_STATUS_SCAN_ABORTING	2
-#define IWM_STATUS_ASSOCIATING		3
-#define IWM_STATUS_ASSOCIATED		4
+#define IWM_STATUS_BUS_READY		0
+#define IWM_STATUS_READY		1
+#define IWM_STATUS_SCANNING		2
+#define IWM_STATUS_SCAN_ABORTING	3
+#define IWM_STATUS_ASSOCIATING		4
+#define IWM_STATUS_ASSOCIATED		5
 
 #define IWM_RADIO_RFKILL_OFF		0
 #define IWM_RADIO_RFKILL_HW		1
drivers/net/wireless/iwmc3200wifi/main.c +5 -1
index 6a2640f..09ed247 100644
@@ -227,11 +227,15 @@ int iwm_priv_init(struct iwm_priv *iwm)
 void iwm_reset(struct iwm_priv *iwm)
 {
 	struct iwm_notif *notif, *next;
+	unsigned long status = 0;
 
 	if (test_bit(IWM_STATUS_READY, &iwm->status))
 		iwm_target_reset(iwm);
 
-	iwm->status = 0;
+	if (test_bit(IWM_STATUS_BUS_READY, &iwm->status))
+		set_bit(IWM_STATUS_BUS_READY, &status);
+
+	iwm->status = status;
 	iwm->scan_id = 1;
 
 	list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
drivers/net/wireless/iwmc3200wifi/netdev.c +6 -4
index 68e2c3b..5b7aa34 100644
@@ -54,9 +54,10 @@
 static int iwm_open(struct net_device *ndev)
 {
 	struct iwm_priv *iwm = ndev_to_iwm(ndev);
-	int ret = 0;
+	int ret = -ENODEV;
 
-	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)
+	    && test_bit(IWM_STATUS_BUS_READY, &iwm->status))
 		ret = iwm_up(iwm);
 
 	return ret;
@@ -65,9 +66,10 @@ static int iwm_open(struct net_device *ndev)
 static int iwm_stop(struct net_device *ndev)
 {
 	struct iwm_priv *iwm = ndev_to_iwm(ndev);
-	int ret = 0;
+	int ret = -ENODEV;
 
-	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)
+	    && test_bit(IWM_STATUS_BUS_READY, &iwm->status))
 		ret = iwm_down(iwm);
 
 	return ret;
drivers/net/wireless/iwmc3200wifi/sdio.c +4 -0
index b54da67..97e7da3 100644
@@ -454,6 +454,9 @@ static int iwm_sdio_probe(struct sdio_func *func,
 
 	INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
 
+	/* The SDIO bus is set and ready to be enabled */
+	set_bit(IWM_STATUS_BUS_READY, &iwm->status);
+
 	dev_info(dev, "IWM SDIO probe\n");
 
 	return 0;
@@ -476,6 +479,7 @@ static void iwm_sdio_remove(struct sdio_func *func)
 	destroy_workqueue(hw->isr_wq);
 
 	sdio_set_drvdata(func, NULL);
+	clear_bit(IWM_STATUS_BUS_READY, &iwm->status);
 
 	dev_info(dev, "IWM SDIO remove\n");
 
#14 lore raw

Re: [PATCH 01/12][2.6.31 and w-t] iwmc3200wifi: fix sdio initialisation race

Quoted 10 lines · Johannes Berg
Quoted 7 lines · Samuel Ortiz

From: Samuel Ortiz <[email protected]>

When modprobing this driver, the netdev interface is ready before the SDIO function is correctly set.
This can lead to userspace calling ndo_open() before the SDIO bus is ready. We fix that by returning an error from ndo_open() when the IWM_STATUS_BUS_READY bit is not set yet.

Can't you wait for it to be set instead?

I thought about it, but I didnt want to add one more completion/wait_queue pointer to our already crowded iwm structures.
But if you or John insist on it, I can definitely do it.

Cheers,
Samuel.

Quoted 105 lines

johannes

Quoted 103 lines
---
 drivers/net/wireless/iwmc3200wifi/iwm.h    |   11 ++++++-----
 drivers/net/wireless/iwmc3200wifi/main.c   |    6 +++++-
 drivers/net/wireless/iwmc3200wifi/netdev.c |   10 ++++++----
 drivers/net/wireless/iwmc3200wifi/sdio.c   |    4 ++++
 4 files changed, 21 insertions(+), 10 deletions(-)
drivers/net/wireless/iwmc3200wifi/iwm.h +6 -5
index 635c16e..3cc4e91 100644
@@ -180,11 +180,12 @@ struct iwm_key {
 
 #define IWM_SCAN_ID_MAX 0xff
 
-#define IWM_STATUS_READY		0
-#define IWM_STATUS_SCANNING		1
-#define IWM_STATUS_SCAN_ABORTING	2
-#define IWM_STATUS_ASSOCIATING		3
-#define IWM_STATUS_ASSOCIATED		4
+#define IWM_STATUS_BUS_READY		0
+#define IWM_STATUS_READY		1
+#define IWM_STATUS_SCANNING		2
+#define IWM_STATUS_SCAN_ABORTING	3
+#define IWM_STATUS_ASSOCIATING		4
+#define IWM_STATUS_ASSOCIATED		5
 
 #define IWM_RADIO_RFKILL_OFF		0
 #define IWM_RADIO_RFKILL_HW		1
drivers/net/wireless/iwmc3200wifi/main.c +5 -1
index 6a2640f..09ed247 100644
@@ -227,11 +227,15 @@ int iwm_priv_init(struct iwm_priv *iwm)
 void iwm_reset(struct iwm_priv *iwm)
 {
 	struct iwm_notif *notif, *next;
+	unsigned long status = 0;
 
 	if (test_bit(IWM_STATUS_READY, &iwm->status))
 		iwm_target_reset(iwm);
 
-	iwm->status = 0;
+	if (test_bit(IWM_STATUS_BUS_READY, &iwm->status))
+		set_bit(IWM_STATUS_BUS_READY, &status);
+
+	iwm->status = status;
 	iwm->scan_id = 1;
 
 	list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
drivers/net/wireless/iwmc3200wifi/netdev.c +6 -4
index 68e2c3b..5b7aa34 100644
@@ -54,9 +54,10 @@
 static int iwm_open(struct net_device *ndev)
 {
 	struct iwm_priv *iwm = ndev_to_iwm(ndev);
-	int ret = 0;
+	int ret = -ENODEV;
 
-	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)
+	    && test_bit(IWM_STATUS_BUS_READY, &iwm->status))
 		ret = iwm_up(iwm);
 
 	return ret;
@@ -65,9 +66,10 @@ static int iwm_open(struct net_device *ndev)
 static int iwm_stop(struct net_device *ndev)
 {
 	struct iwm_priv *iwm = ndev_to_iwm(ndev);
-	int ret = 0;
+	int ret = -ENODEV;
 
-	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
+	if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)
+	    && test_bit(IWM_STATUS_BUS_READY, &iwm->status))
 		ret = iwm_down(iwm);
 
 	return ret;
drivers/net/wireless/iwmc3200wifi/sdio.c +4 -0
index b54da67..97e7da3 100644
@@ -454,6 +454,9 @@ static int iwm_sdio_probe(struct sdio_func *func,
 
 	INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
 
+	/* The SDIO bus is set and ready to be enabled */
+	set_bit(IWM_STATUS_BUS_READY, &iwm->status);
+
 	dev_info(dev, "IWM SDIO probe\n");
 
 	return 0;
@@ -476,6 +479,7 @@ static void iwm_sdio_remove(struct sdio_func *func)
 	destroy_workqueue(hw->isr_wq);
 
 	sdio_set_drvdata(func, NULL);
+	clear_bit(IWM_STATUS_BUS_READY, &iwm->status);
 
 	dev_info(dev, "IWM SDIO remove\n");
 
Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/