aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/iwmc3200wifi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi')
-rw-r--r--drivers/net/wireless/iwmc3200wifi/cfg80211.c66
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.c1
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debug.h2
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debugfs.c105
-rw-r--r--drivers/net/wireless/iwmc3200wifi/fw.c56
-rw-r--r--drivers/net/wireless/iwmc3200wifi/iwm.h8
-rw-r--r--drivers/net/wireless/iwmc3200wifi/lmac.h15
-rw-r--r--drivers/net/wireless/iwmc3200wifi/main.c60
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rx.c87
9 files changed, 345 insertions, 55 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index a6e852f4f92..a56a2b0ac99 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);
@@ -326,11 +327,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;
}
@@ -565,6 +563,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))
@@ -573,6 +572,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);
@@ -614,7 +621,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,
@@ -625,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 a68a2aff3c1..23b52fa2605 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/debug.h b/drivers/net/wireless/iwmc3200wifi/debug.h
index 8fbb42d9c21..e35c9b693d1 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 0fa7b9150d5..1465379f900 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/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c
index 0f32cab9ced..6b0bcad758c 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 7a51bc340fd..1b02a4e2a1a 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;
@@ -175,8 +176,9 @@ 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
+#define IWM_STATUS_RESETTING 5
struct iwm_tx_queue {
int id;
@@ -273,6 +275,7 @@ struct iwm_priv {
struct iw_statistics wstats;
struct delayed_work stats_request;
+ struct delayed_work disconnect;
struct iwm_debugfs dbg;
@@ -286,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)));
};
@@ -315,6 +320,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/lmac.h b/drivers/net/wireless/iwmc3200wifi/lmac.h
index 19213e165f5..6c1a14c4480 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 cf2574442b5..d668e475632 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) |
@@ -108,8 +113,28 @@ 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_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);
+}
+
+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)
{
@@ -162,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);
@@ -175,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)
@@ -198,6 +224,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);
@@ -233,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;
}
@@ -244,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);
}
/*
@@ -258,7 +291,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) {
@@ -274,6 +311,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:
*
@@ -538,7 +582,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;
@@ -672,7 +716,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 86079a187ee..40dbcbc1659 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));
@@ -118,6 +120,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;
}
@@ -487,8 +491,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,47 +509,80 @@ 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) {
+ cancel_delayed_work(&iwm->disconnect);
+ 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)
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);
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) {
+ cancel_delayed_work(&iwm->disconnect);
+ break;
+ }
+
iwm_link_off(iwm);
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;
}
@@ -556,13 +591,20 @@ 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",
- le32_to_cpu(invalid->reason));
+ IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n", reason);
- clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
+ 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);
iwm->umac_profile_active = 0;
@@ -576,6 +618,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)
@@ -813,7 +868,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",
+ le16_to_cpu(mgt->frame_control));
return 0;
}
@@ -834,8 +890,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: