2009-06-15 15:37:20

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 00/12] 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(-)



2009-06-15 15:38:36

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 06/12][w-t] 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.

Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/wext.c | 19 ++++++++++++++++++-
1 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/wext.c b/drivers/net/wireless/iwmc3200wifi/wext.c
index 584c94d..8891949 100644
--- a/drivers/net/wireless/iwmc3200wifi/wext.c
+++ b/drivers/net/wireless/iwmc3200wifi/wext.c
@@ -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)
--
1.6.3.1


2009-06-15 15:38:53

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 09/12][w-t] 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.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/commands.c | 13 +++++++++----
drivers/net/wireless/iwmc3200wifi/commands.h | 4 ++--
2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 145f6f5..0d35afe 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -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;

diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h
index 3510df8..e24d5b6 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.h
+++ b/drivers/net/wireless/iwmc3200wifi/commands.h
@@ -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
--
1.6.3.1


2009-06-15 15:38:26

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 04/12][2.6.31 and w-t] 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.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/sdio.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
index 057a588..462bbf6 100644
--- a/drivers/net/wireless/iwmc3200wifi/sdio.c
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -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);
--
1.6.3.1


2009-06-15 15:38:16

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 02/12][2.6.31 and w-t] 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.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
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(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index 3cc4e91..a0e7377 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -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);
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 09ed247..cfe1890 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -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
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index 5b7aa34..de16b2b 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -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);
}
--
1.6.3.1


2009-06-15 15:38:41

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 07/12][w-t] 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.

Signed-off-by: Samuel Ortiz <[email protected]>
---
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(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 834a7f5..337a884 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -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;

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index 8b22df3..af11ea2 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -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;
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index fdac13a..f8e5d1b 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -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);
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index d73cf96..49a8be7 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -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;
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h
index 4a95cce..0af2a3c 100644
--- a/drivers/net/wireless/iwmc3200wifi/umac.h
+++ b/drivers/net/wireless/iwmc3200wifi/umac.h
@@ -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;
--
1.6.3.1


2009-06-15 15:39:04

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 11/12][w-t] iwmc3200wifi: remove select LIB80211 from Kconfig

From: Zhu Yi <[email protected]>

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

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/Kconfig | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig
index 651c0f6..1eccb6d 100644
--- a/drivers/net/wireless/iwmc3200wifi/Kconfig
+++ b/drivers/net/wireless/iwmc3200wifi/Kconfig
@@ -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
--
1.6.3.1


2009-06-15 15:38:45

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 05/12][2.6.31 and w-t] 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.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/iwm.h | 1 +
drivers/net/wireless/iwmc3200wifi/main.c | 54 ++++++++++++++++++++++++++---
2 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index 73c1011..8b22df3 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -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)));
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index cfe1890..fdac13a 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -112,6 +112,9 @@ static void iwm_statistics_request(struct work_struct *work)
iwm_send_umac_stats_req(iwm, 0);
}

+int __iwm_up(struct iwm_priv *iwm);
+int __iwm_down(struct iwm_priv *iwm);
+
static void iwm_reset_worker(struct work_struct *work)
{
struct iwm_priv *iwm;
@@ -120,6 +123,19 @@ static void iwm_reset_worker(struct work_struct *work)

iwm = container_of(work, struct iwm_priv, reset_worker);

+ /*
+ * XXX: The iwm->mutex is introduced purely for this reset work,
+ * because the other users for iwm_up and iwm_down are only netdev
+ * ndo_open and ndo_stop which are already protected by rtnl.
+ * Please remove iwm->mutex together if iwm_reset_worker() is not
+ * required in the future.
+ */
+ if (!mutex_trylock(&iwm->mutex)) {
+ IWM_WARN(iwm, "We are in the middle of interface bringing "
+ "UP/DOWN. Skip driver resetting.\n");
+ return;
+ }
+
if (iwm->umac_profile_active) {
profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL);
if (profile)
@@ -128,10 +144,10 @@ static void iwm_reset_worker(struct work_struct *work)
IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
}

- iwm_down(iwm);
+ __iwm_down(iwm);

while (retry++ < 3) {
- ret = iwm_up(iwm);
+ ret = __iwm_up(iwm);
if (!ret)
break;

@@ -142,7 +158,7 @@ static void iwm_reset_worker(struct work_struct *work)
IWM_WARN(iwm, "iwm_up() failed: %d\n", ret);

kfree(profile);
- return;
+ goto out;
}

if (profile) {
@@ -151,6 +167,9 @@ static void iwm_reset_worker(struct work_struct *work)
iwm_send_mlme_profile(iwm);
kfree(profile);
}
+
+ out:
+ mutex_unlock(&iwm->mutex);
}

static void iwm_watchdog(unsigned long data)
@@ -215,6 +234,7 @@ int iwm_priv_init(struct iwm_priv *iwm)
init_timer(&iwm->watchdog);
iwm->watchdog.function = iwm_watchdog;
iwm->watchdog.data = (unsigned long)iwm;
+ mutex_init(&iwm->mutex);

return 0;
}
@@ -480,7 +500,7 @@ void iwm_link_off(struct iwm_priv *iwm)

iwm_rx_free(iwm);

- cancel_delayed_work(&iwm->stats_request);
+ cancel_delayed_work_sync(&iwm->stats_request);
memset(wstats, 0, sizeof(struct iw_statistics));
wstats->qual.updated = IW_QUAL_ALL_INVALID;

@@ -525,7 +545,7 @@ static int iwm_channels_init(struct iwm_priv *iwm)
return 0;
}

-int iwm_up(struct iwm_priv *iwm)
+int __iwm_up(struct iwm_priv *iwm)
{
int ret;
struct iwm_notif *notif_reboot, *notif_ack = NULL;
@@ -661,7 +681,18 @@ int iwm_up(struct iwm_priv *iwm)
return -EIO;
}

-int iwm_down(struct iwm_priv *iwm)
+int iwm_up(struct iwm_priv *iwm)
+{
+ int ret;
+
+ mutex_lock(&iwm->mutex);
+ ret = __iwm_up(iwm);
+ mutex_unlock(&iwm->mutex);
+
+ return ret;
+}
+
+int __iwm_down(struct iwm_priv *iwm)
{
int ret;

@@ -692,3 +723,14 @@ int iwm_down(struct iwm_priv *iwm)

return 0;
}
+
+int iwm_down(struct iwm_priv *iwm)
+{
+ int ret;
+
+ mutex_lock(&iwm->mutex);
+ ret = __iwm_down(iwm);
+ mutex_unlock(&iwm->mutex);
+
+ return ret;
+}
--
1.6.3.1


2009-06-15 15:38:48

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 08/12][w-t] 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.

Signed-off-by: Samuel Ortiz <[email protected]>
---
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(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index 96f714e..ad62b20 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -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,
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 337a884..145f6f5 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -524,9 +524,6 @@ int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx)
{
struct iwm_umac_tx_key_id tx_key_id;

- if (!iwm->default_key || !iwm->default_key->in_use)
- return -EINVAL;
-
tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID;
tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) -
sizeof(struct iwm_umac_wifi_if));
@@ -569,10 +566,9 @@ static int iwm_check_profile(struct iwm_priv *iwm)
return 0;
}

-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 ret;
+ int ret = 0;
u8 cmd[64], *sta_addr, *key_data, key_len;
s8 key_idx;
u16 cmd_size = 0;
@@ -582,9 +578,6 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd;
struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd;

- if (set_tx_key)
- iwm->default_key = key;
-
/*
* We check if our current profile is valid.
* If not, we dont push the key, we just cache them,
@@ -603,8 +596,7 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
key_idx = key->hdr.key_idx;

if (!remove) {
- IWM_DBG_WEXT(iwm, DBG, "key_idx:%d set tx key:%d\n",
- key_idx, set_tx_key);
+ IWM_DBG_WEXT(iwm, DBG, "key_idx:%d\n", key_idx);
IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len);
IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n",
key_hdr->mac, key_hdr->key_idx, key_hdr->multicast);
@@ -616,8 +608,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
iwm->umac_profile->sec.auth_type,
iwm->umac_profile->sec.flags);

- switch (key->alg) {
- case UMAC_CIPHER_TYPE_WEP_40:
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY;
wep40->hdr.buf_size =
cpu_to_le16(sizeof(struct iwm_umac_key_wep40) -
@@ -631,7 +623,7 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
cmd_size = sizeof(struct iwm_umac_key_wep40);
break;

- case UMAC_CIPHER_TYPE_WEP_104:
+ case WLAN_CIPHER_SUITE_WEP104:
wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY;
wep104->hdr.buf_size =
cpu_to_le16(sizeof(struct iwm_umac_key_wep104) -
@@ -645,7 +637,7 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
cmd_size = sizeof(struct iwm_umac_key_wep104);
break;

- case UMAC_CIPHER_TYPE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP:
key_hdr->key_idx++;
ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY;
ccmp->hdr.buf_size =
@@ -657,13 +649,13 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,

memcpy(ccmp->key, key_data, key_len);

- if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
- memcpy(ccmp->iv_count, key->rx_seq, 6);
+ if (key->seq_len)
+ memcpy(ccmp->iv_count, key->seq, key->seq_len);

cmd_size = sizeof(struct iwm_umac_key_ccmp);
break;

- case UMAC_CIPHER_TYPE_TKIP:
+ case WLAN_CIPHER_SUITE_TKIP:
key_hdr->key_idx++;
tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY;
tkip->hdr.buf_size =
@@ -680,8 +672,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE,
IWM_TKIP_MIC_SIZE);

- if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
- memcpy(ccmp->iv_count, key->rx_seq, 6);
+ if (key->seq_len)
+ memcpy(ccmp->iv_count, key->seq, key->seq_len);

cmd_size = sizeof(struct iwm_umac_key_tkip);
break;
@@ -690,8 +682,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
return -ENOTSUPP;
}

- if ((key->alg == UMAC_CIPHER_TYPE_CCMP) ||
- (key->alg == UMAC_CIPHER_TYPE_TKIP))
+ if ((key->cipher == WLAN_CIPHER_SUITE_TKIP) ||
+ (key->cipher == WLAN_CIPHER_SUITE_CCMP))
/*
* UGLY_UGLY_UGLY
* Copied HACK from the MWG driver.
@@ -702,23 +694,11 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
schedule_timeout_interruptible(usecs_to_jiffies(300));

ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1);
- if (ret < 0)
- goto err;
-
- /*
- * We need a default key only if it is set and
- * if we're doing WEP.
- */
- if (iwm->default_key == key &&
- ((key->alg == UMAC_CIPHER_TYPE_WEP_40) ||
- (key->alg == UMAC_CIPHER_TYPE_WEP_104))) {
- ret = iwm_set_tx_key(iwm, key_idx);
- if (ret < 0)
- goto err;
- }
} else {
struct iwm_umac_key_remove key_remove;

+ IWM_DBG_WEXT(iwm, ERR, "Removing key_idx:%d\n", key_idx);
+
key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY;
key_remove.hdr.buf_size =
cpu_to_le16(sizeof(struct iwm_umac_key_remove) -
@@ -732,13 +712,9 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
if (ret < 0)
return ret;

- iwm->keys[key_idx].in_use = 0;
+ iwm->keys[key_idx].key_len = 0;
}

- return 0;
-
- err:
- kfree(key);
return ret;
}

@@ -761,22 +737,24 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm)
}

for (i = 0; i < IWM_NUM_KEYS; i++)
- if (iwm->keys[i].in_use) {
- int default_key = 0;
+ if (iwm->keys[i].key_len) {
struct iwm_key *key = &iwm->keys[i];

- if (key == iwm->default_key)
- default_key = 1;
-
/* Wait for the profile before sending the keys */
wait_event_interruptible_timeout(iwm->mlme_queue,
(test_bit(IWM_STATUS_ASSOCIATING, &iwm->status) ||
test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)),
3 * HZ);

- ret = iwm_set_key(iwm, 0, default_key, key);
+ ret = iwm_set_key(iwm, 0, key);
if (ret < 0)
return ret;
+
+ if (iwm->default_key == i) {
+ ret = iwm_set_tx_key(iwm, i);
+ if (ret < 0)
+ return ret;
+ }
}

return 0;
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h
index 36b13a1..3510df8 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.h
+++ b/drivers/net/wireless/iwmc3200wifi/commands.h
@@ -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,
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index af11ea2..a08c6e9 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -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;
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index f8e5d1b..3ac5a16 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -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);
diff --git a/drivers/net/wireless/iwmc3200wifi/wext.c b/drivers/net/wireless/iwmc3200wifi/wext.c
index 8891949..3062f37 100644
--- a/drivers/net/wireless/iwmc3200wifi/wext.c
+++ b/drivers/net/wireless/iwmc3200wifi/wext.c
@@ -84,6 +84,8 @@ static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
struct iwm_priv *iwm = ndev_to_iwm(dev);
int ret;

+ IWM_DBG_WEXT(iwm, DBG, "Set BSSID: %pM\n", ap_addr->sa_data);
+
if (iwm->conf.mode == UMAC_MODE_IBSS)
return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);

@@ -116,8 +118,7 @@ static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
*/
if (is_zero_ether_addr(ap_addr->sa_data)) {
for (i = 0; i < IWM_NUM_KEYS; i++)
- iwm->keys[i].in_use = 0;
-
+ iwm->keys[i].key_len = 0;
}

ret = iwm_invalidate_mlme_profile(iwm);
@@ -163,6 +164,8 @@ static int iwm_wext_siwessid(struct net_device *dev,
size_t len = data->length;
int ret;

+ IWM_DBG_WEXT(iwm, DBG, "Set ESSID: >%s<\n", ssid);
+
if (iwm->conf.mode == UMAC_MODE_IBSS)
return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);

@@ -212,27 +215,6 @@ static int iwm_wext_giwessid(struct net_device *dev,
return 0;
}

-static struct iwm_key *
-iwm_key_init(struct iwm_priv *iwm, u8 key_idx, bool in_use,
- struct iw_encode_ext *ext, u8 alg)
-{
- struct iwm_key *key = &iwm->keys[key_idx];
-
- memset(key, 0, sizeof(struct iwm_key));
- memcpy(key->hdr.mac, ext->addr.sa_data, ETH_ALEN);
- key->hdr.key_idx = key_idx;
- if (is_broadcast_ether_addr(ext->addr.sa_data))
- key->hdr.multicast = 1;
-
- key->in_use = in_use;
- key->flags = ext->ext_flags;
- key->alg = alg;
- key->key_len = ext->key_len;
- memcpy(key->key, ext->key, ext->key_len);
-
- return key;
-}
-
static int iwm_wext_giwrate(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rate, char *extra)
@@ -244,184 +226,6 @@ static int iwm_wext_giwrate(struct net_device *dev,
return 0;
}

-static int iwm_wext_siwencode(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *erq, char *key_buf)
-{
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- struct iwm_key *uninitialized_var(key);
- int idx, i, uninitialized_var(alg), remove = 0, ret;
-
- IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", erq->length);
- IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
-
- if (!iwm->umac_profile) {
- IWM_ERR(iwm, "UMAC profile not allocated yet\n");
- return -ENODEV;
- }
-
- if (erq->length == WLAN_KEY_LEN_WEP40) {
- alg = UMAC_CIPHER_TYPE_WEP_40;
- iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_40;
- iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
- } else if (erq->length == WLAN_KEY_LEN_WEP104) {
- alg = UMAC_CIPHER_TYPE_WEP_104;
- iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_104;
- iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_104;
- }
-
- if (erq->flags & IW_ENCODE_RESTRICTED)
- iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
- else
- iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
-
- idx = erq->flags & IW_ENCODE_INDEX;
- if (idx == 0) {
- if (iwm->default_key)
- for (i = 0; i < IWM_NUM_KEYS; i++) {
- if (iwm->default_key == &iwm->keys[i]) {
- idx = i;
- break;
- }
- }
- else
- iwm->default_key = &iwm->keys[idx];
- } else if (idx < 1 || idx > 4) {
- return -EINVAL;
- } else
- idx--;
-
- if (erq->flags & IW_ENCODE_DISABLED)
- remove = 1;
- else if (erq->length == 0) {
- if (!iwm->keys[idx].in_use)
- return -EINVAL;
- iwm->default_key = &iwm->keys[idx];
- }
-
- if (erq->length) {
- key = &iwm->keys[idx];
- memset(key, 0, sizeof(struct iwm_key));
- memset(key->hdr.mac, 0xff, ETH_ALEN);
- key->hdr.key_idx = idx;
- key->hdr.multicast = 1;
- key->in_use = !remove;
- key->alg = alg;
- key->key_len = erq->length;
- memcpy(key->key, key_buf, erq->length);
-
- IWM_DBG_WEXT(iwm, DBG, "Setting key %d, default: %d\n",
- idx, !!iwm->default_key);
- }
-
- if (remove) {
- if ((erq->flags & IW_ENCODE_NOKEY) || (erq->length == 0)) {
- int j;
- for (j = 0; j < IWM_NUM_KEYS; j++)
- if (iwm->keys[j].in_use) {
- struct iwm_key *k = &iwm->keys[j];
-
- k->in_use = 0;
- ret = iwm_set_key(iwm, remove, 0, k);
- if (ret < 0)
- return ret;
- }
-
- iwm->umac_profile->sec.ucast_cipher =
- UMAC_CIPHER_TYPE_NONE;
- iwm->umac_profile->sec.mcast_cipher =
- UMAC_CIPHER_TYPE_NONE;
- iwm->umac_profile->sec.auth_type =
- UMAC_AUTH_TYPE_OPEN;
-
- return 0;
- } else {
- key->in_use = 0;
- return iwm_set_key(iwm, remove, 0, key);
- }
- }
-
- /*
- * If we havent set a profile yet, we cant set keys.
- * Keys will be pushed after we're associated.
- */
- 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)
- 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_wext_giwencode(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *erq, char *key)
-{
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- int idx, i;
-
- idx = erq->flags & IW_ENCODE_INDEX;
- if (idx < 1 || idx > 4) {
- idx = -1;
- if (!iwm->default_key) {
- erq->length = 0;
- erq->flags |= IW_ENCODE_NOKEY;
- return 0;
- } else
- for (i = 0; i < IWM_NUM_KEYS; i++) {
- if (iwm->default_key == &iwm->keys[i]) {
- idx = i;
- break;
- }
- }
- if (idx < 0)
- return -EINVAL;
- } else
- idx--;
-
- erq->flags = idx + 1;
-
- if (!iwm->keys[idx].in_use) {
- erq->length = 0;
- erq->flags |= IW_ENCODE_DISABLED;
- return 0;
- }
-
- memcpy(key, iwm->keys[idx].key,
- min_t(int, erq->length, iwm->keys[idx].key_len));
- erq->length = iwm->keys[idx].key_len;
- erq->flags |= IW_ENCODE_ENABLED;
-
- if (iwm->umac_profile->mode == UMAC_MODE_BSS) {
- switch (iwm->umac_profile->sec.auth_type) {
- case UMAC_AUTH_TYPE_OPEN:
- erq->flags |= IW_ENCODE_OPEN;
- break;
- default:
- erq->flags |= IW_ENCODE_RESTRICTED;
- break;
- }
- }
-
- return 0;
-}
-
static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
{
if (wpa_version & IW_AUTH_WPA_VERSION_WPA2)
@@ -481,6 +285,8 @@ static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
{
u8 *auth_type = &iwm->umac_profile->sec.auth_type;

+ IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
+
if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
*auth_type = UMAC_AUTH_TYPE_8021X;
else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
@@ -530,6 +336,8 @@ static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
{
u8 *auth_type = &iwm->umac_profile->sec.auth_type;

+ IWM_DBG_WEXT(iwm, DBG, "auth_alg: 0x%x\n", auth_alg);
+
switch (auth_alg) {
case IW_AUTH_ALG_OPEN_SYSTEM:
*auth_type = UMAC_AUTH_TYPE_OPEN;
@@ -541,6 +349,7 @@ static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
return -EINVAL;
*auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
} else {
+ IWM_DBG_WEXT(iwm, DBG, "WEP shared key\n");
*auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
}
break;
@@ -603,75 +412,6 @@ static int iwm_wext_giwauth(struct net_device *dev,
return 0;
}

-static int iwm_wext_siwencodeext(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *erq, char *extra)
-{
- struct iwm_priv *iwm = ndev_to_iwm(dev);
- struct iwm_key *key;
- struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
- int uninitialized_var(alg), idx, i, remove = 0;
-
- IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg);
- IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len);
- IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags);
- IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
- IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length);
-
- switch (ext->alg) {
- case IW_ENCODE_ALG_NONE:
- remove = 1;
- break;
- case IW_ENCODE_ALG_WEP:
- if (ext->key_len == WLAN_KEY_LEN_WEP40)
- alg = UMAC_CIPHER_TYPE_WEP_40;
- else if (ext->key_len == WLAN_KEY_LEN_WEP104)
- alg = UMAC_CIPHER_TYPE_WEP_104;
- else {
- IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len);
- return -EINVAL;
- }
-
- break;
- case IW_ENCODE_ALG_TKIP:
- alg = UMAC_CIPHER_TYPE_TKIP;
- break;
- case IW_ENCODE_ALG_CCMP:
- alg = UMAC_CIPHER_TYPE_CCMP;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- idx = erq->flags & IW_ENCODE_INDEX;
-
- if (idx == 0) {
- if (iwm->default_key)
- for (i = 0; i < IWM_NUM_KEYS; i++) {
- if (iwm->default_key == &iwm->keys[i]) {
- idx = i;
- break;
- }
- }
- } else if (idx < 1 || idx > 4) {
- return -EINVAL;
- } else
- idx--;
-
- if (erq->flags & IW_ENCODE_DISABLED)
- remove = 1;
- else if ((erq->length == 0) ||
- (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
- iwm->default_key = &iwm->keys[idx];
- if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP)
- return iwm_set_tx_key(iwm, idx);
- }
-
- key = iwm_key_init(iwm, idx, !remove, ext, alg);
-
- return iwm_set_key(iwm, remove, !iwm->default_key, key);
-}
-
static const iw_handler iwm_handlers[] =
{
(iw_handler) NULL, /* SIOCSIWCOMMIT */
@@ -716,8 +456,8 @@ static const iw_handler iwm_handlers[] =
(iw_handler) NULL, /* SIOCGIWTXPOW */
(iw_handler) NULL, /* SIOCSIWRETRY */
(iw_handler) NULL, /* SIOCGIWRETRY */
- (iw_handler) iwm_wext_siwencode, /* SIOCSIWENCODE */
- (iw_handler) iwm_wext_giwencode, /* SIOCGIWENCODE */
+ (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */
+ (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */
(iw_handler) iwm_wext_siwpower, /* SIOCSIWPOWER */
(iw_handler) iwm_wext_giwpower, /* SIOCGIWPOWER */
(iw_handler) NULL, /* -- hole -- */
@@ -726,7 +466,7 @@ static const iw_handler iwm_handlers[] =
(iw_handler) NULL, /* SIOCGIWGENIE */
(iw_handler) iwm_wext_siwauth, /* SIOCSIWAUTH */
(iw_handler) iwm_wext_giwauth, /* SIOCGIWAUTH */
- (iw_handler) iwm_wext_siwencodeext, /* SIOCSIWENCODEEXT */
+ (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */
(iw_handler) NULL, /* SIOCGIWENCODEEXT */
(iw_handler) NULL, /* SIOCSIWPMKSA */
(iw_handler) NULL, /* -- hole -- */
--
1.6.3.1


2009-06-15 17:50:19

by Samuel Ortiz

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

On Mon, Jun 15, 2009 at 05:44:07PM +0200, Johannes Berg wrote:
> On Mon, 2009-06-15 at 17:39 +0200, Samuel Ortiz wrote:
> > 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.

> johannes
>
> > Signed-off-by: Samuel Ortiz <[email protected]>
> > ---
> > 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(-)
> >
> > diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
> > index 635c16e..3cc4e91 100644
> > --- a/drivers/net/wireless/iwmc3200wifi/iwm.h
> > +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
> > @@ -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
> > diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
> > index 6a2640f..09ed247 100644
> > --- a/drivers/net/wireless/iwmc3200wifi/main.c
> > +++ b/drivers/net/wireless/iwmc3200wifi/main.c
> > @@ -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) {
> > diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
> > index 68e2c3b..5b7aa34 100644
> > --- a/drivers/net/wireless/iwmc3200wifi/netdev.c
> > +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
> > @@ -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;
> > diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
> > index b54da67..97e7da3 100644
> > --- a/drivers/net/wireless/iwmc3200wifi/sdio.c
> > +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
> > @@ -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");
> >



--
Intel Open Source Technology Centre
http://oss.intel.com/

2009-06-15 15:38:10

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 01/12][2.6.31 and w-t] 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.

Signed-off-by: Samuel Ortiz <[email protected]>
---
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(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index 635c16e..3cc4e91 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -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
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 6a2640f..09ed247 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -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) {
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index 68e2c3b..5b7aa34 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -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;
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
index b54da67..97e7da3 100644
--- a/drivers/net/wireless/iwmc3200wifi/sdio.c
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -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");

--
1.6.3.1


2009-06-15 15:44:47

by Johannes Berg

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

On Mon, 2009-06-15 at 17:39 +0200, Samuel Ortiz wrote:
> 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

> Signed-off-by: Samuel Ortiz <[email protected]>
> ---
> 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(-)
>
> diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
> index 635c16e..3cc4e91 100644
> --- a/drivers/net/wireless/iwmc3200wifi/iwm.h
> +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
> @@ -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
> diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
> index 6a2640f..09ed247 100644
> --- a/drivers/net/wireless/iwmc3200wifi/main.c
> +++ b/drivers/net/wireless/iwmc3200wifi/main.c
> @@ -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) {
> diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
> index 68e2c3b..5b7aa34 100644
> --- a/drivers/net/wireless/iwmc3200wifi/netdev.c
> +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
> @@ -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;
> diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
> index b54da67..97e7da3 100644
> --- a/drivers/net/wireless/iwmc3200wifi/sdio.c
> +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
> @@ -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");
>


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2009-06-15 15:39:09

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 12/12][w-t] 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.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
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(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index 71efbc1..e58026f 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -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)
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index a08c6e9..f187f34 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -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)));
};
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index 48e626f..5064a31 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -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;
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 49a8be7..55871da 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -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;
}
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
index 462bbf6..85aa2b1 100644
--- a/drivers/net/wireless/iwmc3200wifi/sdio.c
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -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)
diff --git a/drivers/net/wireless/iwmc3200wifi/wext.c b/drivers/net/wireless/iwmc3200wifi/wext.c
index 3062f37..9734573 100644
--- a/drivers/net/wireless/iwmc3200wifi/wext.c
+++ b/drivers/net/wireless/iwmc3200wifi/wext.c
@@ -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 */
--
1.6.3.1


2009-06-15 15:38:21

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 03/12][2.6.31 and w-t] 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.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
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(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index a0e7377..73c1011 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -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);
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index de16b2b..d1797b9 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -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));
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
index 97e7da3..057a588 100644
--- a/drivers/net/wireless/iwmc3200wifi/sdio.c
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -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);

--
1.6.3.1


2009-06-15 15:39:10

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 10/12][w-t] 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.

Signed-off-by: Samuel Ortiz <[email protected]>
---
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(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index ad62b20..71efbc1 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -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);
}

diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 3ac5a16..bbc3c6b 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -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;

diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index d1797b9..48e626f 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -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)
--
1.6.3.1