2009-09-01 13:12:41

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 0/9] iwmc3200wifi updates

Hi John,

This 9 patches serie is the latest iwmc3200wifi update.
All of them are targeted for a wireless-testing inclusion, hoping it's not too
late to make it to the next merge window.

Samuel Ortiz (5):
iwmc3200wifi: Set WEP key from connect
iwmc3200wifi: Fix sparse warning
iwmc3200wifi: New initial LMAC calibration
iwmc3200wifi: Handle UMAC stalls and UMAC assert properly
iwmc3200wifi: Add a last_fw_err debugfs entry

Zhu Yi (4):
iwmc3200wifi: invalidate profile when necessary before connect
iwmc3200wifi: use cfg80211_roamed to send roam event
iwmc3200wifi: add disconnect work
iwmc3200wifi: fix misuse of le16_to_cpu

drivers/net/wireless/iwmc3200wifi/cfg80211.c | 66 ++++++++++++++--
drivers/net/wireless/iwmc3200wifi/commands.c | 1 +
drivers/net/wireless/iwmc3200wifi/debug.h | 2 +
drivers/net/wireless/iwmc3200wifi/debugfs.c | 105 +++++++++++++++++++++++++-
drivers/net/wireless/iwmc3200wifi/fw.c | 56 ++++++++++----
drivers/net/wireless/iwmc3200wifi/iwm.h | 8 ++-
drivers/net/wireless/iwmc3200wifi/lmac.h | 15 ++++
drivers/net/wireless/iwmc3200wifi/main.c | 60 +++++++++++++--
drivers/net/wireless/iwmc3200wifi/rx.c | 87 +++++++++++++++++----
9 files changed, 345 insertions(+), 55 deletions(-)



2009-09-01 13:12:36

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 4/9] iwmc3200wifi: use cfg80211_roamed to send roam event

From: Zhu Yi <[email protected]>

The device sends connection terminated and [re]association success
(or failure) events when roaming occours. The patch uses
cfg80211_roamed instead of cfg80211_connect_result to notify SME
for roaming.

Signed-off-by: Zhu Yi <[email protected]>
Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/cfg80211.c | 2 +-
drivers/net/wireless/iwmc3200wifi/commands.c | 1 +
drivers/net/wireless/iwmc3200wifi/iwm.h | 2 +-
drivers/net/wireless/iwmc3200wifi/rx.c | 26 ++++++++++++++++++++------
4 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index 2784b08..a56a2b0 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -673,7 +673,7 @@ static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active);

if (iwm->umac_profile_active)
- return iwm_invalidate_mlme_profile(iwm);
+ iwm_invalidate_mlme_profile(iwm);

return 0;
}
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index a68a2af..23b52fa 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -756,6 +756,7 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm)
return ret;
}

+ set_bit(IWM_STATUS_SME_CONNECTING, &iwm->status);
return 0;
}

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index 7a51bc3..f054cc8 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -175,7 +175,7 @@ struct iwm_key {
#define IWM_STATUS_READY 0
#define IWM_STATUS_SCANNING 1
#define IWM_STATUS_SCAN_ABORTING 2
-#define IWM_STATUS_ASSOCIATING 3
+#define IWM_STATUS_SME_CONNECTING 3
#define IWM_STATUS_ASSOCIATED 4

struct iwm_tx_queue {
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 86079a1..9e6f2cd 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -487,8 +487,6 @@ static int iwm_mlme_assoc_start(struct iwm_priv *iwm, u8 *buf,

start = (struct iwm_umac_notif_assoc_start *)buf;

- set_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
-
IWM_DBG_MLME(iwm, INFO, "Association with %pM Started, reason: %d\n",
start->bssid, le32_to_cpu(start->roam_reason));

@@ -507,14 +505,23 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
IWM_DBG_MLME(iwm, INFO, "Association with %pM completed, status: %d\n",
complete->bssid, complete->status);

- clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
-
switch (le32_to_cpu(complete->status)) {
case UMAC_ASSOC_COMPLETE_SUCCESS:
set_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
memcpy(iwm->bssid, complete->bssid, ETH_ALEN);
iwm->channel = complete->channel;

+ /* Internal roaming state, avoid notifying SME. */
+ if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)
+ && iwm->conf.mode == UMAC_MODE_BSS) {
+ cfg80211_roamed(iwm_to_ndev(iwm),
+ complete->bssid,
+ iwm->req_ie, iwm->req_ie_len,
+ iwm->resp_ie, iwm->resp_ie_len,
+ GFP_KERNEL);
+ break;
+ }
+
iwm_link_on(iwm);

if (iwm->conf.mode == UMAC_MODE_IBSS)
@@ -531,6 +538,11 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
memset(iwm->bssid, 0, ETH_ALEN);
iwm->channel = 0;

+ /* Internal roaming state, avoid notifying SME. */
+ if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)
+ && iwm->conf.mode == UMAC_MODE_BSS)
+ break;
+
iwm_link_off(iwm);

if (iwm->conf.mode == UMAC_MODE_IBSS)
@@ -540,6 +552,7 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
+ break;
default:
break;
}
@@ -562,7 +575,7 @@ static int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf,
IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n",
le32_to_cpu(invalid->reason));

- clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
+ clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status);
clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);

iwm->umac_profile_active = 0;
@@ -813,7 +826,8 @@ static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf,
iwm->resp_ie = kmemdup(mgt->u.reassoc_resp.variable,
iwm->resp_ie_len, GFP_KERNEL);
} else {
- IWM_ERR(iwm, "Unsupported management frame");
+ IWM_ERR(iwm, "Unsupported management frame: 0x%x",
+ cpu_to_le16(mgt->frame_control));
return 0;
}

--
1.6.3.3


2009-09-01 13:12:58

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 6/9] iwmc3200wifi: fix misuse of le16_to_cpu

From: Zhu Yi <[email protected]>

Also mark some functions static.

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

diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index c41fa8c..6cf2f0c 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -128,8 +128,8 @@ static void iwm_disconnect_work(struct work_struct *work)
cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, GFP_KERNEL);
}

-int __iwm_up(struct iwm_priv *iwm);
-int __iwm_down(struct iwm_priv *iwm);
+static int __iwm_up(struct iwm_priv *iwm);
+static int __iwm_down(struct iwm_priv *iwm);

static void iwm_reset_worker(struct work_struct *work)
{
@@ -559,7 +559,7 @@ static int iwm_channels_init(struct iwm_priv *iwm)
return 0;
}

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

-int __iwm_down(struct iwm_priv *iwm)
+static int __iwm_down(struct iwm_priv *iwm)
{
int ret;

diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 4d9793e..2daa586 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -850,7 +850,7 @@ static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf,
iwm->resp_ie_len, GFP_KERNEL);
} else {
IWM_ERR(iwm, "Unsupported management frame: 0x%x",
- cpu_to_le16(mgt->frame_control));
+ le16_to_cpu(mgt->frame_control));
return 0;
}

--
1.6.3.3


2009-09-01 13:12:59

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 8/9] iwmc3200wifi: Handle UMAC stalls and UMAC assert properly

When UMAC stalls or asserts, we want to reset the device. But when we're
associated, the current reset worker will end up calling
cfg80211_connect_result() with the cfg80211 sme layer knowing that we're
reassociating. That ends up with some ugly warnings.
With this patch we're telling the upper layer that we've roamed if
reassociation succeeds, and that we're disconnected if it fails.

Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/iwm.h | 2 ++
drivers/net/wireless/iwmc3200wifi/main.c | 18 +++++++++++++++---
drivers/net/wireless/iwmc3200wifi/rx.c | 29 +++++++++++++++++++++++------
3 files changed, 40 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index 74964ee..f5c2d6f 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -178,6 +178,7 @@ struct iwm_key {
#define IWM_STATUS_SCAN_ABORTING 2
#define IWM_STATUS_SME_CONNECTING 3
#define IWM_STATUS_ASSOCIATED 4
+#define IWM_STATUS_RESETTING 5

struct iwm_tx_queue {
int id;
@@ -317,6 +318,7 @@ 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_resetting(struct iwm_priv *iwm);
void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
struct iwm_umac_notif_alive *alive);
int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb);
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index fc7fce4..6a5b76a 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -187,7 +187,8 @@ static void iwm_reset_worker(struct work_struct *work)
memcpy(iwm->umac_profile, profile, sizeof(*profile));
iwm_send_mlme_profile(iwm);
kfree(profile);
- }
+ } else
+ clear_bit(IWM_STATUS_RESETTING, &iwm->status);

out:
mutex_unlock(&iwm->mutex);
@@ -200,7 +201,7 @@ static void iwm_watchdog(unsigned long data)
IWM_WARN(iwm, "Watchdog expired: UMAC stalls!\n");

if (modparam_reset)
- schedule_work(&iwm->reset_worker);
+ iwm_resetting(iwm);
}

int iwm_priv_init(struct iwm_priv *iwm)
@@ -284,7 +285,11 @@ void iwm_reset(struct iwm_priv *iwm)
if (test_bit(IWM_STATUS_READY, &iwm->status))
iwm_target_reset(iwm);

- iwm->status = 0;
+ if (test_bit(IWM_STATUS_RESETTING, &iwm->status)) {
+ iwm->status = 0;
+ set_bit(IWM_STATUS_RESETTING, &iwm->status);
+ } else
+ iwm->status = 0;
iwm->scan_id = 1;

list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
@@ -300,6 +305,13 @@ void iwm_reset(struct iwm_priv *iwm)
iwm_link_off(iwm);
}

+void iwm_resetting(struct iwm_priv *iwm)
+{
+ set_bit(IWM_STATUS_RESETTING, &iwm->status);
+
+ schedule_work(&iwm->reset_worker);
+}
+
/*
* Notification code:
*
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 2daa586..14950b1 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -118,6 +118,8 @@ static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
IWM_ERR(iwm, "\tLMAC status: 0x%x\n", le32_to_cpu(fw_err->lmac_status));
IWM_ERR(iwm, "\tSDIO status: 0x%x\n", le32_to_cpu(fw_err->sdio_status));

+ iwm_resetting(iwm);
+
return 0;
}

@@ -528,11 +530,19 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
if (iwm->conf.mode == UMAC_MODE_IBSS)
goto ibss;

- cfg80211_connect_result(iwm_to_ndev(iwm),
+ if (!test_bit(IWM_STATUS_RESETTING, &iwm->status))
+ cfg80211_connect_result(iwm_to_ndev(iwm),
+ complete->bssid,
+ iwm->req_ie, iwm->req_ie_len,
+ iwm->resp_ie, iwm->resp_ie_len,
+ WLAN_STATUS_SUCCESS,
+ GFP_KERNEL);
+ else
+ cfg80211_roamed(iwm_to_ndev(iwm),
complete->bssid,
iwm->req_ie, iwm->req_ie_len,
iwm->resp_ie, iwm->resp_ie_len,
- WLAN_STATUS_SUCCESS, GFP_KERNEL);
+ GFP_KERNEL);
break;
case UMAC_ASSOC_COMPLETE_FAILURE:
clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
@@ -551,19 +561,26 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
if (iwm->conf.mode == UMAC_MODE_IBSS)
goto ibss;

- cfg80211_connect_result(iwm_to_ndev(iwm), complete->bssid,
- NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
+ if (!test_bit(IWM_STATUS_RESETTING, &iwm->status))
+ cfg80211_connect_result(iwm_to_ndev(iwm),
+ complete->bssid,
+ NULL, 0, NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ else
+ cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0,
+ GFP_KERNEL);
break;
default:
break;
}

+ clear_bit(IWM_STATUS_RESETTING, &iwm->status);
return 0;

ibss:
cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL);
+ clear_bit(IWM_STATUS_RESETTING, &iwm->status);
return 0;
}

--
1.6.3.3


2009-09-01 13:12:54

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 7/9] iwmc3200wifi: New initial LMAC calibration

The LMAC calibration API got broken mostly by having a configuration bitmap
being different than the result one.
This patch tries to address that issue by correctly running calibrations with
the newest firmwares, and keeping a backward compatibility fallback path for
older firmwares, where the configuration and result bitmaps were identical.

Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/fw.c | 56 +++++++++++++++++++++---------
drivers/net/wireless/iwmc3200wifi/iwm.h | 1 +
drivers/net/wireless/iwmc3200wifi/lmac.h | 15 ++++++++
drivers/net/wireless/iwmc3200wifi/main.c | 7 +++-
4 files changed, 61 insertions(+), 18 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c
index 0f32cab..6b0bcad 100644
--- a/drivers/net/wireless/iwmc3200wifi/fw.c
+++ b/drivers/net/wireless/iwmc3200wifi/fw.c
@@ -261,6 +261,33 @@ static int iwm_load_lmac(struct iwm_priv *iwm, const char *img_name)
cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_CLK_EN), 0);
}

+static int iwm_init_calib(struct iwm_priv *iwm, unsigned long cfg_bitmap,
+ unsigned long expected_bitmap, u8 rx_iq_cmd)
+{
+ /* Read RX IQ calibration result from EEPROM */
+ if (test_bit(rx_iq_cmd, &cfg_bitmap)) {
+ iwm_store_rxiq_calib_result(iwm);
+ set_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->calib_done_map);
+ }
+
+ iwm_send_prio_table(iwm);
+ iwm_send_init_calib_cfg(iwm, cfg_bitmap);
+
+ while (iwm->calib_done_map != expected_bitmap) {
+ if (iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION,
+ IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT)) {
+ IWM_DBG_FW(iwm, DBG, "Initial calibration timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: "
+ "0x%lx, expected calibrations: 0x%lx\n",
+ iwm->calib_done_map, expected_bitmap);
+ }
+
+ return 0;
+}
+
/*
* We currently have to load 3 FWs:
* 1) The UMAC (Upper MAC).
@@ -276,6 +303,7 @@ static int iwm_load_lmac(struct iwm_priv *iwm, const char *img_name)
int iwm_load_fw(struct iwm_priv *iwm)
{
unsigned long init_calib_map, periodic_calib_map;
+ unsigned long expected_calib_map;
int ret;

/* We first start downloading the UMAC */
@@ -317,27 +345,21 @@ int iwm_load_fw(struct iwm_priv *iwm)
}

init_calib_map = iwm->conf.calib_map & IWM_CALIB_MAP_INIT_MSK;
+ expected_calib_map = iwm->conf.expected_calib_map &
+ IWM_CALIB_MAP_INIT_MSK;
periodic_calib_map = IWM_CALIB_MAP_PER_LMAC(iwm->conf.calib_map);

- /* Read RX IQ calibration result from EEPROM */
- if (test_bit(PHY_CALIBRATE_RX_IQ_CMD, &init_calib_map)) {
- iwm_store_rxiq_calib_result(iwm);
- set_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->calib_done_map);
- }
-
- iwm_send_prio_table(iwm);
- iwm_send_init_calib_cfg(iwm, init_calib_map);
-
- while (iwm->calib_done_map != init_calib_map) {
- ret = iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION,
- IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
- if (ret) {
- IWM_ERR(iwm, "Wait for calibration result timeout\n");
+ ret = iwm_init_calib(iwm, init_calib_map, expected_calib_map,
+ CALIB_CFG_RX_IQ_IDX);
+ if (ret < 0) {
+ /* Let's try the old way */
+ ret = iwm_init_calib(iwm, expected_calib_map,
+ expected_calib_map,
+ PHY_CALIBRATE_RX_IQ_CMD);
+ if (ret < 0) {
+ IWM_ERR(iwm, "Calibration result timeout\n");
goto out;
}
- IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: "
- "0x%lx, requested calibrations: 0x%lx\n",
- iwm->calib_done_map, init_calib_map);
}

/* Handle LMAC CALIBRATION_COMPLETE notification */
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index da49df6..74964ee 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -64,6 +64,7 @@
struct iwm_conf {
u32 sdio_ior_timeout;
unsigned long calib_map;
+ unsigned long expected_calib_map;
bool reset_on_fatal_err;
bool auto_connect;
bool wimax_not_present;
diff --git a/drivers/net/wireless/iwmc3200wifi/lmac.h b/drivers/net/wireless/iwmc3200wifi/lmac.h
index 19213e1..6c1a14c 100644
--- a/drivers/net/wireless/iwmc3200wifi/lmac.h
+++ b/drivers/net/wireless/iwmc3200wifi/lmac.h
@@ -396,9 +396,24 @@ enum {
CALIBRATION_CMD_NUM,
};

+enum {
+ CALIB_CFG_RX_BB_IDX = 0,
+ CALIB_CFG_DC_IDX = 1,
+ CALIB_CFG_LO_IDX = 2,
+ CALIB_CFG_TX_IQ_IDX = 3,
+ CALIB_CFG_RX_IQ_IDX = 4,
+ CALIB_CFG_NOISE_IDX = 5,
+ CALIB_CFG_CRYSTAL_IDX = 6,
+ CALIB_CFG_TEMPERATURE_IDX = 7,
+ CALIB_CFG_PAPD_IDX = 8,
+ CALIB_CFG_LAST_IDX = CALIB_CFG_PAPD_IDX,
+ CALIB_CFG_MODULE_NUM,
+};
+
#define IWM_CALIB_MAP_INIT_MSK 0xFFFF
#define IWM_CALIB_MAP_PER_LMAC(m) ((m & 0xFF0000) >> 16)
#define IWM_CALIB_MAP_PER_UMAC(m) ((m & 0xFF000000) >> 24)
+#define IWM_CALIB_OPCODE_TO_INDEX(op) (op - PHY_CALIBRATE_OPCODES_NUM)

struct iwm_lmac_calib_hdr {
u8 opcode;
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 6cf2f0c..fc7fce4 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -53,7 +53,12 @@
static struct iwm_conf def_iwm_conf = {

.sdio_ior_timeout = 5000,
- .calib_map = BIT(PHY_CALIBRATE_DC_CMD) |
+ .calib_map = BIT(CALIB_CFG_DC_IDX) |
+ BIT(CALIB_CFG_LO_IDX) |
+ BIT(CALIB_CFG_TX_IQ_IDX) |
+ BIT(CALIB_CFG_RX_IQ_IDX) |
+ BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD),
+ .expected_calib_map = BIT(PHY_CALIBRATE_DC_CMD) |
BIT(PHY_CALIBRATE_LO_CMD) |
BIT(PHY_CALIBRATE_TX_IQ_CMD) |
BIT(PHY_CALIBRATE_RX_IQ_CMD) |
--
1.6.3.3


2009-09-01 13:12:30

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 3/9] iwmc3200wifi: Fix sparse warning

iwm_cfg80211_get_station() should be static.

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

diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index d8bb723..2784b08 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -238,8 +238,9 @@ static int iwm_cfg80211_set_default_key(struct wiphy *wiphy,
return iwm_set_tx_key(iwm, key_index);
}

-int iwm_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
- u8 *mac, struct station_info *sinfo)
+static int iwm_cfg80211_get_station(struct wiphy *wiphy,
+ struct net_device *ndev,
+ u8 *mac, struct station_info *sinfo)
{
struct iwm_priv *iwm = ndev_to_iwm(ndev);

--
1.6.3.3


2009-09-01 13:12:41

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 1/9] iwmc3200wifi: invalidate profile when necessary before connect

From: Zhu Yi <[email protected]>

If cfg80211 requests to connect when we have already had an active
profile, invalidate the current profile first before sending a new
profile to UMAC.

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

diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index a6e852f..789ef5c 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -326,11 +326,8 @@ static int iwm_cfg80211_change_iface(struct wiphy *wiphy,

iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);

- if (iwm->umac_profile_active) {
- int ret = iwm_invalidate_mlme_profile(iwm);
- if (ret < 0)
- IWM_ERR(iwm, "Couldn't invalidate profile\n");
- }
+ if (iwm->umac_profile_active)
+ iwm_invalidate_mlme_profile(iwm);

return 0;
}
@@ -573,6 +570,14 @@ static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
if (!sme->ssid)
return -EINVAL;

+ if (iwm->umac_profile_active) {
+ ret = iwm_invalidate_mlme_profile(iwm);
+ if (ret) {
+ IWM_ERR(iwm, "Couldn't invalidate profile\n");
+ return ret;
+ }
+ }
+
if (chan)
iwm->channel =
ieee80211_frequency_to_channel(chan->center_freq);
--
1.6.3.3


2009-09-01 13:12:26

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 2/9] iwmc3200wifi: Set WEP key from connect

When connect is called with the LEGACY_PSK authentication type set, and a
proper sme->key, we need to set the WEP key straight after setting the
profile otherwise the authentication will never start.

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

diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index 789ef5c..d8bb723 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -562,6 +562,7 @@ static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
struct ieee80211_channel *chan = sme->channel;
+ struct key_params key_param;
int ret;

if (!test_bit(IWM_STATUS_READY, &iwm->status))
@@ -619,7 +620,48 @@ static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
return ret;
}

- return iwm_send_mlme_profile(iwm);
+ /*
+ * We save the WEP key in case we want to do shared authentication.
+ * We have to do it so because UMAC will assert whenever it gets a
+ * key before a profile.
+ */
+ if (sme->key) {
+ key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL);
+ if (key_param.key == NULL)
+ return -ENOMEM;
+ key_param.key_len = sme->key_len;
+ key_param.seq_len = 0;
+ key_param.cipher = sme->crypto.ciphers_pairwise[0];
+
+ ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx,
+ NULL, &key_param);
+ kfree(key_param.key);
+ if (ret < 0) {
+ IWM_ERR(iwm, "Invalid key_params\n");
+ return ret;
+ }
+
+ iwm->default_key = sme->key_idx;
+ }
+
+ ret = iwm_send_mlme_profile(iwm);
+
+ if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
+ sme->key == NULL)
+ return ret;
+
+ /*
+ * We want to do shared auth.
+ * We need to actually set the key we previously cached,
+ * and then tell the UMAC it's the default one.
+ * That will trigger the auth+assoc UMAC machinery, and again,
+ * this must be done after setting the profile.
+ */
+ ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]);
+ if (ret < 0)
+ return ret;
+
+ return iwm_set_tx_key(iwm, iwm->default_key);
}

static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
--
1.6.3.3


2009-09-01 13:12:41

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 5/9] iwmc3200wifi: add disconnect work

From: Zhu Yi <[email protected]>

When the driver receives "connection terminated" event from device,
it could be caused by 2 reasons: the firmware is roaming or the
connection is lost (AP disappears). For the former, an association
complete event is supposed to come within 3 seconds. For the latter,
the driver won't receive any event except the connection terminated.
So we kick a delayed work (5*HZ) when we receive the connection
terminated event. It will be canceled if it turns out to be a roaming
event later. Otherwise we notify SME and userspace the disconnection.

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 | 21 +++++++++++++++++++
drivers/net/wireless/iwmc3200wifi/rx.c | 32 +++++++++++++++++++++++++----
3 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index f054cc8..da49df6 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -273,6 +273,7 @@ struct iwm_priv {

struct iw_statistics wstats;
struct delayed_work stats_request;
+ struct delayed_work disconnect;

struct iwm_debugfs dbg;

diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index cf25744..c41fa8c 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -108,6 +108,26 @@ static void iwm_statistics_request(struct work_struct *work)
iwm_send_umac_stats_req(iwm, 0);
}

+static void iwm_disconnect_work(struct work_struct *work)
+{
+ struct iwm_priv *iwm =
+ container_of(work, struct iwm_priv, disconnect.work);
+
+ if (iwm->umac_profile_active)
+ iwm_invalidate_mlme_profile(iwm);
+
+ clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
+ iwm->umac_profile_active = 0;
+ memset(iwm->bssid, 0, ETH_ALEN);
+ iwm->channel = 0;
+
+ iwm_link_off(iwm);
+
+ wake_up_interruptible(&iwm->mlme_queue);
+
+ cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, GFP_KERNEL);
+}
+
int __iwm_up(struct iwm_priv *iwm);
int __iwm_down(struct iwm_priv *iwm);

@@ -198,6 +218,7 @@ int iwm_priv_init(struct iwm_priv *iwm)
spin_lock_init(&iwm->cmd_lock);
iwm->scan_id = 1;
INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request);
+ INIT_DELAYED_WORK(&iwm->disconnect, iwm_disconnect_work);
INIT_WORK(&iwm->reset_worker, iwm_reset_worker);
INIT_LIST_HEAD(&iwm->bss_list);

diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 9e6f2cd..4d9793e 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -514,6 +514,7 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
/* Internal roaming state, avoid notifying SME. */
if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)
&& iwm->conf.mode == UMAC_MODE_BSS) {
+ cancel_delayed_work(&iwm->disconnect);
cfg80211_roamed(iwm_to_ndev(iwm),
complete->bssid,
iwm->req_ie, iwm->req_ie_len,
@@ -540,8 +541,10 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,

/* Internal roaming state, avoid notifying SME. */
if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)
- && iwm->conf.mode == UMAC_MODE_BSS)
+ && iwm->conf.mode == UMAC_MODE_BSS) {
+ cancel_delayed_work(&iwm->disconnect);
break;
+ }

iwm_link_off(iwm);

@@ -569,11 +572,18 @@ static int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf,
struct iwm_wifi_cmd *cmd)
{
struct iwm_umac_notif_profile_invalidate *invalid;
+ u32 reason;

invalid = (struct iwm_umac_notif_profile_invalidate *)buf;
+ reason = le32_to_cpu(invalid->reason);
+
+ IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n", reason);

- IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n",
- le32_to_cpu(invalid->reason));
+ if (reason != UMAC_PROFILE_INVALID_REQUEST &&
+ test_bit(IWM_STATUS_SME_CONNECTING, &iwm->status))
+ cfg80211_connect_result(iwm_to_ndev(iwm), NULL, NULL, 0, NULL,
+ 0, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);

clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status);
clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
@@ -589,6 +599,19 @@ static int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf,
return 0;
}

+#define IWM_DISCONNECT_INTERVAL (5 * HZ)
+
+static int iwm_mlme_connection_terminated(struct iwm_priv *iwm, u8 *buf,
+ unsigned long buf_size,
+ struct iwm_wifi_cmd *cmd)
+{
+ IWM_DBG_MLME(iwm, DBG, "Connection terminated\n");
+
+ schedule_delayed_work(&iwm->disconnect, IWM_DISCONNECT_INTERVAL);
+
+ return 0;
+}
+
static int iwm_mlme_scan_complete(struct iwm_priv *iwm, u8 *buf,
unsigned long buf_size,
struct iwm_wifi_cmd *cmd)
@@ -848,8 +871,7 @@ static int iwm_ntf_mlme(struct iwm_priv *iwm, u8 *buf,
case WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE:
return iwm_mlme_profile_invalidate(iwm, buf, buf_size, cmd);
case WIFI_IF_NTFY_CONNECTION_TERMINATED:
- IWM_DBG_MLME(iwm, DBG, "Connection terminated\n");
- break;
+ return iwm_mlme_connection_terminated(iwm, buf, buf_size, cmd);
case WIFI_IF_NTFY_SCAN_COMPLETE:
return iwm_mlme_scan_complete(iwm, buf, buf_size, cmd);
case WIFI_IF_NTFY_STA_TABLE_CHANGE:
--
1.6.3.3


2009-09-01 13:13:04

by Samuel Ortiz

[permalink] [raw]
Subject: [PATCH 9/9] iwmc3200wifi: Add a last_fw_err debugfs entry

In order to check what was the last fw error we got accross resets, we add
this debugfs entry. It displays the complete ASSERT information.

Signed-off-by: Samuel Ortiz <[email protected]>
---
drivers/net/wireless/iwmc3200wifi/debug.h | 2 +
drivers/net/wireless/iwmc3200wifi/debugfs.c | 105 ++++++++++++++++++++++++++-
drivers/net/wireless/iwmc3200wifi/iwm.h | 2 +
drivers/net/wireless/iwmc3200wifi/main.c | 6 ++
drivers/net/wireless/iwmc3200wifi/rx.c | 2 +
5 files changed, 113 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/iwmc3200wifi/debug.h b/drivers/net/wireless/iwmc3200wifi/debug.h
index 8fbb42d..e35c9b6 100644
--- a/drivers/net/wireless/iwmc3200wifi/debug.h
+++ b/drivers/net/wireless/iwmc3200wifi/debug.h
@@ -108,6 +108,8 @@ struct iwm_debugfs {
struct dentry *txq_dentry;
struct dentry *tx_credit_dentry;
struct dentry *rx_ticket_dentry;
+
+ struct dentry *fw_err_dentry;
};

#ifdef CONFIG_IWM_DEBUG
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c
index 0fa7b91..1465379 100644
--- a/drivers/net/wireless/iwmc3200wifi/debugfs.c
+++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c
@@ -98,7 +98,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules,
iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write,
"%llu\n");

-static int iwm_txrx_open(struct inode *inode, struct file *filp)
+static int iwm_generic_open(struct inode *inode, struct file *filp)
{
filp->private_data = inode->i_private;
return 0;
@@ -289,25 +289,111 @@ static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp,
return ret;
}

+static ssize_t iwm_debugfs_fw_err_read(struct file *filp,
+ char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+
+ struct iwm_priv *iwm = filp->private_data;
+ char buf[512];
+ int buf_len = 512;
+ size_t len = 0;
+
+ if (*ppos != 0)
+ return 0;
+ if (count < sizeof(buf))
+ return -ENOSPC;
+
+ if (!iwm->last_fw_err)
+ return -ENOMEM;
+
+ if (iwm->last_fw_err->line_num == 0)
+ goto out;
+
+ len += snprintf(buf + len, buf_len - len, "%cMAC FW ERROR:\n",
+ (le32_to_cpu(iwm->last_fw_err->category) == UMAC_SYS_ERR_CAT_LMAC)
+ ? 'L' : 'U');
+ len += snprintf(buf + len, buf_len - len,
+ "\tCategory: %d\n",
+ le32_to_cpu(iwm->last_fw_err->category));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tStatus: 0x%x\n",
+ le32_to_cpu(iwm->last_fw_err->status));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tPC: 0x%x\n",
+ le32_to_cpu(iwm->last_fw_err->pc));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tblink1: %d\n",
+ le32_to_cpu(iwm->last_fw_err->blink1));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tblink2: %d\n",
+ le32_to_cpu(iwm->last_fw_err->blink2));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tilink1: %d\n",
+ le32_to_cpu(iwm->last_fw_err->ilink1));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tilink2: %d\n",
+ le32_to_cpu(iwm->last_fw_err->ilink2));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tData1: 0x%x\n",
+ le32_to_cpu(iwm->last_fw_err->data1));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tData2: 0x%x\n",
+ le32_to_cpu(iwm->last_fw_err->data2));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tLine number: %d\n",
+ le32_to_cpu(iwm->last_fw_err->line_num));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tUMAC status: 0x%x\n",
+ le32_to_cpu(iwm->last_fw_err->umac_status));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tLMAC status: 0x%x\n",
+ le32_to_cpu(iwm->last_fw_err->lmac_status));
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tSDIO status: 0x%x\n",
+ le32_to_cpu(iwm->last_fw_err->sdio_status));
+
+out:
+
+ return simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
+}

static const struct file_operations iwm_debugfs_txq_fops = {
.owner = THIS_MODULE,
- .open = iwm_txrx_open,
+ .open = iwm_generic_open,
.read = iwm_debugfs_txq_read,
};

static const struct file_operations iwm_debugfs_tx_credit_fops = {
.owner = THIS_MODULE,
- .open = iwm_txrx_open,
+ .open = iwm_generic_open,
.read = iwm_debugfs_tx_credit_read,
};

static const struct file_operations iwm_debugfs_rx_ticket_fops = {
.owner = THIS_MODULE,
- .open = iwm_txrx_open,
+ .open = iwm_generic_open,
.read = iwm_debugfs_rx_ticket_read,
};

+static const struct file_operations iwm_debugfs_fw_err_fops = {
+ .owner = THIS_MODULE,
+ .open = iwm_generic_open,
+ .read = iwm_debugfs_fw_err_read,
+};
+
int iwm_debugfs_init(struct iwm_priv *iwm)
{
int i, result;
@@ -423,6 +509,16 @@ int iwm_debugfs_init(struct iwm_priv *iwm)
goto error;
}

+ iwm->dbg.fw_err_dentry = debugfs_create_file("last_fw_err", 0200,
+ iwm->dbg.dbgdir, iwm,
+ &iwm_debugfs_fw_err_fops);
+ result = PTR_ERR(iwm->dbg.fw_err_dentry);
+ if (IS_ERR(iwm->dbg.fw_err_dentry) && (result != -ENODEV)) {
+ IWM_ERR(iwm, "Couldn't create last FW err: %d\n", result);
+ goto error;
+ }
+
+
return 0;

error:
@@ -441,6 +537,7 @@ void iwm_debugfs_exit(struct iwm_priv *iwm)
debugfs_remove(iwm->dbg.txq_dentry);
debugfs_remove(iwm->dbg.tx_credit_dentry);
debugfs_remove(iwm->dbg.rx_ticket_dentry);
+ debugfs_remove(iwm->dbg.fw_err_dentry);
if (iwm->bus_ops->debugfs_exit)
iwm->bus_ops->debugfs_exit(iwm);

diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index f5c2d6f..1b02a4e 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -289,6 +289,8 @@ struct iwm_priv {
u8 *resp_ie;
int resp_ie_len;

+ struct iwm_fw_error_hdr *last_fw_err;
+
char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
};

diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 6a5b76a..d668e47 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -260,6 +260,11 @@ int iwm_priv_init(struct iwm_priv *iwm)
iwm->watchdog.data = (unsigned long)iwm;
mutex_init(&iwm->mutex);

+ iwm->last_fw_err = kzalloc(sizeof(struct iwm_fw_error_hdr),
+ GFP_KERNEL);
+ if (iwm->last_fw_err == NULL)
+ return -ENOMEM;
+
return 0;
}

@@ -271,6 +276,7 @@ void iwm_priv_deinit(struct iwm_priv *iwm)
destroy_workqueue(iwm->txq[i].wq);

destroy_workqueue(iwm->rx_wq);
+ kfree(iwm->last_fw_err);
}

/*
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 14950b1..40dbcbc 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -102,6 +102,8 @@ static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
error = (struct iwm_umac_notif_error *)buf;
fw_err = &error->err;

+ memcpy(iwm->last_fw_err, fw_err, sizeof(struct iwm_fw_error_hdr));
+
IWM_ERR(iwm, "%cMAC FW ERROR:\n",
(le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U');
IWM_ERR(iwm, "\tCategory: %d\n", le32_to_cpu(fw_err->category));
--
1.6.3.3