From 4fb4e0bee12d66a175e13ed5d956e61398c34e4e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 21 Oct 2012 12:52:04 +0200 Subject: [PATCH 001/478] drivers/net/wireless/ti/wlcore/main.c: eliminate possible double power off The function wl12xx_set_power_on is only called twice, once in wl12xx_chip_wakeup and once in wl12xx_get_hw_info. On the failure of the call in wl12xx_chip_wakeup, the containing function just returns, but on the failure of the call in wl12xx_get_hw_info, the containing function calls wl1271_power_off. This does not seem necessary, because if wl12xx_set_power_on has set the power on and then fails, it has already turned the power off. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r@ identifier f,free,a; parameter list[n] ps; type T; expression e; @@ f(ps,T a,...) { ... when any when != a = e if(...) { ... free(a); ... return ...; } ... when any } @@ identifier r.f,r.free; expression x,a; expression list[r.n] xs; @@ * x = f(xs,a,...); if (...) { ... free(a); ... return ...; } // Signed-off-by: Julia Lawall Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 25530c8760cb..0eb739bc86f5 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5116,7 +5116,7 @@ static int wl12xx_get_hw_info(struct wl1271 *wl) ret = wl12xx_set_power_on(wl); if (ret < 0) - goto out; + return ret; ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id); if (ret < 0) From ca6dc10343ba6811dc2e1b31dafd54bb5fff6437 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 23 Oct 2012 13:22:48 +0800 Subject: [PATCH 002/478] wlcore: spi: use platform_device_unregister in wl1271_remove() platform_device_unregister() only calls platform_device_del() and platform_device_put(), thus use platform_device_unregister() to simplify the code. Also the documents in platform.c shows that platform_device_del and platform_device_put must _only_ be externally called in error cases. All other usage is a bug. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index a519bc3adec1..76838a4a74ce 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -407,8 +407,7 @@ static int __devexit wl1271_remove(struct spi_device *spi) { struct wl12xx_spi_glue *glue = spi_get_drvdata(spi); - platform_device_del(glue->core); - platform_device_put(glue->core); + platform_device_unregister(glue->core); kfree(glue); return 0; From 200e932649dc11e4d69545a6b513d012a6492d0d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 23 Oct 2012 13:23:13 +0800 Subject: [PATCH 003/478] wlcore: sdio: use platform_device_unregister in wl1271_remove() platform_device_unregister() only calls platform_device_del() and platform_device_put(), thus use platform_device_unregister() to simplify the code. Also the documents in platform.c shows that platform_device_del and platform_device_put must _only_ be externally called in error cases. All other usage is a bug. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/sdio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 73ace4b2604e..4960bcd0df65 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -326,8 +326,7 @@ static void __devexit wl1271_remove(struct sdio_func *func) /* Undo decrement done above in wl1271_probe */ pm_runtime_get_noresume(&func->dev); - platform_device_del(glue->core); - platform_device_put(glue->core); + platform_device_unregister(glue->core); kfree(glue); } From 99c227e3d9270840884ded780787693fd0b82dde Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 23 Oct 2012 13:03:01 -0700 Subject: [PATCH 004/478] drivers/net/wireless/ti/wl1251: remove CONFIG_EXPERIMENTAL This config item has not carried much meaning for a while now and is almost always enabled by default. As agreed during the Linux kernel summit, remove it. CC: Luciano Coelho CC: "John W. Linville" Signed-off-by: Kees Cook Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl1251/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wl1251/Kconfig b/drivers/net/wireless/ti/wl1251/Kconfig index 1fb65849414f..8fec4ed36ac2 100644 --- a/drivers/net/wireless/ti/wl1251/Kconfig +++ b/drivers/net/wireless/ti/wl1251/Kconfig @@ -1,6 +1,6 @@ menuconfig WL1251 tristate "TI wl1251 driver support" - depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS + depends on MAC80211 && GENERIC_HARDIRQS select FW_LOADER select CRC7 ---help--- From 4eeac22c159f053ea34527e4fea359ab10b4b5a5 Mon Sep 17 00:00:00 2001 From: "Janusz.Dziedzic@tieto.com" Date: Wed, 7 Nov 2012 15:42:19 +0200 Subject: [PATCH 005/478] wlcore: SPI - fix spi transfer_list In corner case for wl12xx_spi_raw_write() when len == SPI_AGGR_BUFFER_SIZE we don't setup correctly spi transfer_list. Next we will have garbage and strange errors reported by SPI framework (eg. wrong speed_hz, failed to transfer one message from queue) when iterate transfer_list. Signed-off-by: Janusz Dziedzic Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 76838a4a74ce..9d18f2ff57d0 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -270,7 +270,7 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr, void *buf, size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); - struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS]; + struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)]; struct spi_message m; u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; u32 *cmd; From 25b08bf662dea1291a9872547819f0e96bf68fe6 Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Wed, 7 Nov 2012 01:28:14 +0800 Subject: [PATCH 006/478] wlcore: Fix the usage of wait_for_completion_timeout The return value of wait_for_completion_timeout() is always >= 0 with unsigned int type. So the condition "ret < 0" or "ret >= 0" is pointless. Signed-off-by: liu chuansheng Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/ps.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 4d1414a673fb..ffcd84336f04 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -151,9 +151,6 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) wl12xx_queue_recovery_work(wl); ret = -ETIMEDOUT; goto err; - } else if (ret < 0) { - wl1271_error("ELP wakeup completion error."); - goto err; } } From 8f1fd6cb6a036bf2e600f8a3e397681a5e33ab07 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Fri, 16 Nov 2012 12:20:46 +0530 Subject: [PATCH 007/478] wlcore: Remove redundant check on unsigned variable No need to check whether unsigned variable is less than 0. CC: Luciano Coelho CC: linux-wireless@vger.kernel.org CC: netdev@vger.kernel.org Signed-off-by: Tushar Behera Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index c86bb00c2488..93f801dc94e4 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -993,7 +993,7 @@ static ssize_t sleep_auth_write(struct file *file, return -EINVAL; } - if (value < 0 || value > WL1271_PSM_MAX) { + if (value > WL1271_PSM_MAX) { wl1271_warning("sleep_auth must be between 0 and %d", WL1271_PSM_MAX); return -ERANGE; From 3230f35e09f386ee604f55450dcd26098a3c4bc3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:01 +0200 Subject: [PATCH 008/478] wlcore: start sta role on CHANGED_BSSID Make the connection flow simpler by starting sta role on bssid change. Currently, we start dev role when going idle-off, and start the sta role only after association indication. This complicates the connection flow with some possible intermediate states. Make it simpler by starting sta role on bssid change, which now happens *before* auth req get sent. Update the handling of mac80211's notifications and change wl1271_join/unjoin accordingly - * Split wl1271_join() into wlcore_join (tuning on a channel/bssid) and wlcore_set_assoc (configure sta after association). * Rename wl1271_unjoin() to wlcore_unset_assoc(), as it is no longer the inversion of wl1271_join() (now it's only used to disconnect associated sta / joined ibss, without stopping the role). * Set ssid before starting station role (needed for start_role(sta) While on it, split wl1271_bss_info_changed_sta() into some sub-functions. since we no longer use dev role in the connection flow, we now always use the hlid of the sta role. Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 450 +++++++++++++------------- drivers/net/wireless/ti/wlcore/tx.c | 10 +- 2 files changed, 224 insertions(+), 236 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 0eb739bc86f5..63367c00d2fa 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2468,8 +2468,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw, return ret; } -static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, - bool set_assoc) +static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS); @@ -2489,18 +2488,96 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, /* clear encryption type */ wlvif->encryption_type = KEY_NONE; - if (set_assoc) - set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); - if (is_ibss) ret = wl12xx_cmd_role_start_ibss(wl, wlvif); else ret = wl12xx_cmd_role_start_sta(wl, wlvif); - if (ret < 0) - goto out; - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - goto out; + return ret; +} + +static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb, + int offset) +{ + u8 ssid_len; + const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, + skb->len - offset); + + if (!ptr) { + wl1271_error("No SSID in IEs!"); + return -ENOENT; + } + + ssid_len = ptr[1]; + if (ssid_len > IEEE80211_MAX_SSID_LEN) { + wl1271_error("SSID is too long!"); + return -EINVAL; + } + + wlvif->ssid_len = ssid_len; + memcpy(wlvif->ssid, ptr+2, ssid_len); + return 0; +} + +static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + struct sk_buff *skb; + int ieoffset; + + /* we currently only support setting the ssid from the ap probe req */ + if (wlvif->bss_type != BSS_TYPE_STA_BSS) + return -EINVAL; + + skb = ieee80211_ap_probereq_get(wl->hw, vif); + if (!skb) + return -EINVAL; + + ieoffset = offsetof(struct ieee80211_mgmt, + u.probe_req.variable); + wl1271_ssid_set(wlvif, skb, ieoffset); + dev_kfree_skb(skb); + + return 0; +} + +static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct ieee80211_bss_conf *bss_conf) +{ + int ieoffset; + int ret; + + wlvif->aid = bss_conf->aid; + wlvif->channel_type = bss_conf->channel_type; + wlvif->beacon_int = bss_conf->beacon_int; + + set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); + + /* + * with wl1271, we don't need to update the + * beacon_int and dtim_period, because the firmware + * updates it by itself when the first beacon is + * received after a join. + */ + ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid); + if (ret < 0) + return ret; + + /* + * Get a template for hardware connection maintenance + */ + dev_kfree_skb(wlvif->probereq); + wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl, + wlvif, + NULL); + ieoffset = offsetof(struct ieee80211_mgmt, + u.probe_req.variable); + wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset); + + /* enable the connection monitoring feature */ + ret = wl1271_acx_conn_monit_params(wl, wlvif, true); + if (ret < 0) + return ret; /* * The join command disable the keep-alive mode, shut down its process, @@ -2510,29 +2587,58 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, */ ret = wl1271_acx_keep_alive_mode(wl, wlvif, true); if (ret < 0) - goto out; + return ret; ret = wl1271_acx_aid(wl, wlvif, wlvif->aid); if (ret < 0) - goto out; + return ret; ret = wl12xx_cmd_build_klv_null_data(wl, wlvif); if (ret < 0) - goto out; + return ret; ret = wl1271_acx_keep_alive_config(wl, wlvif, wlvif->sta.klv_template_id, ACX_KEEP_ALIVE_TPL_VALID); if (ret < 0) - goto out; + return ret; -out: return ret; } -static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif) +static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; + bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS; + + /* make sure we are connected (sta) joined */ + if (sta && + !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + return false; + + /* make sure we are joined (ibss) */ + if (!sta && + test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) + return false; + + if (sta) { + /* use defaults when not associated */ + wlvif->aid = 0; + + /* free probe-request template */ + dev_kfree_skb(wlvif->probereq); + wlvif->probereq = NULL; + + /* disable connection monitor features */ + ret = wl1271_acx_conn_monit_params(wl, wlvif, false); + if (ret < 0) + return ret; + + /* Disable the keep-alive feature */ + ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); + if (ret < 0) + return ret; + } if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); @@ -2546,17 +2652,11 @@ static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif) wlvif->sta.klv_template_id, ACX_KEEP_ALIVE_TPL_INVALID); - /* to stop listening to a channel, we disconnect */ - ret = wl12xx_cmd_role_stop_sta(wl, wlvif); - if (ret < 0) - goto out; - /* reset TX security counters on a clean disconnect */ wlvif->tx_security_last_seq_lsb = 0; wlvif->tx_security_seq = 0; -out: - return ret; + return 0; } static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif) @@ -2565,45 +2665,6 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif) wlvif->rate_set = wlvif->basic_rate_set; } -static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif, - bool idle) -{ - int ret; - bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); - - if (idle == cur_idle) - return 0; - - if (idle) { - /* no need to croc if we weren't busy (e.g. during boot) */ - if (wl12xx_dev_role_started(wlvif)) { - ret = wl12xx_stop_dev(wl, wlvif); - if (ret < 0) - goto out; - } - wlvif->rate_set = - wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); - ret = wl1271_acx_sta_rate_policies(wl, wlvif); - if (ret < 0) - goto out; - clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); - } else { - /* The current firmware only supports sched_scan in idle */ - if (wl->sched_scanning) { - wl1271_scan_sched_scan_stop(wl, wlvif); - ieee80211_sched_scan_stopped(wl->hw); - } - - ret = wl12xx_start_dev(wl, wlvif); - if (ret < 0) - goto out; - set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); - } - -out: - return ret; -} - static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_conf *conf, u32 changed) { @@ -3418,30 +3479,6 @@ out: return ret; } -static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb, - int offset) -{ - struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - u8 ssid_len; - const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, - skb->len - offset); - - if (!ptr) { - wl1271_error("No SSID in IEs!"); - return -ENOENT; - } - - ssid_len = ptr[1]; - if (ssid_len > IEEE80211_MAX_SSID_LEN) { - wl1271_error("SSID is too long!"); - return -EINVAL; - } - - wlvif->ssid_len = ssid_len; - memcpy(wlvif->ssid, ptr+2, ssid_len); - return 0; -} - static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset) { int len; @@ -3622,7 +3659,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl, wl1271_debug(DEBUG_MASTER, "beacon updated"); - ret = wl1271_ssid_set(vif, beacon, ieoffset); + ret = wl1271_ssid_set(wlvif, beacon, ieoffset); if (ret < 0) { dev_kfree_skb(beacon); goto out; @@ -3804,6 +3841,81 @@ out: return; } +static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct ieee80211_bss_conf *bss_conf, + u32 sta_rate_set) +{ + u32 rates; + int ret; + + wl1271_debug(DEBUG_MAC80211, + "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x", + bss_conf->bssid, bss_conf->aid, + bss_conf->beacon_int, + bss_conf->basic_rates, sta_rate_set); + + wlvif->beacon_int = bss_conf->beacon_int; + rates = bss_conf->basic_rates; + wlvif->basic_rate_set = + wl1271_tx_enabled_rates_get(wl, rates, + wlvif->band); + wlvif->basic_rate = + wl1271_tx_min_rate_get(wl, + wlvif->basic_rate_set); + + if (sta_rate_set) + wlvif->rate_set = + wl1271_tx_enabled_rates_get(wl, + sta_rate_set, + wlvif->band); + + /* we only support sched_scan while not connected */ + if (wl->sched_scanning) { + wl1271_scan_sched_scan_stop(wl, wlvif); + ieee80211_sched_scan_stopped(wl->hw); + } + + ret = wl1271_acx_sta_rate_policies(wl, wlvif); + if (ret < 0) + return ret; + + ret = wl12xx_cmd_build_null_data(wl, wlvif); + if (ret < 0) + return ret; + + ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif)); + if (ret < 0) + return ret; + + wlcore_set_ssid(wl, wlvif); + + set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); + + return 0; +} + +static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + int ret; + + /* revert back to minimum rates for the current band */ + wl1271_set_band_rate(wl, wlvif); + wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); + + ret = wl1271_acx_sta_rate_policies(wl, wlvif); + if (ret < 0) + return ret; + + if (wlvif->bss_type == BSS_TYPE_STA_BSS && + test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) { + ret = wl12xx_cmd_role_stop_sta(wl, wlvif); + if (ret < 0) + return ret; + } + + clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags); + return 0; +} /* STA/IBSS mode changes */ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, struct ieee80211_vif *vif, @@ -3811,7 +3923,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, u32 changed) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - bool do_join = false, set_assoc = false; + bool do_join = false; bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS); bool ibss_joined = false; u32 sta_rate_set = 0; @@ -3832,9 +3944,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags); ibss_joined = true; } else { - if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, - &wlvif->flags)) - wl1271_unjoin(wl, wlvif); + wlcore_unset_assoc(wl, wlvif); + wl12xx_cmd_role_stop_sta(wl, wlvif); } } @@ -3852,12 +3963,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, do_join = true; } - if (changed & BSS_CHANGED_IDLE && !is_ibss) { - ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle); - if (ret < 0) - wl1271_warning("idle mode change failed %d", ret); - } - if ((changed & BSS_CHANGED_CQM)) { bool enable = false; if (bss_conf->cqm_rssi_thold) @@ -3870,18 +3975,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, wlvif->rssi_thold = bss_conf->cqm_rssi_thold; } - if (changed & BSS_CHANGED_BSSID) - if (!is_zero_ether_addr(bss_conf->bssid)) { - ret = wl12xx_cmd_build_null_data(wl, wlvif); - if (ret < 0) - goto out; - - ret = wl1271_build_qos_null_data(wl, vif); - if (ret < 0) - goto out; - } - - if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) { + if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT)) { rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!sta) @@ -3900,119 +3994,19 @@ sta_not_found: rcu_read_unlock(); } - if ((changed & BSS_CHANGED_ASSOC)) { - if (bss_conf->assoc) { - u32 rates; - int ieoffset; - wlvif->aid = bss_conf->aid; - wlvif->channel_type = bss_conf->channel_type; - wlvif->beacon_int = bss_conf->beacon_int; + if (changed & BSS_CHANGED_BSSID) { + if (!is_zero_ether_addr(bss_conf->bssid)) { + ret = wlcore_set_bssid(wl, wlvif, bss_conf, + sta_rate_set); + if (ret < 0) + goto out; + + /* Need to update the BSSID (for filtering etc) */ do_join = true; - set_assoc = true; - - /* - * use basic rates from AP, and determine lowest rate - * to use with control frames. - */ - rates = bss_conf->basic_rates; - wlvif->basic_rate_set = - wl1271_tx_enabled_rates_get(wl, rates, - wlvif->band); - wlvif->basic_rate = - wl1271_tx_min_rate_get(wl, - wlvif->basic_rate_set); - if (sta_rate_set) - wlvif->rate_set = - wl1271_tx_enabled_rates_get(wl, - sta_rate_set, - wlvif->band); - ret = wl1271_acx_sta_rate_policies(wl, wlvif); - if (ret < 0) - goto out; - - /* - * with wl1271, we don't need to update the - * beacon_int and dtim_period, because the firmware - * updates it by itself when the first beacon is - * received after a join. - */ - ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid); - if (ret < 0) - goto out; - - /* - * Get a template for hardware connection maintenance - */ - dev_kfree_skb(wlvif->probereq); - wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl, - wlvif, - NULL); - ieoffset = offsetof(struct ieee80211_mgmt, - u.probe_req.variable); - wl1271_ssid_set(vif, wlvif->probereq, ieoffset); - - /* enable the connection monitoring feature */ - ret = wl1271_acx_conn_monit_params(wl, wlvif, true); - if (ret < 0) - goto out; } else { - /* use defaults when not associated */ - bool was_assoc = - !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, - &wlvif->flags); - bool was_ifup = - !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT, - &wlvif->flags); - wlvif->aid = 0; - - /* free probe-request template */ - dev_kfree_skb(wlvif->probereq); - wlvif->probereq = NULL; - - /* revert back to minimum rates for the current band */ - wl1271_set_band_rate(wl, wlvif); - wlvif->basic_rate = - wl1271_tx_min_rate_get(wl, - wlvif->basic_rate_set); - ret = wl1271_acx_sta_rate_policies(wl, wlvif); + ret = wlcore_clear_bssid(wl, wlvif); if (ret < 0) goto out; - - /* disable connection monitor features */ - ret = wl1271_acx_conn_monit_params(wl, wlvif, false); - - /* Disable the keep-alive feature */ - ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); - if (ret < 0) - goto out; - - /* restore the bssid filter and go to dummy bssid */ - if (was_assoc) { - /* - * we might have to disable roc, if there was - * no IF_OPER_UP notification. - */ - if (!was_ifup) { - ret = wl12xx_croc(wl, wlvif->role_id); - if (ret < 0) - goto out; - } - /* - * (we also need to disable roc in case of - * roaming on the same channel. until we will - * have a better flow...) - */ - if (test_bit(wlvif->dev_role_id, wl->roc_map)) { - ret = wl12xx_croc(wl, - wlvif->dev_role_id); - if (ret < 0) - goto out; - } - - wl1271_unjoin(wl, wlvif); - if (!bss_conf->idle) - wl12xx_start_dev(wl, wlvif); - } } } @@ -4042,7 +4036,7 @@ sta_not_found: goto out; if (do_join) { - ret = wl1271_join(wl, wlvif, set_assoc); + ret = wlcore_join(wl, wlvif); if (ret < 0) { wl1271_warning("cmd join failed %d", ret); goto out; @@ -4053,18 +4047,19 @@ sta_not_found: ret = wl12xx_roc(wl, wlvif, wlvif->role_id); if (ret < 0) goto out; + } + } + + if (changed & BSS_CHANGED_ASSOC) { + if (bss_conf->assoc) { + ret = wlcore_set_assoc(wl, wlvif, bss_conf); + if (ret < 0) + goto out; if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags)) wl12xx_set_authorized(wl, wlvif); - } - /* - * stop device role if started (we might already be in - * STA/IBSS role). - */ - if (wl12xx_dev_role_started(wlvif)) { - ret = wl12xx_stop_dev(wl, wlvif); - if (ret < 0) - goto out; + } else { + wlcore_unset_assoc(wl, wlvif); } } @@ -4434,6 +4429,7 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags); + clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags); return 0; } diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index a90d3cd09408..a555a70cd321 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -155,21 +155,13 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, struct ieee80211_sta *sta) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - if (!wlvif || wl12xx_is_dummy_packet(wl, skb)) return wl->system_hlid; if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta); - if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) || - test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) && - !ieee80211_is_auth(hdr->frame_control) && - !ieee80211_is_assoc_req(hdr->frame_control)) - return wlvif->sta.hlid; - else - return wlvif->dev_hlid; + return wlvif->sta.hlid; } unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, From 18eab430700dc877bae38cf04f0b0574b4724f0a Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:02 +0200 Subject: [PATCH 009/478] wlcore: workaround start_sta problem in wl12xx fw for some reason, the wl12xx fw is not able to rx/tx on the first start_sta cmd. Workaround it by issuing a dummy start_sta + stop_sta before starting the sta for the final time. Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 9 ++++++--- drivers/net/wireless/ti/wlcore/main.c | 15 ++++++++++++++- drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index dadf1dbb002a..6c9fba486795 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -637,7 +637,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | WLCORE_QUIRK_DUAL_PROBE_TMPL | - WLCORE_QUIRK_TKIP_HEADER_SPACE; + WLCORE_QUIRK_TKIP_HEADER_SPACE | + WLCORE_QUIRK_START_STA_FAILS; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, @@ -657,7 +658,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | WLCORE_QUIRK_DUAL_PROBE_TMPL | - WLCORE_QUIRK_TKIP_HEADER_SPACE; + WLCORE_QUIRK_TKIP_HEADER_SPACE | + WLCORE_QUIRK_START_STA_FAILS; wl->plt_fw_name = WL127X_PLT_FW_NAME; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; @@ -682,7 +684,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) /* wl128x requires TX blocksize alignment */ wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_DUAL_PROBE_TMPL | - WLCORE_QUIRK_TKIP_HEADER_SPACE; + WLCORE_QUIRK_TKIP_HEADER_SPACE | + WLCORE_QUIRK_START_STA_FAILS; wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER, WL128X_MAJOR_VER, WL128X_SUBTYPE_VER, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 63367c00d2fa..054daae9c2fe 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2490,8 +2490,21 @@ static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (is_ibss) ret = wl12xx_cmd_role_start_ibss(wl, wlvif); - else + else { + if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) { + /* + * TODO: this is an ugly workaround for wl12xx fw + * bug - we are not able to tx/rx after the first + * start_sta, so make dummy start+stop calls, + * and then call start_sta again. + * this should be fixed in the fw. + */ + wl12xx_cmd_role_start_sta(wl, wlvif); + wl12xx_cmd_role_stop_sta(wl, wlvif); + } + ret = wl12xx_cmd_role_start_sta(wl, wlvif); + } return ret; } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 68584aa0f2b0..1030b6bffeec 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -450,6 +450,9 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, /* Each RX/TX transaction requires an end-of-transaction transfer */ #define WLCORE_QUIRK_END_OF_TRANSACTION BIT(0) +/* the first start_role(sta) sometimes doesn't work on wl12xx */ +#define WLCORE_QUIRK_START_STA_FAILS BIT(1) + /* wl127x and SPI don't support SDIO block size alignment */ #define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN BIT(2) From dabf37dba405565ab46f4d1821c781730285b9ed Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:03 +0200 Subject: [PATCH 010/478] wlcore: implement .remain_on_channel() callback implement the reamin_on_channel() callback by starting a dev role (already associated with the current vif) on the requested channel/band. This channel is usually different from the channel of the sta role, so pass it to wl12xx_roc() as well, and notify mac80211 (async) when the fw is ready on the new channel. Now, in case of offchannel tx, we should use the dev role hlid, instead of the sta hlid. Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 41 +++---- drivers/net/wireless/ti/wlcore/cmd.h | 6 +- drivers/net/wireless/ti/wlcore/event.c | 7 ++ drivers/net/wireless/ti/wlcore/main.c | 154 +++++++++++++++++++++--- drivers/net/wireless/ti/wlcore/tx.c | 8 ++ drivers/net/wireless/ti/wlcore/wlcore.h | 3 + 6 files changed, 173 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index eaef3f41b252..38fa8ff8d577 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -345,7 +345,9 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type) } static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, - struct wl12xx_vif *wlvif) + struct wl12xx_vif *wlvif, + enum ieee80211_band band, + int channel) { struct wl12xx_cmd_role_start *cmd; int ret; @@ -359,9 +361,9 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id); cmd->role_id = wlvif->dev_role_id; - if (wlvif->band == IEEE80211_BAND_5GHZ) + if (band == IEEE80211_BAND_5GHZ) cmd->band = WLCORE_BAND_5GHZ; - cmd->channel = wlvif->channel; + cmd->channel = channel; if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) { ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid); @@ -1591,12 +1593,12 @@ out: } static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, - u8 role_id) + u8 role_id, enum ieee80211_band band, u8 channel) { struct wl12xx_cmd_roc *cmd; int ret = 0; - wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id); + wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id); if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID)) return -EINVAL; @@ -1608,8 +1610,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, } cmd->role_id = role_id; - cmd->channel = wlvif->channel; - switch (wlvif->band) { + cmd->channel = channel; + switch (band) { case IEEE80211_BAND_2GHZ: cmd->band = WLCORE_BAND_2_4GHZ; break; @@ -1664,30 +1666,18 @@ out: return ret; } -int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id) +int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, + enum ieee80211_band band, u8 channel) { int ret = 0; - bool is_first_roc; if (WARN_ON(test_bit(role_id, wl->roc_map))) return 0; - is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= - WL12XX_MAX_ROLES); - - ret = wl12xx_cmd_roc(wl, wlvif, role_id); + ret = wl12xx_cmd_roc(wl, wlvif, role_id, band, channel); if (ret < 0) goto out; - if (is_first_roc) { - ret = wl1271_cmd_wait_for_event(wl, - REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID); - if (ret < 0) { - wl1271_error("cmd roc event completion error"); - goto out; - } - } - __set_bit(role_id, wl->roc_map); out: return ret; @@ -1780,7 +1770,8 @@ out: } /* start dev role and roc on its channel */ -int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) +int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, + enum ieee80211_band band, int channel) { int ret; @@ -1795,11 +1786,11 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (ret < 0) goto out; - ret = wl12xx_cmd_role_start_dev(wl, wlvif); + ret = wl12xx_cmd_role_start_dev(wl, wlvif, band, channel); if (ret < 0) goto out_disable; - ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id); + ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel); if (ret < 0) goto out_stop; diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 2409f3d71f63..3be2e9228f06 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -39,7 +39,8 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif); -int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); +int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, + enum ieee80211_band band, int channel); int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); @@ -76,7 +77,8 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, u16 tx_seq_16); int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid); -int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id); +int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, + enum ieee80211_band band, u8 channel); int wl12xx_croc(struct wl1271 *wl, u8 role_id); int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, u8 hlid); diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 48907054d493..cf45aaf33bc8 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -250,6 +250,13 @@ static int wl1271_event_process(struct wl1271 *wl) disconnect_sta = true; } + if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, + "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID"); + if (wl->roc_vif) + ieee80211_ready_on_channel(wl->hw); + } + if (disconnect_sta) { u32 num_packets = wl->conf.tx.max_tx_retries; struct ieee80211_sta *sta; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 054daae9c2fe..829d818503ff 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2723,24 +2723,6 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) wl1271_warning("rate policy for channel " "failed %d", ret); - - /* - * change the ROC channel. do it only if we are - * not idle. otherwise, CROC will be called - * anyway. - */ - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, - &wlvif->flags) && - wl12xx_dev_role_started(wlvif) && - !(conf->flags & IEEE80211_CONF_IDLE)) { - ret = wl12xx_stop_dev(wl, wlvif); - if (ret < 0) - return ret; - - ret = wl12xx_start_dev(wl, wlvif); - if (ret < 0) - return ret; - } } } @@ -4057,7 +4039,8 @@ sta_not_found: /* ROC until connected (after EAPOL exchange) */ if (!is_ibss) { - ret = wl12xx_roc(wl, wlvif, wlvif->role_id); + ret = wl12xx_roc(wl, wlvif, wlvif->role_id, + wlvif->band, wlvif->channel); if (ret < 0) goto out; } @@ -4692,6 +4675,134 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop) wl1271_tx_flush(wl); } +static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + int duration) +{ + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct wl1271 *wl = hw->priv; + int channel, ret = 0; + + channel = ieee80211_frequency_to_channel(chan->center_freq); + + wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)", + channel, wlvif->role_id); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + /* return EBUSY if we can't ROC right now */ + if (WARN_ON(wl->roc_vif || + find_first_bit(wl->roc_map, + WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) { + ret = -EBUSY; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl12xx_start_dev(wl, wlvif, chan->band, channel); + if (ret < 0) + goto out_sleep; + + wl->roc_vif = vif; + ieee80211_queue_delayed_work(hw, &wl->roc_complete_work, + msecs_to_jiffies(duration)); +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return ret; +} + +static int __wlcore_roc_completed(struct wl1271 *wl) +{ + struct wl12xx_vif *wlvif; + int ret; + + /* already completed */ + if (unlikely(!wl->roc_vif)) + return 0; + + wlvif = wl12xx_vif_to_data(wl->roc_vif); + + if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) + return -EBUSY; + + ret = wl12xx_stop_dev(wl, wlvif); + if (ret < 0) + return ret; + + wl->roc_vif = NULL; + + return 0; +} + +static int wlcore_roc_completed(struct wl1271 *wl) +{ + int ret; + + wl1271_debug(DEBUG_MAC80211, "roc complete"); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) { + ret = -EBUSY; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = __wlcore_roc_completed(wl); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +static void wlcore_roc_complete_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + int ret; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1271, roc_complete_work); + + ret = wlcore_roc_completed(wl); + if (!ret) + ieee80211_remain_on_channel_expired(wl->hw); +} + +static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct wl1271 *wl = hw->priv; + + wl1271_debug(DEBUG_MAC80211, "mac80211 croc"); + + /* TODO: per-vif */ + wl1271_tx_flush(wl); + + /* + * we can't just flush_work here, because it might deadlock + * (as we might get called from the same workqueue) + */ + cancel_delayed_work_sync(&wl->roc_complete_work); + wlcore_roc_completed(wl); + + return 0; +} + static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; @@ -4883,6 +4994,8 @@ static const struct ieee80211_ops wl1271_ops = { .set_bitrate_mask = wl12xx_set_bitrate_mask, .channel_switch = wl12xx_op_channel_switch, .flush = wlcore_op_flush, + .remain_on_channel = wlcore_op_remain_on_channel, + .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; @@ -5279,6 +5392,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); + wl->hw->wiphy->max_remain_on_channel_duration = 5000; + wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; @@ -5377,6 +5492,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size) INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); + INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work); INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work); INIT_DELAYED_WORK(&wl->connection_loss_work, wl1271_connection_loss_work); diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index a555a70cd321..cf6dbee309a4 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -155,12 +155,20 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, struct ieee80211_sta *sta) { + struct ieee80211_tx_info *control; + if (!wlvif || wl12xx_is_dummy_packet(wl, skb)) return wl->system_hlid; if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta); + control = IEEE80211_SKB_CB(skb); + if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { + wl1271_debug(DEBUG_TX, "tx offchannel"); + return wlvif->dev_hlid; + } + return wlvif->sta.hlid; } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 1030b6bffeec..b636f1db562b 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -286,6 +286,9 @@ struct wl1271 { /* Connection loss work */ struct delayed_work connection_loss_work; + struct ieee80211_vif *roc_vif; + struct delayed_work roc_complete_work; + bool sched_scanning; /* The current band */ From cd1810ddcffbf779afb42c0c675aa93f5a86adda Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:04 +0200 Subject: [PATCH 011/478] wlcore: get channel from bss_conf instead of hw->conf We care only about the operational channel, not about the temporal hw channel (which won't have any real meaning in multi-channel env anyway) Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 829d818503ff..03765cf2a666 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3977,7 +3977,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, goto sta_not_found; /* save the supp_rates of the ap */ - sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band]; + sta_rate_set = sta->supp_rates[wlvif->band]; if (sta->ht_cap.ht_supported) sta_rate_set |= (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) | From b6970ee582acf059fbbc35d861376bd2ae38b7de Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:05 +0200 Subject: [PATCH 012/478] wlcore: add chanctx implementation Add some basic chanctx implementation - debug prints, and save the vif's channel/band/type. After that, we no longer need to handle channel change notifications on op_config. Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 145 ++++++++++++++------------ 1 file changed, 79 insertions(+), 66 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 03765cf2a666..d5cd58c1664d 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2682,49 +2682,7 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_conf *conf, u32 changed) { bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); - int channel, ret; - - channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - - /* if the channel changes while joined, join again */ - if (changed & IEEE80211_CONF_CHANGE_CHANNEL && - ((wlvif->band != conf->channel->band) || - (wlvif->channel != channel) || - (wlvif->channel_type != conf->channel_type))) { - /* send all pending packets */ - ret = wlcore_tx_work_locked(wl); - if (ret < 0) - return ret; - - wlvif->band = conf->channel->band; - wlvif->channel = channel; - wlvif->channel_type = conf->channel_type; - - if (is_ap) { - wl1271_set_band_rate(wl, wlvif); - ret = wl1271_init_ap_rates(wl, wlvif); - if (ret < 0) - wl1271_error("AP rate policy change failed %d", - ret); - } else { - /* - * FIXME: the mac80211 should really provide a fixed - * rate to use here. for now, just use the smallest - * possible rate for the band as a fixed rate for - * association frames and other control messages. - */ - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - wl1271_set_band_rate(wl, wlvif); - - wlvif->basic_rate = - wl1271_tx_min_rate_get(wl, - wlvif->basic_rate_set); - ret = wl1271_acx_sta_rate_policies(wl, wlvif); - if (ret < 0) - wl1271_warning("rate policy for channel " - "failed %d", ret); - } - } + int ret; if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) { @@ -2779,37 +2737,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; struct ieee80211_conf *conf = &hw->conf; - int channel, ret = 0; + int ret = 0; - channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - - wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s" + wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s" " changed 0x%x", - channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level, conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use", changed); - /* - * mac80211 will go to idle nearly immediately after transmitting some - * frames, such as the deauth. To make sure those frames reach the air, - * wait here until the TX queue is fully flushed. - */ - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || - ((changed & IEEE80211_CONF_CHANGE_IDLE) && - (conf->flags & IEEE80211_CONF_IDLE))) - wl1271_tx_flush(wl); - mutex_lock(&wl->mutex); - /* we support configuring the channel and band even while off */ - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - wl->band = conf->channel->band; - wl->channel = channel; - wl->channel_type = conf->channel_type; - } - if (changed & IEEE80211_CONF_CHANGE_POWER) wl->power_level = conf->power_level; @@ -3758,7 +3696,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret = 0; - if ((changed & BSS_CHANGED_BASIC_RATES)) { + if (changed & BSS_CHANGED_BASIC_RATES) { u32 rates = bss_conf->basic_rates; wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates, @@ -4184,6 +4122,76 @@ out: mutex_unlock(&wl->mutex); } +static int wlcore_op_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)", + ieee80211_frequency_to_channel(ctx->channel->center_freq), + ctx->channel_type); + return 0; +} + +static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)", + ieee80211_frequency_to_channel(ctx->channel->center_freq), + ctx->channel_type); +} + +static void wlcore_op_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + wl1271_debug(DEBUG_MAC80211, + "mac80211 change chanctx %d (type %d) changed 0x%x", + ieee80211_frequency_to_channel(ctx->channel->center_freq), + ctx->channel_type, changed); +} + +static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + int channel = ieee80211_frequency_to_channel( + ctx->channel->center_freq); + + wl1271_debug(DEBUG_MAC80211, + "mac80211 assign chanctx (role %d) %d (type %d)", + wlvif->role_id, channel, ctx->channel_type); + + mutex_lock(&wl->mutex); + + wlvif->band = ctx->channel->band; + wlvif->channel = channel; + wlvif->channel_type = ctx->channel_type; + + /* update default rates according to the band */ + wl1271_set_band_rate(wl, wlvif); + + mutex_unlock(&wl->mutex); + + return 0; +} + +static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + + wl1271_debug(DEBUG_MAC80211, + "mac80211 unassign chanctx (role %d) %d (type %d)", + wlvif->role_id, + ieee80211_frequency_to_channel(ctx->channel->center_freq), + ctx->channel_type); + + wl1271_tx_flush(wl); +} + static int wl1271_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) @@ -4996,6 +5004,11 @@ static const struct ieee80211_ops wl1271_ops = { .flush = wlcore_op_flush, .remain_on_channel = wlcore_op_remain_on_channel, .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel, + .add_chanctx = wlcore_op_add_chanctx, + .remove_chanctx = wlcore_op_remove_chanctx, + .change_chanctx = wlcore_op_change_chanctx, + .assign_vif_chanctx = wlcore_op_assign_vif_chanctx, + .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; From 29936266780295c812b9b8ac27a3db669e0ed1be Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:06 +0200 Subject: [PATCH 013/478] wlcore: initiate ROC/CROC on sta state updates Use the sta_state notifications to ROC when a station is about to connect, and CROC respectively on authorization (success) / deletion (failure). Change the wl12xx_update_sta_state() flow to bail out only on error, so multiple code blocks could refer to the same state. Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 44 +++++++++++++++++---------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index d5cd58c1664d..11aedb25ddd1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -83,8 +83,6 @@ static int wl12xx_set_authorized(struct wl1271 *wl, if (ret < 0) return ret; - wl12xx_croc(wl, wlvif->role_id); - wl1271_info("Association completed."); return 0; } @@ -3974,14 +3972,6 @@ sta_not_found: wl1271_warning("cmd join failed %d", ret); goto out; } - - /* ROC until connected (after EAPOL exchange) */ - if (!is_ibss) { - ret = wl12xx_roc(wl, wlvif, wlvif->role_id, - wlvif->band, wlvif->channel); - if (ret < 0) - goto out; - } } if (changed & BSS_CHANGED_ASSOC) { @@ -4398,8 +4388,11 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, /* Add station (AP mode) */ if (is_ap && old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) - return wl12xx_sta_add(wl, wlvif, sta); + new_state == IEEE80211_STA_NONE) { + ret = wl12xx_sta_add(wl, wlvif, sta); + if (ret) + return ret; + } /* Remove station (AP mode) */ if (is_ap && @@ -4407,7 +4400,6 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, new_state == IEEE80211_STA_NOTEXIST) { /* must not fail */ wl12xx_sta_remove(wl, wlvif, sta); - return 0; } /* Authorize station (AP mode) */ @@ -4419,14 +4411,17 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid); - return ret; + if (ret) + return ret; } /* Authorize station */ if (is_sta && new_state == IEEE80211_STA_AUTHORIZED) { set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags); - return wl12xx_set_authorized(wl, wlvif); + ret = wl12xx_set_authorized(wl, wlvif); + if (ret) + return ret; } if (is_sta && @@ -4434,9 +4429,26 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, new_state == IEEE80211_STA_ASSOC) { clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags); clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags); - return 0; } + /* clear ROCs on failure or authorization */ + if (is_sta && + (new_state == IEEE80211_STA_AUTHORIZED || + new_state == IEEE80211_STA_NOTEXIST)) { + if (test_bit(wlvif->role_id, wl->roc_map)) + wl12xx_croc(wl, wlvif->role_id); + } + + if (is_sta && + old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + if (find_first_bit(wl->roc_map, + WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) { + WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID); + wl12xx_roc(wl, wlvif, wlvif->role_id, + wlvif->band, wlvif->channel); + } + } return 0; } From 6c7b519464cf6c64b02ea0d9e41bd2e49bbd51ce Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:07 +0200 Subject: [PATCH 014/478] wlcore: set active psm on association The default ps mode of the fw is auto, while the default ps mode of mac80211 is active (ps off). In order to sync them, configure active ps on association. Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 11aedb25ddd1..e02f2aa68289 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2614,6 +2614,12 @@ static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) return ret; + /* + * The default fw psm configuration is AUTO, while mac80211 default + * setting is off (ACTIVE), so sync the fw with the correct value. + */ + ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE); + return ret; } From 42ec1f82a862b38eb84bc3bbd7fb97b1aa48f18c Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:08 +0200 Subject: [PATCH 015/478] wlcore: specify correct supported_rates The supported_rates field should contain all our supported rates, even if the remote peer doesn't support them. (rename CONF_TX_AP_ENABLED_RATES to CONF_TX_ENABLED_RATES, as we now use it for both ap and sta) Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 12 ++++++++++-- drivers/net/wireless/ti/wlcore/conf.h | 4 ++-- drivers/net/wireless/ti/wlcore/init.c | 2 +- drivers/net/wireless/ti/wlcore/main.c | 6 +++--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 38fa8ff8d577..1cf1225d2f5c 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -441,6 +441,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct wl12xx_cmd_role_start *cmd; + u32 supported_rates; int ret; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -461,7 +462,14 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) cmd->sta.ssid_len = wlvif->ssid_len; memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len); memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN); - cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set); + + supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | + wlcore_hw_sta_get_ap_rate_mask(wl, wlvif); + if (wlvif->p2p) + supported_rates &= ~CONF_TX_CCK_RATES; + + cmd->sta.local_rates = cpu_to_le32(supported_rates); + cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type); if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) { @@ -601,7 +609,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len); } - supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES | + supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x", diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 9e40760bafe1..a5d190816f34 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -415,11 +415,11 @@ struct conf_rx_settings { #define CONF_TX_RATE_MASK_BASIC_P2P CONF_HW_BIT_RATE_6MBPS /* - * Rates supported for data packets when operating as AP. Note the absence + * Rates supported for data packets when operating as STA/AP. Note the absence * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop * one. The rate dropped is not mandatory under any operating mode. */ -#define CONF_TX_AP_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ +#define CONF_TX_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \ CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \ diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 32d157f62f31..84641b3c1b80 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -463,7 +463,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) supported_rates = CONF_TX_OFDM_RATES; else - supported_rates = CONF_TX_AP_ENABLED_RATES; + supported_rates = CONF_TX_ENABLED_RATES; /* unconditionally enable HT rates */ supported_rates |= CONF_TX_MCS_RATES; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e02f2aa68289..ef1fd0435947 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2035,15 +2035,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++) wl12xx_allocate_rate_policy(wl, &wlvif->ap.ucast_rate_idx[i]); - wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES; + wlvif->basic_rate_set = CONF_TX_ENABLED_RATES; /* * TODO: check if basic_rate shouldn't be * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); * instead (the same thing for STA above). */ - wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES; + wlvif->basic_rate = CONF_TX_ENABLED_RATES; /* TODO: this seems to be used only for STA, check it */ - wlvif->rate_set = CONF_TX_AP_ENABLED_RATES; + wlvif->rate_set = CONF_TX_ENABLED_RATES; } wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate; From ec87011a4ac30a4a6ddfbf5dc17e302a490c7763 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:09 +0200 Subject: [PATCH 016/478] wlcore: reconfigure rate policy on association When first configuring the rate policy, before auth, we still don't have the correct rates that were agreed during association. Reconfigure the rate policy on association in order to update them. Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ef1fd0435947..87fa426c46f2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2553,7 +2553,8 @@ static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif) } static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif, - struct ieee80211_bss_conf *bss_conf) + struct ieee80211_bss_conf *bss_conf, + u32 sta_rate_set) { int ieoffset; int ret; @@ -2619,6 +2620,18 @@ static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif, * setting is off (ACTIVE), so sync the fw with the correct value. */ ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE); + if (ret < 0) + return ret; + + if (sta_rate_set) { + wlvif->rate_set = + wl1271_tx_enabled_rates_get(wl, + sta_rate_set, + wlvif->band); + ret = wl1271_acx_sta_rate_policies(wl, wlvif); + if (ret < 0) + return ret; + } return ret; } @@ -3912,7 +3925,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, wlvif->rssi_thold = bss_conf->cqm_rssi_thold; } - if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT)) { + if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT | + BSS_CHANGED_ASSOC)) { rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!sta) @@ -3982,7 +3996,8 @@ sta_not_found: if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { - ret = wlcore_set_assoc(wl, wlvif, bss_conf); + ret = wlcore_set_assoc(wl, wlvif, bss_conf, + sta_rate_set); if (ret < 0) goto out; From 58321b296de93df4a060f012151213fe1a1a3498 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:10 +0200 Subject: [PATCH 017/478] wlcore: refactor CHANGED_HT handling Pass a variable indicating whether HT is enabled, instead of duplicating the function call with different arguments. Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 52 +++++++++++---------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 87fa426c46f2..f526ae0b9744 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4009,41 +4009,29 @@ sta_not_found: } /* Handle new association with HT. Do this after join. */ - if (sta_exists) { - if ((changed & BSS_CHANGED_HT) && - (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { - ret = wl1271_acx_set_ht_capabilities(wl, - &sta_ht_cap, - true, - wlvif->sta.hlid); - if (ret < 0) { - wl1271_warning("Set ht cap true failed %d", - ret); - goto out; - } - } - /* handle new association without HT and disassociation */ - else if (changed & BSS_CHANGED_ASSOC) { - ret = wl1271_acx_set_ht_capabilities(wl, - &sta_ht_cap, - false, - wlvif->sta.hlid); - if (ret < 0) { - wl1271_warning("Set ht cap false failed %d", - ret); - goto out; - } - } - } + if (sta_exists && + (changed & BSS_CHANGED_HT)) { + bool enabled = + bss_conf->channel_type != NL80211_CHAN_NO_HT; - /* Handle HT information change. Done after join. */ - if ((changed & BSS_CHANGED_HT) && - (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { - ret = wl1271_acx_set_ht_information(wl, wlvif, - bss_conf->ht_operation_mode); + ret = wl1271_acx_set_ht_capabilities(wl, + &sta_ht_cap, + enabled, + wlvif->sta.hlid); if (ret < 0) { - wl1271_warning("Set ht information failed %d", ret); + wl1271_warning("Set ht cap failed %d", ret); goto out; + + } + + if (enabled) { + ret = wl1271_acx_set_ht_information(wl, wlvif, + bss_conf->ht_operation_mode); + if (ret < 0) { + wl1271_warning("Set ht information failed %d", + ret); + goto out; + } } } From dc62a3dbf38a7c3aa5a7ae724b04e0fefc10fea4 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 20 Nov 2012 13:20:11 +0200 Subject: [PATCH 018/478] wlcore: configure the remote rates with our own rates With the new connection flow, start_sta is called before the remote rates where updated. Use our own supported rates instead to make sure we don't disable any potential rate (the rate policies will be updated later, but there is currently no way to update the remote rates) Signed-off-by: Eliad Peller Reviewed-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 1cf1225d2f5c..04ba86d2d875 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -479,7 +479,12 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) } cmd->sta.hlid = wlvif->sta.hlid; cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif); - cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set); + /* + * We don't have the correct remote rates in this stage, and there + * is no way to update them later, so use our supported rates instead. + * The fw will take the configured rate policies into account anyway. + */ + cmd->sta.remote_rates = cpu_to_le32(supported_rates); wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d " "basic_rate_set: 0x%x, remote_rates: 0x%x", From d3f5a1b59839e3df3e40b9e520d16b8475d27681 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 19 Nov 2012 17:14:05 +0200 Subject: [PATCH 019/478] wlcore: print role_id on bss_info_changed In multi-vif setup it's useful to know the role_id being configured. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index f526ae0b9744..a420ba3d7547 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4084,8 +4084,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret; - wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x", - (int)changed); + wl1271_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x", + wlvif->role_id, (int)changed); /* * make sure to cancel pending disconnections if our association From a8e27820f27155d2eaea2426b10bac810c293f3b Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 19 Nov 2012 17:14:06 +0200 Subject: [PATCH 020/478] wlcore: don't leak wl->mbox free it on wlcore_free_hw() Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index a420ba3d7547..d425bd5eda7e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5638,6 +5638,7 @@ int wlcore_free_hw(struct wl1271 *wl) device_remove_file(wl->dev, &dev_attr_hw_pg_ver); device_remove_file(wl->dev, &dev_attr_bt_coex_state); + kfree(wl->mbox); free_page((unsigned long)wl->fwlog); dev_kfree_skb(wl->dummy_packet); free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size)); From ef08d0281a90781b07d2030c1c69f4fb2f156267 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 20 Nov 2012 11:03:31 +0200 Subject: [PATCH 021/478] wlcore: avoid using goto in normal code flow Remove goto and label in the code where a simple if can be used. If nothing else, this is at least confusing git diff, which shows the label name as the name of the function. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index d425bd5eda7e..e488056f145e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3929,19 +3929,19 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, BSS_CHANGED_ASSOC)) { rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); - if (!sta) - goto sta_not_found; + if (sta) { + u8 *rx_mask = sta->ht_cap.mcs.rx_mask; - /* save the supp_rates of the ap */ - sta_rate_set = sta->supp_rates[wlvif->band]; - if (sta->ht_cap.ht_supported) - sta_rate_set |= - (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) | - (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET); - sta_ht_cap = sta->ht_cap; - sta_exists = true; + /* save the supp_rates of the ap */ + sta_rate_set = sta->supp_rates[wlvif->band]; + if (sta->ht_cap.ht_supported) + sta_rate_set |= + (rx_mask[0] << HW_HT_RATES_OFFSET) | + (rx_mask[1] << HW_MIMO_RATES_OFFSET); + sta_ht_cap = sta->ht_cap; + sta_exists = true; + } -sta_not_found: rcu_read_unlock(); } From 48af2eb046bd452f381f142b19cf9c86ddf575f5 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 20 Nov 2012 11:03:32 +0200 Subject: [PATCH 022/478] wlcore: remove a bunch of unnecessary parentheses Some if statements had unnecessary parentheses. Remove them for consistency. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e488056f145e..11ee7ccbca5f 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3679,7 +3679,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret = 0; - if ((changed & BSS_CHANGED_BEACON_INT)) { + if (changed & BSS_CHANGED_BEACON_INT) { wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d", bss_conf->beacon_int); @@ -3692,7 +3692,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, wl1271_ap_set_probe_resp_tmpl(wl, rate, vif); } - if ((changed & BSS_CHANGED_BEACON)) { + if (changed & BSS_CHANGED_BEACON) { ret = wlcore_set_beacon_template(wl, vif, is_ap); if (ret < 0) goto out; @@ -3744,7 +3744,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, if (ret < 0) goto out; - if ((changed & BSS_CHANGED_BEACON_ENABLED)) { + if (changed & BSS_CHANGED_BEACON_ENABLED) { if (bss_conf->enable_beacon) { if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) { ret = wl12xx_cmd_role_start_ap(wl, wlvif); @@ -3913,7 +3913,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, do_join = true; } - if ((changed & BSS_CHANGED_CQM)) { + if (changed & BSS_CHANGED_CQM) { bool enable = false; if (bss_conf->cqm_rssi_thold) enable = true; From 598b262a06235ced311a5327e1fbcedd74da53c9 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:11 +0200 Subject: [PATCH 023/478] wlcore: don't call ieee80211_sched_scan_stopped directly When we stop sched scan during connection, we shouldn't call ieee80211_sched_scan_stopped directly, but do it in the normal flow, as part of the SCHED_SCAN_COMPLETED event handling. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 11ee7ccbca5f..111b84fcf66e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3820,10 +3820,8 @@ static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif, wlvif->band); /* we only support sched_scan while not connected */ - if (wl->sched_scanning) { + if (wl->sched_scanning) wl1271_scan_sched_scan_stop(wl, wlvif); - ieee80211_sched_scan_stopped(wl->hw); - } ret = wl1271_acx_sta_rate_policies(wl, wlvif); if (ret < 0) From 512c5385e6dc86addc9e51848913378052aa5e03 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:12 +0200 Subject: [PATCH 024/478] wlcore: make scan scan configuration functions more generic 18xx and 12xx have different scan APIs. In 18xx, the scan and the sched scan use the same struct. Prepare the scan configuration functions to it, by taking more generic params (e.g. ieee80211_channel) instead of specific structs/requests. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/scan.c | 129 ++++++++++++++++---------- 1 file changed, 78 insertions(+), 51 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index d00501493dfe..22dd7e94f40f 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -403,17 +403,19 @@ out: } static int -wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, - struct cfg80211_sched_scan_request *req, - struct conn_scan_ch_params *channels, - u32 band, bool radar, bool passive, - int start, int max_channels, - u8 *n_pactive_ch) +wlcore_scan_get_channels(struct wl1271 *wl, + struct ieee80211_channel *req_channels[], + u32 n_channels, + u32 n_ssids, + struct conn_scan_ch_params *channels, + u32 band, bool radar, bool passive, + int start, int max_channels, + u8 *n_pactive_ch) { struct conf_sched_scan_settings *c = &wl->conf.sched_scan; int i, j; u32 flags; - bool force_passive = !req->n_ssids; + bool force_passive = !n_ssids; u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe; u32 dwell_time_passive, dwell_time_dfs; @@ -423,7 +425,7 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, delta_per_probe = c->dwell_time_delta_per_probe; min_dwell_time_active = c->base_dwell_time + - req->n_ssids * c->num_probe_reqs * delta_per_probe; + n_ssids * c->num_probe_reqs * delta_per_probe; max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta; @@ -433,27 +435,27 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000); for (i = 0, j = start; - i < req->n_channels && j < max_channels; + i < n_channels && j < max_channels; i++) { - flags = req->channels[i]->flags; + flags = req_channels[i]->flags; if (force_passive) flags |= IEEE80211_CHAN_PASSIVE_SCAN; - if ((req->channels[i]->band == band) && + if ((req_channels[i]->band == band) && !(flags & IEEE80211_CHAN_DISABLED) && (!!(flags & IEEE80211_CHAN_RADAR) == radar) && /* if radar is set, we ignore the passive flag */ (radar || !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) { wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", - req->channels[i]->band, - req->channels[i]->center_freq); + req_channels[i]->band, + req_channels[i]->center_freq); wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", - req->channels[i]->hw_value, - req->channels[i]->flags); + req_channels[i]->hw_value, + req_channels[i]->flags); wl1271_debug(DEBUG_SCAN, "max_power %d", - req->channels[i]->max_power); + req_channels[i]->max_power); wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d", min_dwell_time_active, max_dwell_time_active); @@ -473,10 +475,11 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, channels[j].max_duration = cpu_to_le16(max_dwell_time_active); - channels[j].tx_power_att = req->channels[i]->max_power; - channels[j].channel = req->channels[i]->hw_value; + channels[j].tx_power_att = req_channels[i]->max_power; + channels[j].channel = req_channels[i]->hw_value; - if ((band == IEEE80211_BAND_2GHZ) && + if (n_pactive_ch && + (band == IEEE80211_BAND_2GHZ) && (channels[j].channel >= 12) && (channels[j].channel <= 14) && (flags & IEEE80211_CHAN_PASSIVE_SCAN) && @@ -501,45 +504,68 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, } static bool -wl1271_scan_sched_scan_channels(struct wl1271 *wl, - struct cfg80211_sched_scan_request *req, - struct wl1271_cmd_sched_scan_config *cfg) +wlcore_set_scan_chan_params(struct wl1271 *wl, + struct wl1271_cmd_sched_scan_config *cfg, + struct ieee80211_channel *channels[], + u32 n_channels, + u32 n_ssids) { u8 n_pactive_ch = 0; cfg->passive[0] = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, - IEEE80211_BAND_2GHZ, - false, true, 0, - MAX_CHANNELS_2GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_2, + IEEE80211_BAND_2GHZ, + false, true, 0, + MAX_CHANNELS_2GHZ, + &n_pactive_ch); cfg->active[0] = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, - IEEE80211_BAND_2GHZ, - false, false, - cfg->passive[0], - MAX_CHANNELS_2GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_2, + IEEE80211_BAND_2GHZ, + false, false, + cfg->passive[0], + MAX_CHANNELS_2GHZ, + &n_pactive_ch); cfg->passive[1] = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, - IEEE80211_BAND_5GHZ, - false, true, 0, - MAX_CHANNELS_5GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_5, + IEEE80211_BAND_5GHZ, + false, true, 0, + MAX_CHANNELS_5GHZ, + &n_pactive_ch); cfg->dfs = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, - IEEE80211_BAND_5GHZ, - true, true, - cfg->passive[1], - MAX_CHANNELS_5GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_5, + IEEE80211_BAND_5GHZ, + true, true, + cfg->passive[1], + MAX_CHANNELS_5GHZ, + &n_pactive_ch); cfg->active[1] = - wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, - IEEE80211_BAND_5GHZ, - false, false, - cfg->passive[1] + cfg->dfs, - MAX_CHANNELS_5GHZ, - &n_pactive_ch); + wlcore_scan_get_channels(wl, + channels, + n_channels, + n_ssids, + cfg->channels_5, + IEEE80211_BAND_5GHZ, + false, false, + cfg->passive[1] + cfg->dfs, + MAX_CHANNELS_5GHZ, + &n_pactive_ch); + /* 802.11j channels are not supported yet */ cfg->passive[2] = 0; cfg->active[2] = 0; @@ -705,7 +731,8 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); - if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) { + if (!wlcore_set_scan_chan_params(wl, cfg, req->channels, + req->n_channels, req->n_ssids)) { wl1271_error("scan channel list is empty"); ret = -EINVAL; goto out; From 750a986d6a69f3f9ed7913bab8c6d171f291ebe6 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:13 +0200 Subject: [PATCH 025/478] wl18xx: change fw name and temporarily fail loading The new fw (8.5.0.0.28) is not backward compatible with older drivers. Use a new fw name (along with bumping the min fw version), and add some code to fail any boot attempt during the fw api alignment patches (as the driver is not functional in these transitional patches). This code will be removed after the api alignment will be done. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 6 +++++- drivers/net/wireless/ti/wl18xx/wl18xx.h | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index a39682a7c25f..8ba1c9387a14 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -595,7 +595,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { }; /* TODO: maybe move to a new header file? */ -#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin" +#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin" static int wl18xx_identify_chip(struct wl1271 *wl) { @@ -1385,6 +1385,10 @@ static int wl18xx_setup(struct wl1271 *wl) struct wl18xx_priv *priv = wl->priv; int ret; + wl1271_error("driver is in transitional commit (due to fw api" + "change) and can't be booted!"); + return -EINVAL; + wl->rtable = wl18xx_rtable; wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS; wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS; diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 96a1e438d677..4d295a574a87 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -26,10 +26,10 @@ /* minimum FW required for driver */ #define WL18XX_CHIP_VER 8 -#define WL18XX_IFTYPE_VER 2 +#define WL18XX_IFTYPE_VER 5 #define WL18XX_MAJOR_VER 0 #define WL18XX_SUBTYPE_VER 0 -#define WL18XX_MINOR_VER 100 +#define WL18XX_MINOR_VER 28 #define WL18XX_CMD_MAX_SIZE 740 From e9b9d4550733dc322cd0d191e0423a480d9180ef Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:14 +0200 Subject: [PATCH 026/478] wlcore: update commands enum to new fw api Align the commands enum with the new fw api (8.4.0.0.19) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 3be2e9228f06..d021305c1ff9 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -151,8 +151,11 @@ enum wl1271_commands { CMD_WFD_START_DISCOVERY = 45, CMD_WFD_STOP_DISCOVERY = 46, CMD_WFD_ATTRIBUTE_CONFIG = 47, - CMD_NOP = 48, - CMD_LAST_COMMAND, + CMD_GENERIC_CFG = 48, + CMD_NOP = 49, + + /* start of 18xx specific commands */ + CMD_DFS_CHANNEL_CONFIG = 60, MAX_COMMAND_ID = 0xFFFF, }; From 78e28062fea51c62280cd17fe6143ed583f83ba0 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:15 +0200 Subject: [PATCH 027/478] wlcore: split 18xx and 12xx scan mechanism The scan APIs of 12xx and 18xx are totally different. Use some common functions as much as possible (e.g. for setting scan channels), but split scan.c into chip-specific scan.c files, each implementing its own scan mechanism. (in other words - move most of the current wlcore's scan.c into wl12xx, and implement a similar mechanism in 18xx, according to the new api) New wlcore ops are introduced in order to call the chip-specific scan functions. The template indices used for each scan (regular/scheduled) are also different between the chips, so set the correct indices used for each scan type after identifying the chip. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/Makefile | 2 +- drivers/net/wireless/ti/wl12xx/main.c | 11 + drivers/net/wireless/ti/wl12xx/scan.c | 500 +++++++++++++++++++++++ drivers/net/wireless/ti/wl12xx/scan.h | 138 +++++++ drivers/net/wireless/ti/wl18xx/Makefile | 2 +- drivers/net/wireless/ti/wl18xx/main.c | 13 +- drivers/net/wireless/ti/wl18xx/scan.c | 320 +++++++++++++++ drivers/net/wireless/ti/wl18xx/scan.h | 120 ++++++ drivers/net/wireless/ti/wlcore/cmd.c | 12 +- drivers/net/wireless/ti/wlcore/cmd.h | 6 +- drivers/net/wireless/ti/wlcore/event.c | 4 +- drivers/net/wireless/ti/wlcore/init.c | 10 +- drivers/net/wireless/ti/wlcore/main.c | 15 +- drivers/net/wireless/ti/wlcore/scan.c | 513 ++---------------------- drivers/net/wireless/ti/wlcore/scan.h | 125 +----- drivers/net/wireless/ti/wlcore/tx.c | 1 + drivers/net/wireless/ti/wlcore/wlcore.h | 13 + 17 files changed, 1208 insertions(+), 597 deletions(-) create mode 100644 drivers/net/wireless/ti/wl12xx/scan.c create mode 100644 drivers/net/wireless/ti/wl12xx/scan.h create mode 100644 drivers/net/wireless/ti/wl18xx/scan.c create mode 100644 drivers/net/wireless/ti/wl18xx/scan.h diff --git a/drivers/net/wireless/ti/wl12xx/Makefile b/drivers/net/wireless/ti/wl12xx/Makefile index da509aa7d009..8d9afd238adc 100644 --- a/drivers/net/wireless/ti/wl12xx/Makefile +++ b/drivers/net/wireless/ti/wl12xx/Makefile @@ -1,3 +1,3 @@ -wl12xx-objs = main.o cmd.o acx.o debugfs.o +wl12xx-objs = main.o cmd.o acx.o debugfs.o scan.o obj-$(CONFIG_WL12XX) += wl12xx.o diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 6c9fba486795..ada7031777cc 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -38,6 +38,7 @@ #include "reg.h" #include "cmd.h" #include "acx.h" +#include "scan.h" #include "debugfs.h" static char *fref_param; @@ -698,6 +699,11 @@ static int wl12xx_identify_chip(struct wl1271 *wl) goto out; } + /* common settings */ + wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY; + wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY; + wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; + wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; out: return ret; } @@ -1618,6 +1624,11 @@ static struct wlcore_ops wl12xx_ops = { .set_rx_csum = NULL, .ap_get_mimo_wide_rate_mask = NULL, .debugfs_init = wl12xx_debugfs_add_files, + .scan_start = wl12xx_scan_start, + .scan_stop = wl12xx_scan_stop, + .scan_completed = wl12xx_scan_completed, + .sched_scan_start = wl12xx_sched_scan_start, + .sched_scan_stop = wl12xx_scan_sched_scan_stop, .get_spare_blocks = wl12xx_get_spare_blocks, .set_key = wl12xx_set_key, .pre_pkt_send = NULL, diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c new file mode 100644 index 000000000000..a99e8764162d --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/scan.c @@ -0,0 +1,500 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include "scan.h" +#include "../wlcore/debug.h" +#include "../wlcore/tx.h" + +static int wl1271_get_scan_channels(struct wl1271 *wl, + struct cfg80211_scan_request *req, + struct basic_scan_channel_params *channels, + enum ieee80211_band band, bool passive) +{ + struct conf_scan_settings *c = &wl->conf.scan; + int i, j; + u32 flags; + + for (i = 0, j = 0; + i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; + i++) { + flags = req->channels[i]->flags; + + if (!test_bit(i, wl->scan.scanned_ch) && + !(flags & IEEE80211_CHAN_DISABLED) && + (req->channels[i]->band == band) && + /* + * In passive scans, we scan all remaining + * channels, even if not marked as such. + * In active scans, we only scan channels not + * marked as passive. + */ + (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) { + wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", + req->channels[i]->band, + req->channels[i]->center_freq); + wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", + req->channels[i]->hw_value, + req->channels[i]->flags); + wl1271_debug(DEBUG_SCAN, + "max_antenna_gain %d, max_power %d", + req->channels[i]->max_antenna_gain, + req->channels[i]->max_power); + wl1271_debug(DEBUG_SCAN, "beacon_found %d", + req->channels[i]->beacon_found); + + if (!passive) { + channels[j].min_duration = + cpu_to_le32(c->min_dwell_time_active); + channels[j].max_duration = + cpu_to_le32(c->max_dwell_time_active); + } else { + channels[j].min_duration = + cpu_to_le32(c->min_dwell_time_passive); + channels[j].max_duration = + cpu_to_le32(c->max_dwell_time_passive); + } + channels[j].early_termination = 0; + channels[j].tx_power_att = req->channels[i]->max_power; + channels[j].channel = req->channels[i]->hw_value; + + memset(&channels[j].bssid_lsb, 0xff, 4); + memset(&channels[j].bssid_msb, 0xff, 2); + + /* Mark the channels we already used */ + set_bit(i, wl->scan.scanned_ch); + + j++; + } + } + + return j; +} + +#define WL1271_NOTHING_TO_SCAN 1 + +static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, + enum ieee80211_band band, + bool passive, u32 basic_rate) +{ + struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + struct wl1271_cmd_scan *cmd; + struct wl1271_cmd_trigger_scan_to *trigger; + int ret; + u16 scan_options = 0; + + /* skip active scans if we don't have SSIDs */ + if (!passive && wl->scan.req->n_ssids == 0) + return WL1271_NOTHING_TO_SCAN; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); + if (!cmd || !trigger) { + ret = -ENOMEM; + goto out; + } + + if (wl->conf.scan.split_scan_timeout) + scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; + + if (passive) + scan_options |= WL1271_SCAN_OPT_PASSIVE; + + cmd->params.role_id = wlvif->role_id; + + if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) { + ret = -EINVAL; + goto out; + } + + cmd->params.scan_options = cpu_to_le16(scan_options); + + cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, + cmd->channels, + band, passive); + if (cmd->params.n_ch == 0) { + ret = WL1271_NOTHING_TO_SCAN; + goto out; + } + + cmd->params.tx_rate = cpu_to_le32(basic_rate); + cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; + cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; + cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; + + if (band == IEEE80211_BAND_2GHZ) + cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ; + else + cmd->params.band = WL1271_SCAN_BAND_5_GHZ; + + if (wl->scan.ssid_len && wl->scan.ssid) { + cmd->params.ssid_len = wl->scan.ssid_len; + memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len); + } + + memcpy(cmd->addr, vif->addr, ETH_ALEN); + + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + cmd->params.role_id, band, + wl->scan.ssid, wl->scan.ssid_len, + wl->scan.req->ie, + wl->scan.req->ie_len, false); + if (ret < 0) { + wl1271_error("PROBE request template failed"); + goto out; + } + + trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); + ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, + sizeof(*trigger), 0); + if (ret < 0) { + wl1271_error("trigger scan to failed for hw scan"); + goto out; + } + + wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); + + ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("SCAN failed"); + goto out; + } + +out: + kfree(cmd); + kfree(trigger); + return ret; +} + +int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + struct wl1271_cmd_header *cmd = NULL; + int ret = 0; + + if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE)) + return -EINVAL; + + wl1271_debug(DEBUG_CMD, "cmd scan stop"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd, + sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("cmd stop_scan failed"); + goto out; + } +out: + kfree(cmd); + return ret; +} + +void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + int ret = 0; + enum ieee80211_band band; + u32 rate, mask; + + switch (wl->scan.state) { + case WL1271_SCAN_STATE_IDLE: + break; + + case WL1271_SCAN_STATE_2GHZ_ACTIVE: + band = IEEE80211_BAND_2GHZ; + mask = wlvif->bitrate_masks[band]; + if (wl->scan.req->no_cck) { + mask &= ~CONF_TX_CCK_RATES; + if (!mask) + mask = CONF_TX_RATE_MASK_BASIC_P2P; + } + rate = wl1271_tx_min_rate_get(wl, mask); + ret = wl1271_scan_send(wl, wlvif, band, false, rate); + if (ret == WL1271_NOTHING_TO_SCAN) { + wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; + wl1271_scan_stm(wl, wlvif); + } + + break; + + case WL1271_SCAN_STATE_2GHZ_PASSIVE: + band = IEEE80211_BAND_2GHZ; + mask = wlvif->bitrate_masks[band]; + if (wl->scan.req->no_cck) { + mask &= ~CONF_TX_CCK_RATES; + if (!mask) + mask = CONF_TX_RATE_MASK_BASIC_P2P; + } + rate = wl1271_tx_min_rate_get(wl, mask); + ret = wl1271_scan_send(wl, wlvif, band, true, rate); + if (ret == WL1271_NOTHING_TO_SCAN) { + if (wl->enable_11a) + wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; + else + wl->scan.state = WL1271_SCAN_STATE_DONE; + wl1271_scan_stm(wl, wlvif); + } + + break; + + case WL1271_SCAN_STATE_5GHZ_ACTIVE: + band = IEEE80211_BAND_5GHZ; + rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); + ret = wl1271_scan_send(wl, wlvif, band, false, rate); + if (ret == WL1271_NOTHING_TO_SCAN) { + wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; + wl1271_scan_stm(wl, wlvif); + } + + break; + + case WL1271_SCAN_STATE_5GHZ_PASSIVE: + band = IEEE80211_BAND_5GHZ; + rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); + ret = wl1271_scan_send(wl, wlvif, band, true, rate); + if (ret == WL1271_NOTHING_TO_SCAN) { + wl->scan.state = WL1271_SCAN_STATE_DONE; + wl1271_scan_stm(wl, wlvif); + } + + break; + + case WL1271_SCAN_STATE_DONE: + wl->scan.failed = false; + cancel_delayed_work(&wl->scan_complete_work); + ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, + msecs_to_jiffies(0)); + break; + + default: + wl1271_error("invalid scan state"); + break; + } + + if (ret < 0) { + cancel_delayed_work(&wl->scan_complete_work); + ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, + msecs_to_jiffies(0)); + } +} + +static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd, + struct wlcore_scan_channels *cmd_channels) +{ + memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive)); + memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active)); + cmd->dfs = cmd_channels->dfs; + cmd->n_pactive_ch = cmd_channels->passive_active; + + memcpy(cmd->channels_2, cmd_channels->channels_2, + sizeof(cmd->channels_2)); + memcpy(cmd->channels_5, cmd_channels->channels_5, + sizeof(cmd->channels_2)); + /* channels_4 are not supported, so no need to copy them */ +} + +int wl1271_scan_sched_scan_config(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct wl1271_cmd_sched_scan_config *cfg = NULL; + struct wlcore_scan_channels *cfg_channels = NULL; + struct conf_sched_scan_settings *c = &wl->conf.sched_scan; + int i, ret; + bool force_passive = !req->n_ssids; + + wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->role_id = wlvif->role_id; + cfg->rssi_threshold = c->rssi_threshold; + cfg->snr_threshold = c->snr_threshold; + cfg->n_probe_reqs = c->num_probe_reqs; + /* cycles set to 0 it means infinite (until manually stopped) */ + cfg->cycles = 0; + /* report APs when at least 1 is found */ + cfg->report_after = 1; + /* don't stop scanning automatically when something is found */ + cfg->terminate = 0; + cfg->tag = WL1271_SCAN_DEFAULT_TAG; + /* don't filter on BSS type */ + cfg->bss_type = SCAN_BSS_TYPE_ANY; + /* currently NL80211 supports only a single interval */ + for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) + cfg->intervals[i] = cpu_to_le32(req->interval); + + cfg->ssid_len = 0; + ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); + if (ret < 0) + goto out; + + cfg->filter_type = ret; + + wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); + + cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL); + if (!cfg_channels) { + ret = -ENOMEM; + goto out; + } + + if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels, + req->n_channels, req->n_ssids)) { + wl1271_error("scan channel list is empty"); + ret = -EINVAL; + goto out; + } + wl12xx_adjust_channels(cfg, cfg_channels); + + if (!force_passive && cfg->active[0]) { + u8 band = IEEE80211_BAND_2GHZ; + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + wlvif->role_id, band, + req->ssids[0].ssid, + req->ssids[0].ssid_len, + ies->ie[band], + ies->len[band], true); + if (ret < 0) { + wl1271_error("2.4GHz PROBE request template failed"); + goto out; + } + } + + if (!force_passive && cfg->active[1]) { + u8 band = IEEE80211_BAND_5GHZ; + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + wlvif->role_id, band, + req->ssids[0].ssid, + req->ssids[0].ssid_len, + ies->ie[band], + ies->len[band], true); + if (ret < 0) { + wl1271_error("5GHz PROBE request template failed"); + goto out; + } + } + + wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); + + ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, + sizeof(*cfg), 0); + if (ret < 0) { + wl1271_error("SCAN configuration failed"); + goto out; + } +out: + kfree(cfg_channels); + kfree(cfg); + return ret; +} + +int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + struct wl1271_cmd_sched_scan_start *start; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); + + if (wlvif->bss_type != BSS_TYPE_STA_BSS) + return -EOPNOTSUPP; + + if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) && + test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) + return -EBUSY; + + start = kzalloc(sizeof(*start), GFP_KERNEL); + if (!start) + return -ENOMEM; + + start->role_id = wlvif->role_id; + start->tag = WL1271_SCAN_DEFAULT_TAG; + + ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, + sizeof(*start), 0); + if (ret < 0) { + wl1271_error("failed to send scan start command"); + goto out_free; + } + +out_free: + kfree(start); + return ret; +} + +int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + int ret; + + ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies); + if (ret < 0) + return ret; + + return wl1271_scan_sched_scan_start(wl, wlvif); +} + +void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + struct wl1271_cmd_sched_scan_stop *stop; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); + + /* FIXME: what to do if alloc'ing to stop fails? */ + stop = kzalloc(sizeof(*stop), GFP_KERNEL); + if (!stop) { + wl1271_error("failed to alloc memory to send sched scan stop"); + return; + } + + stop->role_id = wlvif->role_id; + stop->tag = WL1271_SCAN_DEFAULT_TAG; + + ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, + sizeof(*stop), 0); + if (ret < 0) { + wl1271_error("failed to send sched scan stop command"); + goto out_free; + } + +out_free: + kfree(stop); +} + +int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_scan_request *req) +{ + wl1271_scan_stm(wl, wlvif); + return 0; +} + +void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + wl1271_scan_stm(wl, wlvif); +} diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h new file mode 100644 index 000000000000..bd075dec5946 --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/scan.h @@ -0,0 +1,138 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL12XX_SCAN_H__ +#define __WL12XX_SCAN_H__ + +#include "../wlcore/wlcore.h" +#include "../wlcore/cmd.h" +#include "../wlcore/scan.h" + +struct basic_scan_params { + /* Scan option flags (WL1271_SCAN_OPT_*) */ + __le16 scan_options; + u8 role_id; + /* Number of scan channels in the list (maximum 30) */ + u8 n_ch; + /* This field indicates the number of probe requests to send + per channel for an active scan */ + u8 n_probe_reqs; + u8 tid_trigger; + u8 ssid_len; + u8 use_ssid_list; + + /* Rate bit field for sending the probes */ + __le32 tx_rate; + + u8 ssid[IEEE80211_MAX_SSID_LEN]; + /* Band to scan */ + u8 band; + + u8 scan_tag; + u8 padding2[2]; +} __packed; + +struct basic_scan_channel_params { + /* Duration in TU to wait for frames on a channel for active scan */ + __le32 min_duration; + __le32 max_duration; + __le32 bssid_lsb; + __le16 bssid_msb; + u8 early_termination; + u8 tx_power_att; + u8 channel; + /* FW internal use only! */ + u8 dfs_candidate; + u8 activity_detected; + u8 pad; +} __packed; + +struct wl1271_cmd_scan { + struct wl1271_cmd_header header; + + struct basic_scan_params params; + struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS]; + + /* src mac address */ + u8 addr[ETH_ALEN]; + u8 padding[2]; +} __packed; + +struct wl1271_cmd_sched_scan_config { + struct wl1271_cmd_header header; + + __le32 intervals[SCAN_MAX_CYCLE_INTERVALS]; + + s8 rssi_threshold; /* for filtering (in dBm) */ + s8 snr_threshold; /* for filtering (in dB) */ + + u8 cycles; /* maximum number of scan cycles */ + u8 report_after; /* report when this number of results are received */ + u8 terminate; /* stop scanning after reporting */ + + u8 tag; + u8 bss_type; /* for filtering */ + u8 filter_type; + + u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ + u8 ssid[IEEE80211_MAX_SSID_LEN]; + + u8 n_probe_reqs; /* Number of probes requests per channel */ + + u8 passive[SCAN_MAX_BANDS]; + u8 active[SCAN_MAX_BANDS]; + + u8 dfs; + + u8 n_pactive_ch; /* number of pactive (passive until fw detects energy) + channels in BG band */ + u8 role_id; + u8 padding[1]; + struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; + struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; + struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; +} __packed; + +struct wl1271_cmd_sched_scan_start { + struct wl1271_cmd_header header; + + u8 tag; + u8 role_id; + u8 padding[2]; +} __packed; + +struct wl1271_cmd_sched_scan_stop { + struct wl1271_cmd_header header; + + u8 tag; + u8 role_id; + u8 padding[2]; +} __packed; + +int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_scan_request *req); +int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); +void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif); +int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); +void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); +#endif diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile index 67c098734c7f..f74224960c70 100644 --- a/drivers/net/wireless/ti/wl18xx/Makefile +++ b/drivers/net/wireless/ti/wl18xx/Makefile @@ -1,3 +1,3 @@ -wl18xx-objs = main.o acx.o tx.o io.o debugfs.o +wl18xx-objs = main.o acx.o tx.o io.o debugfs.o scan.o obj-$(CONFIG_WL18XX) += wl18xx.o diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 8ba1c9387a14..97c23c8dd5e9 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -38,6 +38,7 @@ #include "tx.h" #include "wl18xx.h" #include "io.h" +#include "scan.h" #include "debugfs.h" #define WL18XX_RX_CHECKSUM_MASK 0x40 @@ -612,7 +613,8 @@ static int wl18xx_identify_chip(struct wl1271 *wl) WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN | - WLCORE_QUIRK_TX_PAD_LAST_FRAME; + WLCORE_QUIRK_TX_PAD_LAST_FRAME | + WLCORE_QUIRK_DUAL_PROBE_TMPL; wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER, WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER, @@ -630,6 +632,10 @@ static int wl18xx_identify_chip(struct wl1271 *wl) goto out; } + wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; + wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; + wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC; + wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC; out: return ret; } @@ -1320,6 +1326,11 @@ static struct wlcore_ops wl18xx_ops = { .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, .get_mac = wl18xx_get_mac, .debugfs_init = wl18xx_debugfs_add_files, + .scan_start = wl18xx_scan_start, + .scan_stop = wl18xx_scan_stop, + .scan_completed = wl18xx_scan_completed, + .sched_scan_start = wl18xx_sched_scan_start, + .sched_scan_stop = wl18xx_scan_sched_scan_stop, .handle_static_data = wl18xx_handle_static_data, .get_spare_blocks = wl18xx_get_spare_blocks, .set_key = wl18xx_set_key, diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c new file mode 100644 index 000000000000..f31d0d480d1b --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/scan.c @@ -0,0 +1,320 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include "scan.h" +#include "../wlcore/debug.h" + +static void wl18xx_adjust_channels(struct wl18xx_cmd_scan_params *cmd, + struct wlcore_scan_channels *cmd_channels) +{ + memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive)); + memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active)); + cmd->dfs = cmd_channels->dfs; + cmd->passive_active = cmd_channels->passive_active; + + memcpy(cmd->channels_2, cmd_channels->channels_2, + sizeof(cmd->channels_2)); + memcpy(cmd->channels_5, cmd_channels->channels_5, + sizeof(cmd->channels_2)); + /* channels_4 are not supported, so no need to copy them */ +} + +static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_scan_request *req) +{ + struct wl18xx_cmd_scan_params *cmd; + struct wlcore_scan_channels *cmd_channels = NULL; + int ret; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->role_id = wlvif->role_id; + + if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { + ret = -EINVAL; + goto out; + } + + cmd->scan_type = SCAN_TYPE_SEARCH; + cmd->rssi_threshold = -127; + cmd->snr_threshold = 0; + + cmd->bss_type = SCAN_BSS_TYPE_ANY; + + cmd->ssid_from_list = 0; + cmd->filter = 0; + cmd->add_broadcast = 0; + + cmd->urgency = 0; + cmd->protect = 0; + + cmd->n_probe_reqs = wl->conf.scan.num_probe_reqs; + cmd->terminate_after = 0; + + /* configure channels */ + WARN_ON(req->n_ssids > 1); + + cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); + if (!cmd_channels) { + ret = -ENOMEM; + goto out; + } + + wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, + req->n_channels, req->n_ssids); + wl18xx_adjust_channels(cmd, cmd_channels); + + /* + * all the cycles params (except total cycles) should + * remain 0 for normal scan + */ + cmd->total_cycles = 1; + + if (req->no_cck) + cmd->rate = WL18XX_SCAN_RATE_6; + + cmd->tag = WL1271_SCAN_DEFAULT_TAG; + + if (req->n_ssids) { + cmd->ssid_len = req->ssids[0].ssid_len; + memcpy(cmd->ssid, req->ssids[0].ssid, cmd->ssid_len); + } + + /* TODO: per-band ies? */ + if (cmd->active[0]) { + u8 band = IEEE80211_BAND_2GHZ; + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + cmd->role_id, band, + req->ssids[0].ssid, + req->ssids[0].ssid_len, + req->ie, + req->ie_len, + false); + if (ret < 0) { + wl1271_error("2.4GHz PROBE request template failed"); + goto out; + } + } + + if (cmd->active[1]) { + u8 band = IEEE80211_BAND_5GHZ; + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + cmd->role_id, band, + req->ssids[0].ssid, + req->ssids[0].ssid_len, + req->ie, + req->ie_len, + false); + if (ret < 0) { + wl1271_error("5GHz PROBE request template failed"); + goto out; + } + } + + wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); + + ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("SCAN failed"); + goto out; + } + +out: + kfree(cmd_channels); + kfree(cmd); + return ret; +} + +void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + wl->scan.failed = false; + cancel_delayed_work(&wl->scan_complete_work); + ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, + msecs_to_jiffies(0)); +} + +static +int wl18xx_scan_sched_scan_config(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct wl18xx_cmd_scan_params *cmd; + struct wlcore_scan_channels *cmd_channels = NULL; + struct conf_sched_scan_settings *c = &wl->conf.sched_scan; + int ret; + int filter_type; + + wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); + + filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); + if (filter_type < 0) + return filter_type; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->role_id = wlvif->role_id; + + if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { + ret = -EINVAL; + goto out; + } + + cmd->scan_type = SCAN_TYPE_PERIODIC; + cmd->rssi_threshold = c->rssi_threshold; + cmd->snr_threshold = c->snr_threshold; + + /* don't filter on BSS type */ + cmd->bss_type = SCAN_BSS_TYPE_ANY; + + cmd->ssid_from_list = 1; + if (filter_type == SCAN_SSID_FILTER_LIST) + cmd->filter = 1; + cmd->add_broadcast = 0; + + cmd->urgency = 0; + cmd->protect = 0; + + cmd->n_probe_reqs = c->num_probe_reqs; + /* don't stop scanning automatically when something is found */ + cmd->terminate_after = 0; + + cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); + if (!cmd_channels) { + ret = -ENOMEM; + goto out; + } + + /* configure channels */ + wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, + req->n_channels, req->n_ssids); + wl18xx_adjust_channels(cmd, cmd_channels); + + cmd->short_cycles_sec = 0; + cmd->long_cycles_sec = cpu_to_le16(req->interval); + cmd->short_cycles_count = 0; + + cmd->total_cycles = 0; + + cmd->tag = WL1271_SCAN_DEFAULT_TAG; + + if (cmd->active[0]) { + u8 band = IEEE80211_BAND_2GHZ; + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + cmd->role_id, band, + req->ssids[0].ssid, + req->ssids[0].ssid_len, + ies->ie[band], + ies->len[band], + true); + if (ret < 0) { + wl1271_error("2.4GHz PROBE request template failed"); + goto out; + } + } + + if (cmd->active[1]) { + u8 band = IEEE80211_BAND_5GHZ; + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + cmd->role_id, band, + req->ssids[0].ssid, + req->ssids[0].ssid_len, + ies->ie[band], + ies->len[band], + true); + if (ret < 0) { + wl1271_error("5GHz PROBE request template failed"); + goto out; + } + } + + wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); + + ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("SCAN failed"); + goto out; + } + +out: + kfree(cmd_channels); + kfree(cmd); + return ret; +} + +int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies); +} + +static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 scan_type) +{ + struct wl18xx_cmd_scan_stop *stop; + int ret; + + wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); + + stop = kzalloc(sizeof(*stop), GFP_KERNEL); + if (!stop) { + wl1271_error("failed to alloc memory to send sched scan stop"); + return -ENOMEM; + } + + stop->role_id = wlvif->role_id; + stop->scan_type = scan_type; + + ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0); + if (ret < 0) { + wl1271_error("failed to send sched scan stop command"); + goto out_free; + } + +out_free: + kfree(stop); + return ret; +} + +void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC); +} +int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_scan_request *req) +{ + return wl18xx_scan_send(wl, wlvif, req); +} + +int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH); +} diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h new file mode 100644 index 000000000000..404baf17aa28 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/scan.h @@ -0,0 +1,120 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_SCAN_H__ +#define __WL18XX_SCAN_H__ + +#include "../wlcore/wlcore.h" +#include "../wlcore/cmd.h" +#include "../wlcore/scan.h" + +struct tracking_ch_params { + struct conn_scan_ch_params channel; + + __le32 bssid_lsb; + __le16 bssid_msb; + + u8 padding[2]; +} __packed; + +enum +{ + SCAN_TYPE_SEARCH = 0, + SCAN_TYPE_PERIODIC = 1, + SCAN_TYPE_TRACKING = 2, +}; + +/* probe request rate */ +enum +{ + WL18XX_SCAN_RATE_1 = 0, + WL18XX_SCAN_RATE_5_5 = 1, + WL18XX_SCAN_RATE_6 = 2, +}; + +struct wl18xx_cmd_scan_params { + struct wl1271_cmd_header header; + + u8 role_id; + u8 scan_type; + + s8 rssi_threshold; /* for filtering (in dBm) */ + s8 snr_threshold; /* for filtering (in dB) */ + + u8 bss_type; /* for filtering */ + u8 ssid_from_list; /* use ssid from configured ssid list */ + u8 filter; /* forward only results with matching ssids */ + + /* + * add broadcast ssid in addition to the configured ssids. + * the driver should add dummy entry for it (?). + */ + u8 add_broadcast; + + u8 urgency; + u8 protect; /* ??? */ + u8 n_probe_reqs; /* Number of probes requests per channel */ + u8 terminate_after; /* early terminate scan operation */ + + u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */ + u8 active[SCAN_MAX_BANDS]; /* number of active scan channels */ + u8 dfs; /* number of dfs channels in 5ghz */ + u8 passive_active; /* number of passive before active channels 2.4ghz */ + + __le16 short_cycles_sec; + __le16 long_cycles_sec; + u8 short_cycles_count; + u8 total_cycles; /* 0 - infinite */ + u8 rate; + u8 padding[1]; + + union { + struct { + struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; + struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; + struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; + }; + struct tracking_ch_params channels_tracking[WL1271_SCAN_MAX_CHANNELS]; + } ; + + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ + u8 tag; + u8 padding1[2]; +} __packed; + +struct wl18xx_cmd_scan_stop { + struct wl1271_cmd_header header; + + u8 role_id; + u8 scan_type; + u8 padding[2]; +} __packed; + +int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_scan_request *req); +int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); +void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif); +int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); +void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); +#endif diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 04ba86d2d875..c8c776684d2c 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -131,6 +131,7 @@ fail: wl12xx_queue_recovery_work(wl); return ret; } +EXPORT_SYMBOL_GPL(wl1271_cmd_send); /* * Poll the mailbox event field until any of the bits in the mask is set or a @@ -1049,8 +1050,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb; int ret; u32 rate; - u16 template_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; - u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; + u16 template_id_2_4 = wl->scan_templ_id_2_4; + u16 template_id_5 = wl->scan_templ_id_5; skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, ie, ie_len); @@ -1061,10 +1062,10 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); - if (!sched_scan && + if (sched_scan && (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) { - template_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4; - template_id_5 = CMD_TEMPL_APP_PROBE_REQ_5; + template_id_2_4 = wl->sched_scan_templ_id_2_4; + template_id_5 = wl->sched_scan_templ_id_5; } rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); @@ -1081,6 +1082,7 @@ out: dev_kfree_skb(skb); return ret; } +EXPORT_SYMBOL_GPL(wl12xx_cmd_build_probe_req); struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index d021305c1ff9..9e9062fdb712 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -172,8 +172,8 @@ enum cmd_templ { CMD_TEMPL_PS_POLL, CMD_TEMPL_KLV, CMD_TEMPL_DISCONNECT, - CMD_TEMPL_APP_PROBE_REQ_2_4, - CMD_TEMPL_APP_PROBE_REQ_5, + CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY, + CMD_TEMPL_APP_PROBE_REQ_5_LEGACY, CMD_TEMPL_BAR, /* for firmware internal use only */ CMD_TEMPL_CTS, /* * For CTS-to-self (FastCTS) mechanism @@ -184,6 +184,8 @@ enum cmd_templ { CMD_TEMPL_DEAUTH_AP, CMD_TEMPL_TEMPORARY, CMD_TEMPL_LINK_MEASUREMENT_REPORT, + CMD_TEMPL_PROBE_REQ_2_4_PERIODIC, + CMD_TEMPL_PROBE_REQ_5_PERIODIC, CMD_TEMPL_MAX = 0xff }; diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index cf45aaf33bc8..d9353dad08d9 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -117,7 +117,9 @@ static int wl1271_event_process(struct wl1271 *wl) wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); - wl1271_scan_stm(wl, wl->scan_vif); + if (wl->scan_vif) + wl->ops->scan_completed(wl, + wl12xx_vif_to_data(wl->scan_vif)); } if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 84641b3c1b80..06e1bf98f44e 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -41,14 +41,14 @@ int wl1271_init_templates_config(struct wl1271 *wl) /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, - CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, + wl->scan_templ_id_2_4, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, - CMD_TEMPL_CFG_PROBE_REQ_5, + wl->scan_templ_id_5, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) @@ -56,14 +56,16 @@ int wl1271_init_templates_config(struct wl1271 *wl) if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) { ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, - CMD_TEMPL_APP_PROBE_REQ_2_4, NULL, + wl->sched_scan_templ_id_2_4, + NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, - CMD_TEMPL_APP_PROBE_REQ_5, NULL, + wl->sched_scan_templ_id_5, + NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 111b84fcf66e..0f7a9338d10b 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3252,7 +3252,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, goto out_sleep; } - ret = wl1271_scan(hw->priv, vif, ssid, len, req); + ret = wlcore_scan(hw->priv, vif, ssid, len, req); out_sleep: wl1271_ps_elp_sleep(wl); out: @@ -3265,6 +3265,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan"); @@ -3282,7 +3283,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, goto out; if (wl->scan.state != WL1271_SCAN_STATE_DONE) { - ret = wl1271_scan_stop(wl); + ret = wl->ops->scan_stop(wl, wlvif); if (ret < 0) goto out_sleep; } @@ -3329,11 +3330,7 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, if (ret < 0) goto out; - ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies); - if (ret < 0) - goto out_sleep; - - ret = wl1271_scan_sched_scan_start(wl, wlvif); + ret = wl->ops->sched_scan_start(wl, wlvif, req, ies); if (ret < 0) goto out_sleep; @@ -3364,7 +3361,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, if (ret < 0) goto out; - wl1271_scan_sched_scan_stop(wl, wlvif); + wl->ops->sched_scan_stop(wl, wlvif); wl1271_ps_elp_sleep(wl); out: @@ -3821,7 +3818,7 @@ static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif, /* we only support sched_scan while not connected */ if (wl->sched_scanning) - wl1271_scan_sched_scan_stop(wl, wlvif); + wl->ops->sched_scan_stop(wl, wlvif); ret = wl1271_acx_sta_rate_policies(wl, wlvif); if (ret < 0) diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index 22dd7e94f40f..9eab64d1679b 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -89,319 +89,6 @@ out: } - -static int wl1271_get_scan_channels(struct wl1271 *wl, - struct cfg80211_scan_request *req, - struct basic_scan_channel_params *channels, - enum ieee80211_band band, bool passive) -{ - struct conf_scan_settings *c = &wl->conf.scan; - int i, j; - u32 flags; - - for (i = 0, j = 0; - i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; - i++) { - flags = req->channels[i]->flags; - - if (!test_bit(i, wl->scan.scanned_ch) && - !(flags & IEEE80211_CHAN_DISABLED) && - (req->channels[i]->band == band) && - /* - * In passive scans, we scan all remaining - * channels, even if not marked as such. - * In active scans, we only scan channels not - * marked as passive. - */ - (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) { - wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", - req->channels[i]->band, - req->channels[i]->center_freq); - wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", - req->channels[i]->hw_value, - req->channels[i]->flags); - wl1271_debug(DEBUG_SCAN, - "max_antenna_gain %d, max_power %d", - req->channels[i]->max_antenna_gain, - req->channels[i]->max_power); - wl1271_debug(DEBUG_SCAN, "beacon_found %d", - req->channels[i]->beacon_found); - - if (!passive) { - channels[j].min_duration = - cpu_to_le32(c->min_dwell_time_active); - channels[j].max_duration = - cpu_to_le32(c->max_dwell_time_active); - } else { - channels[j].min_duration = - cpu_to_le32(c->min_dwell_time_passive); - channels[j].max_duration = - cpu_to_le32(c->max_dwell_time_passive); - } - channels[j].early_termination = 0; - channels[j].tx_power_att = req->channels[i]->max_power; - channels[j].channel = req->channels[i]->hw_value; - - memset(&channels[j].bssid_lsb, 0xff, 4); - memset(&channels[j].bssid_msb, 0xff, 2); - - /* Mark the channels we already used */ - set_bit(i, wl->scan.scanned_ch); - - j++; - } - } - - return j; -} - -#define WL1271_NOTHING_TO_SCAN 1 - -static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, - enum ieee80211_band band, - bool passive, u32 basic_rate) -{ - struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - struct wl1271_cmd_scan *cmd; - struct wl1271_cmd_trigger_scan_to *trigger; - int ret; - u16 scan_options = 0; - - /* skip active scans if we don't have SSIDs */ - if (!passive && wl->scan.req->n_ssids == 0) - return WL1271_NOTHING_TO_SCAN; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); - if (!cmd || !trigger) { - ret = -ENOMEM; - goto out; - } - - if (wl->conf.scan.split_scan_timeout) - scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; - - if (passive) - scan_options |= WL1271_SCAN_OPT_PASSIVE; - - cmd->params.role_id = wlvif->role_id; - - if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) { - ret = -EINVAL; - goto out; - } - - cmd->params.scan_options = cpu_to_le16(scan_options); - - cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, - cmd->channels, - band, passive); - if (cmd->params.n_ch == 0) { - ret = WL1271_NOTHING_TO_SCAN; - goto out; - } - - cmd->params.tx_rate = cpu_to_le32(basic_rate); - cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; - cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; - cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; - - if (band == IEEE80211_BAND_2GHZ) - cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ; - else - cmd->params.band = WL1271_SCAN_BAND_5_GHZ; - - if (wl->scan.ssid_len && wl->scan.ssid) { - cmd->params.ssid_len = wl->scan.ssid_len; - memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len); - } - - memcpy(cmd->addr, vif->addr, ETH_ALEN); - - ret = wl12xx_cmd_build_probe_req(wl, wlvif, - cmd->params.role_id, band, - wl->scan.ssid, wl->scan.ssid_len, - wl->scan.req->ie, - wl->scan.req->ie_len, false); - if (ret < 0) { - wl1271_error("PROBE request template failed"); - goto out; - } - - trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); - ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, - sizeof(*trigger), 0); - if (ret < 0) { - wl1271_error("trigger scan to failed for hw scan"); - goto out; - } - - wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); - - ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); - if (ret < 0) { - wl1271_error("SCAN failed"); - goto out; - } - -out: - kfree(cmd); - kfree(trigger); - return ret; -} - -void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif) -{ - struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - int ret = 0; - enum ieee80211_band band; - u32 rate, mask; - - switch (wl->scan.state) { - case WL1271_SCAN_STATE_IDLE: - break; - - case WL1271_SCAN_STATE_2GHZ_ACTIVE: - band = IEEE80211_BAND_2GHZ; - mask = wlvif->bitrate_masks[band]; - if (wl->scan.req->no_cck) { - mask &= ~CONF_TX_CCK_RATES; - if (!mask) - mask = CONF_TX_RATE_MASK_BASIC_P2P; - } - rate = wl1271_tx_min_rate_get(wl, mask); - ret = wl1271_scan_send(wl, vif, band, false, rate); - if (ret == WL1271_NOTHING_TO_SCAN) { - wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; - wl1271_scan_stm(wl, vif); - } - - break; - - case WL1271_SCAN_STATE_2GHZ_PASSIVE: - band = IEEE80211_BAND_2GHZ; - mask = wlvif->bitrate_masks[band]; - if (wl->scan.req->no_cck) { - mask &= ~CONF_TX_CCK_RATES; - if (!mask) - mask = CONF_TX_RATE_MASK_BASIC_P2P; - } - rate = wl1271_tx_min_rate_get(wl, mask); - ret = wl1271_scan_send(wl, vif, band, true, rate); - if (ret == WL1271_NOTHING_TO_SCAN) { - if (wl->enable_11a) - wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; - else - wl->scan.state = WL1271_SCAN_STATE_DONE; - wl1271_scan_stm(wl, vif); - } - - break; - - case WL1271_SCAN_STATE_5GHZ_ACTIVE: - band = IEEE80211_BAND_5GHZ; - rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); - ret = wl1271_scan_send(wl, vif, band, false, rate); - if (ret == WL1271_NOTHING_TO_SCAN) { - wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; - wl1271_scan_stm(wl, vif); - } - - break; - - case WL1271_SCAN_STATE_5GHZ_PASSIVE: - band = IEEE80211_BAND_5GHZ; - rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); - ret = wl1271_scan_send(wl, vif, band, true, rate); - if (ret == WL1271_NOTHING_TO_SCAN) { - wl->scan.state = WL1271_SCAN_STATE_DONE; - wl1271_scan_stm(wl, vif); - } - - break; - - case WL1271_SCAN_STATE_DONE: - wl->scan.failed = false; - cancel_delayed_work(&wl->scan_complete_work); - ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, - msecs_to_jiffies(0)); - break; - - default: - wl1271_error("invalid scan state"); - break; - } - - if (ret < 0) { - cancel_delayed_work(&wl->scan_complete_work); - ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, - msecs_to_jiffies(0)); - } -} - -int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif, - const u8 *ssid, size_t ssid_len, - struct cfg80211_scan_request *req) -{ - /* - * cfg80211 should guarantee that we don't get more channels - * than what we have registered. - */ - BUG_ON(req->n_channels > WL1271_MAX_CHANNELS); - - if (wl->scan.state != WL1271_SCAN_STATE_IDLE) - return -EBUSY; - - wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE; - - if (ssid_len && ssid) { - wl->scan.ssid_len = ssid_len; - memcpy(wl->scan.ssid, ssid, ssid_len); - } else { - wl->scan.ssid_len = 0; - } - - wl->scan_vif = vif; - wl->scan.req = req; - memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); - - /* we assume failure so that timeout scenarios are handled correctly */ - wl->scan.failed = true; - ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, - msecs_to_jiffies(WL1271_SCAN_TIMEOUT)); - - wl1271_scan_stm(wl, vif); - - return 0; -} - -int wl1271_scan_stop(struct wl1271 *wl) -{ - struct wl1271_cmd_header *cmd = NULL; - int ret = 0; - - if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE)) - return -EINVAL; - - wl1271_debug(DEBUG_CMD, "cmd scan stop"); - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } - - ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd, - sizeof(*cmd), 0); - if (ret < 0) { - wl1271_error("cmd stop_scan failed"); - goto out; - } -out: - kfree(cmd); - return ret; -} - static int wlcore_scan_get_channels(struct wl1271 *wl, struct ieee80211_channel *req_channels[], @@ -503,9 +190,9 @@ wlcore_scan_get_channels(struct wl1271 *wl, return j - start; } -static bool +bool wlcore_set_scan_chan_params(struct wl1271 *wl, - struct wl1271_cmd_sched_scan_config *cfg, + struct wlcore_scan_channels *cfg, struct ieee80211_channel *channels[], u32 n_channels, u32 n_ssids) @@ -570,7 +257,7 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, cfg->passive[2] = 0; cfg->active[2] = 0; - cfg->n_pactive_ch = n_pactive_ch; + cfg->passive_active = n_pactive_ch; wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", cfg->active[0], cfg->passive[0]); @@ -582,10 +269,48 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, cfg->passive[1] || cfg->active[1] || cfg->dfs || cfg->passive[2] || cfg->active[2]; } +EXPORT_SYMBOL_GPL(wlcore_set_scan_chan_params); +int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif, + const u8 *ssid, size_t ssid_len, + struct cfg80211_scan_request *req) +{ + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + + /* + * cfg80211 should guarantee that we don't get more channels + * than what we have registered. + */ + BUG_ON(req->n_channels > WL1271_MAX_CHANNELS); + + if (wl->scan.state != WL1271_SCAN_STATE_IDLE) + return -EBUSY; + + wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE; + + if (ssid_len && ssid) { + wl->scan.ssid_len = ssid_len; + memcpy(wl->scan.ssid, ssid, ssid_len); + } else { + wl->scan.ssid_len = 0; + } + + wl->scan_vif = vif; + wl->scan.req = req; + memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); + + /* we assume failure so that timeout scenarios are handled correctly */ + wl->scan.failed = true; + ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, + msecs_to_jiffies(WL1271_SCAN_TIMEOUT)); + + wl->ops->scan_start(wl, wlvif, req); + + return 0; +} /* Returns the scan type to be used or a negative value on error */ -static int -wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, +int +wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req) { @@ -688,129 +413,7 @@ out: return ret; return type; } - -int wl1271_scan_sched_scan_config(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) -{ - struct wl1271_cmd_sched_scan_config *cfg = NULL; - struct conf_sched_scan_settings *c = &wl->conf.sched_scan; - int i, ret; - bool force_passive = !req->n_ssids; - - wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); - - cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); - if (!cfg) - return -ENOMEM; - - cfg->role_id = wlvif->role_id; - cfg->rssi_threshold = c->rssi_threshold; - cfg->snr_threshold = c->snr_threshold; - cfg->n_probe_reqs = c->num_probe_reqs; - /* cycles set to 0 it means infinite (until manually stopped) */ - cfg->cycles = 0; - /* report APs when at least 1 is found */ - cfg->report_after = 1; - /* don't stop scanning automatically when something is found */ - cfg->terminate = 0; - cfg->tag = WL1271_SCAN_DEFAULT_TAG; - /* don't filter on BSS type */ - cfg->bss_type = SCAN_BSS_TYPE_ANY; - /* currently NL80211 supports only a single interval */ - for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) - cfg->intervals[i] = cpu_to_le32(req->interval); - - cfg->ssid_len = 0; - ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req); - if (ret < 0) - goto out; - - cfg->filter_type = ret; - - wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); - - if (!wlcore_set_scan_chan_params(wl, cfg, req->channels, - req->n_channels, req->n_ssids)) { - wl1271_error("scan channel list is empty"); - ret = -EINVAL; - goto out; - } - - if (!force_passive && cfg->active[0]) { - u8 band = IEEE80211_BAND_2GHZ; - ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, - req->ssids[0].ssid, - req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], true); - if (ret < 0) { - wl1271_error("2.4GHz PROBE request template failed"); - goto out; - } - } - - if (!force_passive && cfg->active[1]) { - u8 band = IEEE80211_BAND_5GHZ; - ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, - req->ssids[0].ssid, - req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], true); - if (ret < 0) { - wl1271_error("5GHz PROBE request template failed"); - goto out; - } - } - - wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); - - ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, - sizeof(*cfg), 0); - if (ret < 0) { - wl1271_error("SCAN configuration failed"); - goto out; - } -out: - kfree(cfg); - return ret; -} - -int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif) -{ - struct wl1271_cmd_sched_scan_start *start; - int ret = 0; - - wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); - - if (wlvif->bss_type != BSS_TYPE_STA_BSS) - return -EOPNOTSUPP; - - if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) && - test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) - return -EBUSY; - - start = kzalloc(sizeof(*start), GFP_KERNEL); - if (!start) - return -ENOMEM; - - start->role_id = wlvif->role_id; - start->tag = WL1271_SCAN_DEFAULT_TAG; - - ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, - sizeof(*start), 0); - if (ret < 0) { - wl1271_error("failed to send scan start command"); - goto out_free; - } - -out_free: - kfree(start); - return ret; -} +EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list); void wl1271_scan_sched_scan_results(struct wl1271 *wl) { @@ -818,31 +421,3 @@ void wl1271_scan_sched_scan_results(struct wl1271 *wl) ieee80211_sched_scan_results(wl->hw); } - -void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) -{ - struct wl1271_cmd_sched_scan_stop *stop; - int ret = 0; - - wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); - - /* FIXME: what to do if alloc'ing to stop fails? */ - stop = kzalloc(sizeof(*stop), GFP_KERNEL); - if (!stop) { - wl1271_error("failed to alloc memory to send sched scan stop"); - return; - } - - stop->role_id = wlvif->role_id; - stop->tag = WL1271_SCAN_DEFAULT_TAG; - - ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, - sizeof(*stop), 0); - if (ret < 0) { - wl1271_error("failed to send sched scan stop command"); - goto out_free; - } - -out_free: - kfree(stop); -} diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index 29f3c8d6b046..25b0422ec989 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -26,21 +26,19 @@ #include "wlcore.h" -int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif, +int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif, const u8 *ssid, size_t ssid_len, struct cfg80211_scan_request *req); -int wl1271_scan_stop(struct wl1271 *wl); int wl1271_scan_build_probe_req(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u8 band); -void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif); +void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl1271_scan_complete_work(struct work_struct *work); int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies); int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif); -void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl1271_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_MAX_CHANNELS 24 @@ -66,56 +64,6 @@ enum { WL1271_SCAN_STATE_DONE }; -struct basic_scan_params { - /* Scan option flags (WL1271_SCAN_OPT_*) */ - __le16 scan_options; - u8 role_id; - /* Number of scan channels in the list (maximum 30) */ - u8 n_ch; - /* This field indicates the number of probe requests to send - per channel for an active scan */ - u8 n_probe_reqs; - u8 tid_trigger; - u8 ssid_len; - u8 use_ssid_list; - - /* Rate bit field for sending the probes */ - __le32 tx_rate; - - u8 ssid[IEEE80211_MAX_SSID_LEN]; - /* Band to scan */ - u8 band; - - u8 scan_tag; - u8 padding2[2]; -} __packed; - -struct basic_scan_channel_params { - /* Duration in TU to wait for frames on a channel for active scan */ - __le32 min_duration; - __le32 max_duration; - __le32 bssid_lsb; - __le16 bssid_msb; - u8 early_termination; - u8 tx_power_att; - u8 channel; - /* FW internal use only! */ - u8 dfs_candidate; - u8 activity_detected; - u8 pad; -} __packed; - -struct wl1271_cmd_scan { - struct wl1271_cmd_header header; - - struct basic_scan_params params; - struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS]; - - /* src mac address */ - u8 addr[ETH_ALEN]; - u8 padding[2]; -} __packed; - struct wl1271_cmd_trigger_scan_to { struct wl1271_cmd_header header; @@ -160,43 +108,6 @@ struct conn_scan_ch_params { u8 padding[3]; } __packed; -struct wl1271_cmd_sched_scan_config { - struct wl1271_cmd_header header; - - __le32 intervals[SCAN_MAX_CYCLE_INTERVALS]; - - s8 rssi_threshold; /* for filtering (in dBm) */ - s8 snr_threshold; /* for filtering (in dB) */ - - u8 cycles; /* maximum number of scan cycles */ - u8 report_after; /* report when this number of results are received */ - u8 terminate; /* stop scanning after reporting */ - - u8 tag; - u8 bss_type; /* for filtering */ - u8 filter_type; - - u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ - u8 ssid[IEEE80211_MAX_SSID_LEN]; - - u8 n_probe_reqs; /* Number of probes requests per channel */ - - u8 passive[SCAN_MAX_BANDS]; - u8 active[SCAN_MAX_BANDS]; - - u8 dfs; - - u8 n_pactive_ch; /* number of pactive (passive until fw detects energy) - channels in BG band */ - u8 role_id; - u8 padding[1]; - - struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; - struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; - struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; -} __packed; - - #define SCHED_SCAN_MAX_SSIDS 16 enum { @@ -220,21 +131,27 @@ struct wl1271_cmd_sched_scan_ssid_list { u8 padding[2]; } __packed; -struct wl1271_cmd_sched_scan_start { - struct wl1271_cmd_header header; +struct wlcore_scan_channels { + u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */ + u8 active[SCAN_MAX_BANDS]; /* number of active scan channels */ + u8 dfs; /* number of dfs channels in 5ghz */ + u8 passive_active; /* number of passive before active channels 2.4ghz */ - u8 tag; - u8 role_id; - u8 padding[2]; -} __packed; + struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; + struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; + struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; +}; -struct wl1271_cmd_sched_scan_stop { - struct wl1271_cmd_header header; - - u8 tag; - u8 role_id; - u8 padding[2]; -} __packed; +bool +wlcore_set_scan_chan_params(struct wl1271 *wl, + struct wlcore_scan_channels *cfg, + struct ieee80211_channel *channels[], + u32 n_channels, + u32 n_ssids); +int +wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req); #endif /* __WL1271_SCAN_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index cf6dbee309a4..2844f305a489 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -1135,6 +1135,7 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) return BIT(__ffs(rate_set)); } +EXPORT_SYMBOL_GPL(wl1271_tx_min_rate_get); void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, enum wlcore_queue_stop_reason reason) diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index b636f1db562b..9f82ee3a4a3a 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -82,6 +82,14 @@ struct wlcore_ops { int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir); int (*handle_static_data)(struct wl1271 *wl, struct wl1271_static_data *static_data); + int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_scan_request *req); + int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); + void (*scan_completed)(struct wl1271 *wl, struct wl12xx_vif *wlvif); + int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); + void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem); int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, @@ -370,6 +378,11 @@ struct wl1271 { const char *sr_fw_name; const char *mr_fw_name; + u8 scan_templ_id_2_4; + u8 scan_templ_id_5; + u8 sched_scan_templ_id_2_4; + u8 sched_scan_templ_id_5; + /* per-chip-family private structure */ void *priv; From 0a1c720c63be74bbfe416fa085d0783348e4d69b Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:16 +0200 Subject: [PATCH 028/478] wl18xx: increase MAX_CHANNELS_5GHZ Some regdomains have more than 23 valid 5ghz channels, so 18xx's MAX_CHANNELS_5GHZ was increased to 32. Since now we have different max 5ghz channels values for wl12xx and wl18xx, add a new wl->max_channels_5ghz field, and use it for scan channels configuration. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 1 + drivers/net/wireless/ti/wl12xx/scan.h | 4 +++- drivers/net/wireless/ti/wl18xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/scan.h | 4 +++- drivers/net/wireless/ti/wlcore/scan.c | 6 +++--- drivers/net/wireless/ti/wlcore/scan.h | 10 +++++++++- drivers/net/wireless/ti/wlcore/wlcore.h | 1 + 7 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index ada7031777cc..0c81eeb4f5b8 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -704,6 +704,7 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY; wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; + wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ; out: return ret; } diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h index bd075dec5946..264af7ac2785 100644 --- a/drivers/net/wireless/ti/wl12xx/scan.h +++ b/drivers/net/wireless/ti/wl12xx/scan.h @@ -26,6 +26,8 @@ #include "../wlcore/cmd.h" #include "../wlcore/scan.h" +#define WL12XX_MAX_CHANNELS_5GHZ 23 + struct basic_scan_params { /* Scan option flags (WL1271_SCAN_OPT_*) */ __le16 scan_options; @@ -107,7 +109,7 @@ struct wl1271_cmd_sched_scan_config { u8 role_id; u8 padding[1]; struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; - struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; + struct conn_scan_ch_params channels_5[WL12XX_MAX_CHANNELS_5GHZ]; struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; } __packed; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 97c23c8dd5e9..e81b3518d288 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -636,6 +636,7 @@ static int wl18xx_identify_chip(struct wl1271 *wl) wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC; wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC; + wl->max_channels_5 = WL18XX_MAX_CHANNELS_5GHZ; out: return ret; } diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h index 404baf17aa28..0e026ecb311e 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.h +++ b/drivers/net/wireless/ti/wl18xx/scan.h @@ -50,6 +50,8 @@ enum WL18XX_SCAN_RATE_6 = 2, }; +#define WL18XX_MAX_CHANNELS_5GHZ 32 + struct wl18xx_cmd_scan_params { struct wl1271_cmd_header header; @@ -89,7 +91,7 @@ struct wl18xx_cmd_scan_params { union { struct { struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; - struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; + struct conn_scan_ch_params channels_5[WL18XX_MAX_CHANNELS_5GHZ]; struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; }; struct tracking_ch_params channels_tracking[WL1271_SCAN_MAX_CHANNELS]; diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index 9eab64d1679b..e7d0a02be5b0 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -228,7 +228,7 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, cfg->channels_5, IEEE80211_BAND_5GHZ, false, true, 0, - MAX_CHANNELS_5GHZ, + wl->max_channels_5, &n_pactive_ch); cfg->dfs = wlcore_scan_get_channels(wl, @@ -239,7 +239,7 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, IEEE80211_BAND_5GHZ, true, true, cfg->passive[1], - MAX_CHANNELS_5GHZ, + wl->max_channels_5, &n_pactive_ch); cfg->active[1] = wlcore_scan_get_channels(wl, @@ -250,7 +250,7 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, IEEE80211_BAND_5GHZ, false, false, cfg->passive[1] + cfg->dfs, - MAX_CHANNELS_5GHZ, + wl->max_channels_5, &n_pactive_ch); /* 802.11j channels are not supported yet */ diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index 25b0422ec989..8465f35c6a06 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -71,9 +71,17 @@ struct wl1271_cmd_trigger_scan_to { } __packed; #define MAX_CHANNELS_2GHZ 14 -#define MAX_CHANNELS_5GHZ 23 #define MAX_CHANNELS_4GHZ 4 +/* + * This max value here is used only for the struct definition of + * wlcore_scan_channels. This struct is used by both 12xx + * and 18xx (which have different max 5ghz channels value). + * In order to make sure this is large enough, just use the + * max possible 5ghz channels. + */ +#define MAX_CHANNELS_5GHZ 42 + #define SCAN_MAX_CYCLE_INTERVALS 16 #define SCAN_MAX_BANDS 3 diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 9f82ee3a4a3a..36fde4e01969 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -382,6 +382,7 @@ struct wl1271 { u8 scan_templ_id_5; u8 sched_scan_templ_id_2_4; u8 sched_scan_templ_id_5; + u8 max_channels_5; /* per-chip-family private structure */ void *priv; From b6acb4e00e187cb5ae8dd479958a02fe0ea97bf0 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:17 +0200 Subject: [PATCH 029/478] wlcore: update acx enum update the acx enum to the new fw api. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/acx.c | 2 +- drivers/net/wireless/ti/wl18xx/acx.h | 8 +++++++- drivers/net/wireless/ti/wlcore/acx.h | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index 72840e23bf59..adff9cde7db7 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -75,7 +75,7 @@ int wl18xx_acx_set_checksum_state(struct wl1271 *wl) acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED; - ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx)); + ret = wl1271_cmd_configure(wl, ACX_CSUM_CONFIG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("failed to set Tx checksum state: %d", ret); goto out; diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index e2609a6b7341..394d12500834 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -26,7 +26,13 @@ #include "../wlcore/acx.h" enum { - ACX_CLEAR_STATISTICS = 0x0047, + ACX_NS_IPV6_FILTER = 0x0050, + ACX_PEER_HT_OPERATION_MODE_CFG = 0x0051, + ACX_CSUM_CONFIG = 0x0052, + ACX_SIM_CONFIG = 0x0053, + ACX_CLEAR_STATISTICS = 0x0054, + ACX_AUTO_RX_STREAMING = 0x0055, + ACX_PEER_CAP = 0x0056 }; /* numbers of bits the length field takes (add 1 for the actual number) */ diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index d03215d6b3bd..126536c6a393 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -1025,7 +1025,6 @@ enum { ACX_CONFIG_HANGOVER = 0x0042, ACX_FEATURE_CFG = 0x0043, ACX_PROTECTION_CFG = 0x0044, - ACX_CHECKSUM_CONFIG = 0x0045, }; From fcab189027cdd68df7f97474d1419aaa4a82130c Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:18 +0200 Subject: [PATCH 030/478] wlcore: update channel_switch/stop_channel_switch commands Some fields were added to the channel_switch and stop_channel_switch commands. Unfortunately, the new 18xx channel_switch struct is not backward compatible with the 12xx channel switch struct. Add a new channel_switch op to wlcore, and update the driver accordingly. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/cmd.c | 37 ++++++++++++ drivers/net/wireless/ti/wl12xx/cmd.h | 20 +++++++ drivers/net/wireless/ti/wl12xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/Makefile | 2 +- drivers/net/wireless/ti/wl18xx/cmd.c | 80 +++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/cmd.h | 52 ++++++++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 2 + drivers/net/wireless/ti/wlcore/cmd.c | 40 +------------ drivers/net/wireless/ti/wlcore/cmd.h | 19 +----- drivers/net/wireless/ti/wlcore/main.c | 5 +- drivers/net/wireless/ti/wlcore/wlcore.h | 3 + 11 files changed, 204 insertions(+), 57 deletions(-) create mode 100644 drivers/net/wireless/ti/wl18xx/cmd.c create mode 100644 drivers/net/wireless/ti/wl18xx/cmd.h diff --git a/drivers/net/wireless/ti/wl12xx/cmd.c b/drivers/net/wireless/ti/wl12xx/cmd.c index 622206241e83..7dc9f965037d 100644 --- a/drivers/net/wireless/ti/wl12xx/cmd.c +++ b/drivers/net/wireless/ti/wl12xx/cmd.c @@ -284,3 +284,40 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl) kfree(radio_parms); return ret; } + +int wl12xx_cmd_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_channel_switch *ch_switch) +{ + struct wl12xx_cmd_channel_switch *cmd; + int ret; + + wl1271_debug(DEBUG_ACX, "cmd channel switch"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->role_id = wlvif->role_id; + cmd->channel = ch_switch->channel->hw_value; + cmd->switch_time = ch_switch->count; + cmd->stop_tx = ch_switch->block_tx; + + /* FIXME: control from mac80211 in the future */ + /* Enable TX on the target channel */ + cmd->post_switch_tx_disable = 0; + + ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send channel switch command"); + goto out_free; + } + +out_free: + kfree(cmd); + +out: + return ret; +} diff --git a/drivers/net/wireless/ti/wl12xx/cmd.h b/drivers/net/wireless/ti/wl12xx/cmd.h index 140a0e8829d5..32cbad54e993 100644 --- a/drivers/net/wireless/ti/wl12xx/cmd.h +++ b/drivers/net/wireless/ti/wl12xx/cmd.h @@ -103,10 +103,30 @@ struct wl1271_ext_radio_parms_cmd { u8 padding[3]; } __packed; +struct wl12xx_cmd_channel_switch { + struct wl1271_cmd_header header; + + u8 role_id; + + /* The new serving channel */ + u8 channel; + /* Relative time of the serving channel switch in TBTT units */ + u8 switch_time; + /* Stop the role TX, should expect it after radar detection */ + u8 stop_tx; + /* The target channel tx status 1-stopped 0-open*/ + u8 post_switch_tx_disable; + + u8 padding[3]; +} __packed; + int wl1271_cmd_general_parms(struct wl1271 *wl); int wl128x_cmd_general_parms(struct wl1271 *wl); int wl1271_cmd_radio_parms(struct wl1271 *wl); int wl128x_cmd_radio_parms(struct wl1271 *wl); int wl1271_cmd_ext_radio_parms(struct wl1271 *wl); +int wl12xx_cmd_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_channel_switch *ch_switch); #endif /* __WL12XX_CMD_H__ */ diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 0c81eeb4f5b8..b93de04ccd6a 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1632,6 +1632,7 @@ static struct wlcore_ops wl12xx_ops = { .sched_scan_stop = wl12xx_scan_sched_scan_stop, .get_spare_blocks = wl12xx_get_spare_blocks, .set_key = wl12xx_set_key, + .channel_switch = wl12xx_cmd_channel_switch, .pre_pkt_send = NULL, }; diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile index f74224960c70..81a864f3d793 100644 --- a/drivers/net/wireless/ti/wl18xx/Makefile +++ b/drivers/net/wireless/ti/wl18xx/Makefile @@ -1,3 +1,3 @@ -wl18xx-objs = main.o acx.o tx.o io.o debugfs.o scan.o +wl18xx-objs = main.o acx.o tx.o io.o debugfs.o scan.o cmd.o obj-$(CONFIG_WL18XX) += wl18xx.o diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c new file mode 100644 index 000000000000..1d1f6cc7a50a --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/cmd.c @@ -0,0 +1,80 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "../wlcore/cmd.h" +#include "../wlcore/debug.h" +#include "../wlcore/hw_ops.h" + +#include "cmd.h" + +int wl18xx_cmd_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_channel_switch *ch_switch) +{ + struct wl18xx_cmd_channel_switch *cmd; + u32 supported_rates; + int ret; + + wl1271_debug(DEBUG_ACX, "cmd channel switch"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->role_id = wlvif->role_id; + cmd->channel = ch_switch->channel->hw_value; + cmd->switch_time = ch_switch->count; + cmd->stop_tx = ch_switch->block_tx; + + switch (ch_switch->channel->band) { + case IEEE80211_BAND_2GHZ: + cmd->band = WLCORE_BAND_2_4GHZ; + break; + case IEEE80211_BAND_5GHZ: + cmd->band = WLCORE_BAND_5GHZ; + break; + default: + wl1271_error("invalid channel switch band: %d", + ch_switch->channel->band); + ret = -EINVAL; + goto out_free; + } + + supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | + wlcore_hw_sta_get_ap_rate_mask(wl, wlvif); + if (wlvif->p2p) + supported_rates &= ~CONF_TX_CCK_RATES; + cmd->local_supported_rates = cpu_to_le32(supported_rates); + cmd->channel_type = wlvif->channel_type; + + ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send channel switch command"); + goto out_free; + } + +out_free: + kfree(cmd); +out: + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h new file mode 100644 index 000000000000..6687d10899ac --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/cmd.h @@ -0,0 +1,52 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_CMD_H__ +#define __WL18XX_CMD_H__ + +#include "../wlcore/wlcore.h" +#include "../wlcore/acx.h" + +struct wl18xx_cmd_channel_switch { + struct wl1271_cmd_header header; + + u8 role_id; + + /* The new serving channel */ + u8 channel; + /* Relative time of the serving channel switch in TBTT units */ + u8 switch_time; + /* Stop the role TX, should expect it after radar detection */ + u8 stop_tx; + + __le32 local_supported_rates; + + u8 channel_type; + u8 band; + + u8 padding[2]; +} __packed; + +int wl18xx_cmd_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_channel_switch *ch_switch); + +#endif diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index e81b3518d288..2e54a3ea813c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -34,6 +34,7 @@ #include "reg.h" #include "conf.h" +#include "cmd.h" #include "acx.h" #include "tx.h" #include "wl18xx.h" @@ -1335,6 +1336,7 @@ static struct wlcore_ops wl18xx_ops = { .handle_static_data = wl18xx_handle_static_data, .get_spare_blocks = wl18xx_get_spare_blocks, .set_key = wl18xx_set_key, + .channel_switch = wl18xx_cmd_channel_switch, .pre_pkt_send = wl18xx_pre_pkt_send, }; diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index c8c776684d2c..979676529ed0 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1722,43 +1722,7 @@ out: return ret; } -int wl12xx_cmd_channel_switch(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct ieee80211_channel_switch *ch_switch) -{ - struct wl12xx_cmd_channel_switch *cmd; - int ret; - - wl1271_debug(DEBUG_ACX, "cmd channel switch"); - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } - - cmd->role_id = wlvif->role_id; - cmd->channel = ch_switch->channel->hw_value; - cmd->switch_time = ch_switch->count; - cmd->stop_tx = ch_switch->block_tx; - - /* FIXME: control from mac80211 in the future */ - cmd->post_switch_tx_disable = 0; /* Enable TX on the target channel */ - - ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0); - if (ret < 0) { - wl1271_error("failed to send channel switch command"); - goto out_free; - } - -out_free: - kfree(cmd); - -out: - return ret; -} - -int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl) +int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_stop_channel_switch *cmd; int ret; @@ -1771,6 +1735,8 @@ int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl) goto out; } + cmd->role_id = wlvif->role_id; + ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to stop channel switch command"); diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 9e9062fdb712..96d53a730a31 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -89,7 +89,8 @@ int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); int wl12xx_cmd_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); -int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl); +int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif); int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); @@ -633,27 +634,13 @@ struct wl12xx_cmd_stop_fwlog { struct wl1271_cmd_header header; } __packed; -struct wl12xx_cmd_channel_switch { +struct wl12xx_cmd_stop_channel_switch { struct wl1271_cmd_header header; u8 role_id; - - /* The new serving channel */ - u8 channel; - /* Relative time of the serving channel switch in TBTT units */ - u8 switch_time; - /* Stop the role TX, should expect it after radar detection */ - u8 stop_tx; - /* The target channel tx status 1-stopped 0-open*/ - u8 post_switch_tx_disable; - u8 padding[3]; } __packed; -struct wl12xx_cmd_stop_channel_switch { - struct wl1271_cmd_header header; -} __packed; - /* Used to check radio status after calibration */ #define MAX_TLV_LENGTH 500 #define TEST_CMD_P2G_CAL 2 /* TX BiP */ diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 0f7a9338d10b..efb770a02822 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2673,7 +2673,7 @@ static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); - wl12xx_cmd_stop_channel_switch(wl); + wl12xx_cmd_stop_channel_switch(wl, wlvif); ieee80211_chswitch_done(vif, false); } @@ -4680,8 +4680,7 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, /* TODO: change mac80211 to pass vif as param */ wl12xx_for_each_wlvif_sta(wl, wlvif) { - ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch); - + ret = wl->ops->channel_switch(wl, wlvif, ch_switch); if (!ret) set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 36fde4e01969..1ad49c9ea7a4 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -95,6 +95,9 @@ struct wlcore_ops { struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); + int (*channel_switch)(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_channel_switch *ch_switch); u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len); }; From c50a282515dc7092f7318708a0f3ae7ca7342b9f Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:19 +0200 Subject: [PATCH 031/478] wlcore: update events enum/struct to new fw api The event mailbox in wl18xx has a different (non-compatible) structure. Create common functions in wlcore to handle the events, and call them from the chip-specific event mailbox parsers. This way, each driver (wl12xx/wl18xx) extracts the event mailbox by itself according to its own structure, and then calls the common wlcore functions to handle it. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/Makefile | 2 +- drivers/net/wireless/ti/wl12xx/event.c | 112 +++++++ drivers/net/wireless/ti/wl12xx/event.h | 111 +++++++ drivers/net/wireless/ti/wl12xx/main.c | 24 +- drivers/net/wireless/ti/wl18xx/Makefile | 2 +- drivers/net/wireless/ti/wl18xx/event.c | 99 ++++++ drivers/net/wireless/ti/wl18xx/event.h | 76 +++++ drivers/net/wireless/ti/wl18xx/main.c | 19 +- drivers/net/wireless/ti/wlcore/boot.c | 19 +- drivers/net/wireless/ti/wlcore/cmd.c | 44 +-- drivers/net/wireless/ti/wlcore/cmd.h | 2 + drivers/net/wireless/ti/wlcore/event.c | 352 ++++++++++------------ drivers/net/wireless/ti/wlcore/event.h | 100 ++---- drivers/net/wireless/ti/wlcore/main.c | 133 +++++--- drivers/net/wireless/ti/wlcore/scan.c | 8 +- drivers/net/wireless/ti/wlcore/wlcore.h | 15 +- drivers/net/wireless/ti/wlcore/wlcore_i.h | 3 + 17 files changed, 742 insertions(+), 379 deletions(-) create mode 100644 drivers/net/wireless/ti/wl12xx/event.c create mode 100644 drivers/net/wireless/ti/wl12xx/event.h create mode 100644 drivers/net/wireless/ti/wl18xx/event.c create mode 100644 drivers/net/wireless/ti/wl18xx/event.h diff --git a/drivers/net/wireless/ti/wl12xx/Makefile b/drivers/net/wireless/ti/wl12xx/Makefile index 8d9afd238adc..e6a24056b3c8 100644 --- a/drivers/net/wireless/ti/wl12xx/Makefile +++ b/drivers/net/wireless/ti/wl12xx/Makefile @@ -1,3 +1,3 @@ -wl12xx-objs = main.o cmd.o acx.o debugfs.o scan.o +wl12xx-objs = main.o cmd.o acx.o debugfs.o scan.o event.o obj-$(CONFIG_WL12XX) += wl12xx.o diff --git a/drivers/net/wireless/ti/wl12xx/event.c b/drivers/net/wireless/ti/wl12xx/event.c new file mode 100644 index 000000000000..6437dac12e70 --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/event.c @@ -0,0 +1,112 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "event.h" +#include "scan.h" +#include "../wlcore/cmd.h" +#include "../wlcore/debug.h" + +int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, + bool *timeout) +{ + u32 local_event; + + switch (event) { + case WLCORE_EVENT_ROLE_STOP_COMPLETE: + local_event = ROLE_STOP_COMPLETE_EVENT_ID; + break; + + case WLCORE_EVENT_PEER_REMOVE_COMPLETE: + local_event = PEER_REMOVE_COMPLETE_EVENT_ID; + break; + + default: + /* event not implemented */ + return 0; + } + return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout); +} + +int wl12xx_process_mailbox_events(struct wl1271 *wl) +{ + struct wl12xx_event_mailbox *mbox = wl->mbox; + u32 vector; + + + vector = le32_to_cpu(mbox->events_vector); + vector &= ~(le32_to_cpu(mbox->events_mask)); + + wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector); + + if (vector & SCAN_COMPLETE_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "status: 0x%x", + mbox->scheduled_scan_status); + + if (wl->scan_wlvif) + wl12xx_scan_completed(wl, wl->scan_wlvif); + } + + if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) + wlcore_event_sched_scan_report(wl, + mbox->scheduled_scan_status); + + if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) + wlcore_event_sched_scan_completed(wl, + mbox->scheduled_scan_status); + if (vector & SOFT_GEMINI_SENSE_EVENT_ID) + wlcore_event_soft_gemini_sense(wl, + mbox->soft_gemini_sense_info); + + if (vector & BSS_LOSE_EVENT_ID) + wlcore_event_beacon_loss(wl, 0xff); + + if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) + wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric); + + if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) + wlcore_event_ba_rx_constraint(wl, + BIT(mbox->role_id), + mbox->rx_ba_allowed); + + if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) + wlcore_event_channel_switch(wl, 0xff, + mbox->channel_switch_status); + + if (vector & DUMMY_PACKET_EVENT_ID) + wlcore_event_dummy_packet(wl); + + /* + * "TX retries exceeded" has a different meaning according to mode. + * In AP mode the offending station is disconnected. + */ + if (vector & MAX_TX_RETRY_EVENT_ID) + wlcore_event_max_tx_failure(wl, + le16_to_cpu(mbox->sta_tx_retry_exceeded)); + + if (vector & INACTIVE_STA_EVENT_ID) + wlcore_event_inactive_sta(wl, + le16_to_cpu(mbox->sta_aging_status)); + + if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) + wlcore_event_roc_complete(wl); + + return 0; +} diff --git a/drivers/net/wireless/ti/wl12xx/event.h b/drivers/net/wireless/ti/wl12xx/event.h new file mode 100644 index 000000000000..a5cc3fcd9eea --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/event.h @@ -0,0 +1,111 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL12XX_EVENT_H__ +#define __WL12XX_EVENT_H__ + +#include "../wlcore/wlcore.h" + +enum { + MEASUREMENT_START_EVENT_ID = BIT(8), + MEASUREMENT_COMPLETE_EVENT_ID = BIT(9), + SCAN_COMPLETE_EVENT_ID = BIT(10), + WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11), + AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12), + RESERVED1 = BIT(13), + PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14), + ROLE_STOP_COMPLETE_EVENT_ID = BIT(15), + RADAR_DETECTED_EVENT_ID = BIT(16), + CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), + BSS_LOSE_EVENT_ID = BIT(18), + REGAINED_BSS_EVENT_ID = BIT(19), + MAX_TX_RETRY_EVENT_ID = BIT(20), + DUMMY_PACKET_EVENT_ID = BIT(21), + SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), + CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23), + SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), + PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25), + INACTIVE_STA_EVENT_ID = BIT(26), + PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27), + PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28), + PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29), + BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30), + REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31), +}; + +struct wl12xx_event_mailbox { + __le32 events_vector; + __le32 events_mask; + __le32 reserved_1; + __le32 reserved_2; + + u8 number_of_scan_results; + u8 scan_tag; + u8 completed_scan_status; + u8 reserved_3; + + u8 soft_gemini_sense_info; + u8 soft_gemini_protective_info; + s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; + u8 change_auto_mode_timeout; + u8 scheduled_scan_status; + u8 reserved4; + /* tuned channel (roc) */ + u8 roc_channel; + + __le16 hlid_removed_bitmap; + + /* bitmap of aged stations (by HLID) */ + __le16 sta_aging_status; + + /* bitmap of stations (by HLID) which exceeded max tx retries */ + __le16 sta_tx_retry_exceeded; + + /* discovery completed results */ + u8 discovery_tag; + u8 number_of_preq_results; + u8 number_of_prsp_results; + u8 reserved_5; + + /* rx ba constraint */ + u8 role_id; /* 0xFF means any role. */ + u8 rx_ba_allowed; + u8 reserved_6[2]; + + /* Channel switch results */ + + u8 channel_switch_role_id; + u8 channel_switch_status; + u8 reserved_7[2]; + + u8 ps_poll_delivery_failure_role_ids; + u8 stopped_role_ids; + u8 started_role_ids; + + u8 reserved_8[9]; +} __packed; + +int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, + bool *timeout); +int wl12xx_process_mailbox_events(struct wl1271 *wl); + +#endif + diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index b93de04ccd6a..7138fe49c63d 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -39,6 +39,7 @@ #include "cmd.h" #include "acx.h" #include "scan.h" +#include "event.h" #include "debugfs.h" static char *fref_param; @@ -1229,6 +1230,23 @@ static int wl12xx_boot(struct wl1271 *wl) if (ret < 0) goto out; + wl->event_mask = BSS_LOSE_EVENT_ID | + REGAINED_BSS_EVENT_ID | + SCAN_COMPLETE_EVENT_ID | + ROLE_STOP_COMPLETE_EVENT_ID | + RSSI_SNR_TRIGGER_0_EVENT_ID | + PSPOLL_DELIVERY_FAILURE_EVENT_ID | + SOFT_GEMINI_SENSE_EVENT_ID | + PERIODIC_SCAN_REPORT_EVENT_ID | + PERIODIC_SCAN_COMPLETE_EVENT_ID | + DUMMY_PACKET_EVENT_ID | + PEER_REMOVE_COMPLETE_EVENT_ID | + BA_SESSION_RX_CONSTRAINT_EVENT_ID | + REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | + INACTIVE_STA_EVENT_ID | + MAX_TX_RETRY_EVENT_ID | + CHANNEL_SWITCH_COMPLETE_EVENT_ID; + ret = wlcore_boot_run_firmware(wl); if (ret < 0) goto out; @@ -1609,6 +1627,8 @@ static struct wlcore_ops wl12xx_ops = { .plt_init = wl12xx_plt_init, .trigger_cmd = wl12xx_trigger_cmd, .ack_event = wl12xx_ack_event, + .wait_for_event = wl12xx_wait_for_event, + .process_mailbox_events = wl12xx_process_mailbox_events, .calc_tx_blocks = wl12xx_calc_tx_blocks, .set_tx_desc_blocks = wl12xx_set_tx_desc_blocks, .set_tx_desc_data_len = wl12xx_set_tx_desc_data_len, @@ -1627,7 +1647,6 @@ static struct wlcore_ops wl12xx_ops = { .debugfs_init = wl12xx_debugfs_add_files, .scan_start = wl12xx_scan_start, .scan_stop = wl12xx_scan_stop, - .scan_completed = wl12xx_scan_completed, .sched_scan_start = wl12xx_sched_scan_start, .sched_scan_stop = wl12xx_scan_sched_scan_stop, .get_spare_blocks = wl12xx_get_spare_blocks, @@ -1719,7 +1738,8 @@ static int __devinit wl12xx_probe(struct platform_device *pdev) int ret; hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv), - WL12XX_AGGR_BUFFER_SIZE); + WL12XX_AGGR_BUFFER_SIZE, + sizeof(struct wl12xx_event_mailbox)); if (IS_ERR(hw)) { wl1271_error("can't allocate hw"); ret = PTR_ERR(hw); diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile index 81a864f3d793..ae2b81735785 100644 --- a/drivers/net/wireless/ti/wl18xx/Makefile +++ b/drivers/net/wireless/ti/wl18xx/Makefile @@ -1,3 +1,3 @@ -wl18xx-objs = main.o acx.o tx.o io.o debugfs.o scan.o cmd.o +wl18xx-objs = main.o acx.o tx.o io.o debugfs.o scan.o cmd.o event.o obj-$(CONFIG_WL18XX) += wl18xx.o diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c new file mode 100644 index 000000000000..bf4233c99028 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -0,0 +1,99 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "event.h" +#include "scan.h" +#include "../wlcore/cmd.h" +#include "../wlcore/debug.h" + +int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, + bool *timeout) +{ + u32 local_event; + + switch (event) { + case WLCORE_EVENT_PEER_REMOVE_COMPLETE: + local_event = PEER_REMOVE_COMPLETE_EVENT_ID; + break; + + default: + /* event not implemented */ + return 0; + } + return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout); +} + +int wl18xx_process_mailbox_events(struct wl1271 *wl) +{ + struct wl18xx_event_mailbox *mbox = wl->mbox; + u32 vector; + + vector = le32_to_cpu(mbox->events_vector); + wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector); + + if (vector & SCAN_COMPLETE_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "scan results: %d", + mbox->number_of_scan_results); + + if (wl->scan_wlvif) + wl18xx_scan_completed(wl, wl->scan_wlvif); + } + + if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) + wlcore_event_sched_scan_completed(wl, 1); + + if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) + wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric); + + if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) + wlcore_event_ba_rx_constraint(wl, + le16_to_cpu(mbox->rx_ba_role_id_bitmap), + le16_to_cpu(mbox->rx_ba_allowed_bitmap)); + + if (vector & BSS_LOSS_EVENT_ID) + wlcore_event_beacon_loss(wl, + le16_to_cpu(mbox->bss_loss_bitmap)); + + if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) + wlcore_event_channel_switch(wl, + le16_to_cpu(mbox->channel_switch_role_id_bitmap), + true); + + if (vector & DUMMY_PACKET_EVENT_ID) + wlcore_event_dummy_packet(wl); + + /* + * "TX retries exceeded" has a different meaning according to mode. + * In AP mode the offending station is disconnected. + */ + if (vector & MAX_TX_FAILURE_EVENT_ID) + wlcore_event_max_tx_failure(wl, + le32_to_cpu(mbox->tx_retry_exceeded_bitmap)); + + if (vector & INACTIVE_STA_EVENT_ID) + wlcore_event_inactive_sta(wl, + le32_to_cpu(mbox->inactive_sta_bitmap)); + + if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) + wlcore_event_roc_complete(wl); + + return 0; +} diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h new file mode 100644 index 000000000000..b55fd31e471a --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -0,0 +1,76 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_EVENT_H__ +#define __WL18XX_EVENT_H__ + +#include "../wlcore/wlcore.h" + +enum { + SCAN_COMPLETE_EVENT_ID = BIT(8), + RADAR_DETECTED_EVENT_ID = BIT(9), + CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(10), + BSS_LOSS_EVENT_ID = BIT(11), + MAX_TX_FAILURE_EVENT_ID = BIT(12), + DUMMY_PACKET_EVENT_ID = BIT(13), + INACTIVE_STA_EVENT_ID = BIT(14), + PEER_REMOVE_COMPLETE_EVENT_ID = BIT(15), + PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(16), + BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(17), + REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18), + DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19), +}; + +struct wl18xx_event_mailbox { + __le32 events_vector; + + u8 number_of_scan_results; + u8 number_of_sched_scan_results; + + __le16 channel_switch_role_id_bitmap; + + s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; + + /* bitmap of removed links */ + __le32 hlid_removed_bitmap; + + /* rx ba constraint */ + __le16 rx_ba_role_id_bitmap; /* 0xfff means any role. */ + __le16 rx_ba_allowed_bitmap; + + /* bitmap of roc completed (by role id) */ + __le16 roc_completed_bitmap; + + /* bitmap of stations (by role id) with bss loss */ + __le16 bss_loss_bitmap; + + /* bitmap of stations (by HLID) which exceeded max tx retries */ + __le32 tx_retry_exceeded_bitmap; + + /* bitmap of inactive stations (by HLID) */ + __le32 inactive_sta_bitmap; +} __packed; + +int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, + bool *timeout); +int wl18xx_process_mailbox_events(struct wl1271 *wl); + +#endif diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 2e54a3ea813c..0895ffaad5a9 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -40,6 +40,7 @@ #include "wl18xx.h" #include "io.h" #include "scan.h" +#include "event.h" #include "debugfs.h" #define WL18XX_RX_CHECKSUM_MASK 0x40 @@ -851,6 +852,18 @@ static int wl18xx_boot(struct wl1271 *wl) if (ret < 0) goto out; + wl->event_mask = BSS_LOSS_EVENT_ID | + SCAN_COMPLETE_EVENT_ID | + RSSI_SNR_TRIGGER_0_EVENT_ID | + PERIODIC_SCAN_COMPLETE_EVENT_ID | + DUMMY_PACKET_EVENT_ID | + PEER_REMOVE_COMPLETE_EVENT_ID | + BA_SESSION_RX_CONSTRAINT_EVENT_ID | + REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | + INACTIVE_STA_EVENT_ID | + MAX_TX_FAILURE_EVENT_ID | + CHANNEL_SWITCH_COMPLETE_EVENT_ID; + ret = wlcore_boot_run_firmware(wl); if (ret < 0) goto out; @@ -1313,6 +1326,8 @@ static struct wlcore_ops wl18xx_ops = { .plt_init = wl18xx_plt_init, .trigger_cmd = wl18xx_trigger_cmd, .ack_event = wl18xx_ack_event, + .wait_for_event = wl18xx_wait_for_event, + .process_mailbox_events = wl18xx_process_mailbox_events, .calc_tx_blocks = wl18xx_calc_tx_blocks, .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks, .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len, @@ -1330,7 +1345,6 @@ static struct wlcore_ops wl18xx_ops = { .debugfs_init = wl18xx_debugfs_add_files, .scan_start = wl18xx_scan_start, .scan_stop = wl18xx_scan_stop, - .scan_completed = wl18xx_scan_completed, .sched_scan_start = wl18xx_sched_scan_start, .sched_scan_stop = wl18xx_scan_sched_scan_stop, .handle_static_data = wl18xx_handle_static_data, @@ -1524,7 +1538,8 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) int ret; hw = wlcore_alloc_hw(sizeof(struct wl18xx_priv), - WL18XX_AGGR_BUFFER_SIZE); + WL18XX_AGGR_BUFFER_SIZE, + sizeof(struct wl18xx_event_mailbox)); if (IS_ERR(hw)) { wl1271_error("can't allocate hw"); ret = PTR_ERR(hw); diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 375ea574eafb..230c765ab401 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -491,7 +491,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) if (ret < 0) return ret; - wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); + wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size; wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); @@ -508,23 +508,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) */ /* unmask required mbox events */ - wl->event_mask = BSS_LOSE_EVENT_ID | - REGAINED_BSS_EVENT_ID | - SCAN_COMPLETE_EVENT_ID | - ROLE_STOP_COMPLETE_EVENT_ID | - RSSI_SNR_TRIGGER_0_EVENT_ID | - PSPOLL_DELIVERY_FAILURE_EVENT_ID | - SOFT_GEMINI_SENSE_EVENT_ID | - PERIODIC_SCAN_REPORT_EVENT_ID | - PERIODIC_SCAN_COMPLETE_EVENT_ID | - DUMMY_PACKET_EVENT_ID | - PEER_REMOVE_COMPLETE_EVENT_ID | - BA_SESSION_RX_CONSTRAINT_EVENT_ID | - REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | - INACTIVE_STA_EVENT_ID | - MAX_TX_RETRY_EVENT_ID | - CHANNEL_SWITCH_COMPLETE_EVENT_ID; - ret = wl1271_event_unmask(wl); if (ret < 0) { wl1271_error("EVENT mask setting failed"); diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 979676529ed0..63102f20df63 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -137,8 +137,8 @@ EXPORT_SYMBOL_GPL(wl1271_cmd_send); * Poll the mailbox event field until any of the bits in the mask is set or a * timeout occurs (WL1271_EVENT_TIMEOUT in msecs) */ -static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, - u32 mask, bool *timeout) +int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl, + u32 mask, bool *timeout) { u32 *events_vector; u32 event; @@ -188,20 +188,7 @@ out: kfree(events_vector); return ret; } - -static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) -{ - int ret; - bool timeout = false; - - ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask, &timeout); - if (ret != 0 || timeout) { - wl12xx_queue_recovery_work(wl); - return ret; - } - - return 0; -} +EXPORT_SYMBOL_GPL(wlcore_cmd_wait_for_event_or_timeout); int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type, u8 *role_id) @@ -423,12 +410,6 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl, goto out_free; } - ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID); - if (ret < 0) { - wl1271_error("cmd role stop dev event completion error"); - goto out_free; - } - wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid); out_free: @@ -516,7 +497,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl12xx_cmd_role_stop *cmd; int ret; - bool timeout = false; if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)) return -EINVAL; @@ -539,17 +519,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) goto out_free; } - /* - * Sometimes the firmware doesn't send this event, so we just - * time out without failing. Queue recovery for other - * failures. - */ - ret = wl1271_cmd_wait_for_event_or_timeout(wl, - ROLE_STOP_COMPLETE_EVENT_ID, - &timeout); - if (ret) - wl12xx_queue_recovery_work(wl); - wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid); out_free: @@ -1505,9 +1474,10 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid) goto out_free; } - ret = wl1271_cmd_wait_for_event_or_timeout(wl, - PEER_REMOVE_COMPLETE_EVENT_ID, - &timeout); + ret = wl->ops->wait_for_event(wl, + WLCORE_EVENT_PEER_REMOVE_COMPLETE, + &timeout); + /* * We are ok with a timeout here. The event is sometimes not sent * due to a firmware bug. In case of another error (like SDIO timeout) diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 96d53a730a31..46513dac5fb1 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -94,6 +94,8 @@ int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); +int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl, + u32 mask, bool *timeout); enum wl1271_commands { CMD_INTERROGATE = 1, /* use this to read information elements */ diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index d9353dad08d9..3c20393d255e 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -29,25 +29,29 @@ #include "scan.h" #include "wl12xx_80211.h" -static void wl1271_event_rssi_trigger(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct event_mailbox *mbox) +void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr) { - struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; enum nl80211_cqm_rssi_threshold_event event; - s8 metric = mbox->rssi_snr_trigger_metric[0]; + s8 metric = metric_arr[0]; wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric); - if (metric <= wlvif->rssi_thold) - event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; - else - event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + /* TODO: check actual multi-role support */ + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (metric <= wlvif->rssi_thold) + event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; + else + event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; - if (event != wlvif->last_rssi_event) - ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL); - wlvif->last_rssi_event = event; + vif = wl12xx_wlvif_to_vif(wlvif); + if (event != wlvif->last_rssi_event) + ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL); + wlvif->last_rssi_event = event; + } } +EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger); static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) { @@ -74,8 +78,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) } } -static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, - u8 enable) +void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable) { struct wl12xx_vif *wlvif; @@ -87,210 +90,179 @@ static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, wl1271_recalc_rx_streaming(wl, wlvif); } } - } +EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense); -static void wl1271_event_mbox_dump(struct event_mailbox *mbox) +void wlcore_event_sched_scan_report(struct wl1271 *wl, + u8 status) { - wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); - wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); - wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); + wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)", + status); + + wl1271_scan_sched_scan_results(wl); } +EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_report); -static int wl1271_event_process(struct wl1271 *wl) +void wlcore_event_sched_scan_completed(struct wl1271 *wl, + u8 status) +{ + wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)", + status); + + if (wl->sched_scanning) { + ieee80211_sched_scan_stopped(wl->hw); + wl->sched_scanning = false; + } +} +EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed); + +void wlcore_event_ba_rx_constraint(struct wl1271 *wl, + unsigned long roles_bitmap, + unsigned long allowed_bitmap) { - struct event_mailbox *mbox = wl->mbox; - struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; - u32 vector; - bool disconnect_sta = false; - unsigned long sta_bitmap = 0; - int ret; - wl1271_event_mbox_dump(mbox); + wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx", + __func__, roles_bitmap, allowed_bitmap); - vector = le32_to_cpu(mbox->events_vector); - vector &= ~(le32_to_cpu(mbox->events_mask)); - wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); + wl12xx_for_each_wlvif(wl, wlvif) { + if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || + !test_bit(wlvif->role_id , &roles_bitmap)) + continue; - if (vector & SCAN_COMPLETE_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "status: 0x%x", - mbox->scheduled_scan_status); - - if (wl->scan_vif) - wl->ops->scan_completed(wl, - wl12xx_vif_to_data(wl->scan_vif)); + wlvif->ba_allowed = !!test_bit(wlvif->role_id, + &allowed_bitmap); + if (!wlvif->ba_allowed) + wl1271_stop_ba_event(wl, wlvif); } +} +EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint); - if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " - "(status 0x%0x)", mbox->scheduled_scan_status); +void wlcore_event_channel_switch(struct wl1271 *wl, + unsigned long roles_bitmap, + bool success) +{ + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; - wl1271_scan_sched_scan_results(wl); + wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d", + __func__, roles_bitmap, success); + + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || + !test_bit(wlvif->role_id , &roles_bitmap)) + continue; + + if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, + &wlvif->flags)) + continue; + + vif = wl12xx_wlvif_to_vif(wlvif); + + ieee80211_chswitch_done(vif, success); + cancel_delayed_work(&wlvif->channel_switch_work); } +} +EXPORT_SYMBOL_GPL(wlcore_event_channel_switch); - if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT " - "(status 0x%0x)", mbox->scheduled_scan_status); - if (wl->sched_scanning) { - ieee80211_sched_scan_stopped(wl->hw); - wl->sched_scanning = false; +void wlcore_event_dummy_packet(struct wl1271 *wl) +{ + wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); + wl1271_tx_dummy_packet(wl); +} +EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet); + +static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap) +{ + u32 num_packets = wl->conf.tx.max_tx_retries; + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + const u8 *addr; + int h; + + for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) { + bool found = false; + /* find the ap vif connected to this sta */ + wl12xx_for_each_wlvif_ap(wl, wlvif) { + if (!test_bit(h, wlvif->ap.sta_hlid_map)) + continue; + found = true; + break; } + if (!found) + continue; + + vif = wl12xx_wlvif_to_vif(wlvif); + addr = wl->links[h].addr; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, addr); + if (sta) { + wl1271_debug(DEBUG_EVENT, "remove sta %d", h); + ieee80211_report_low_ack(sta, num_packets); + } + rcu_read_unlock(); } +} - if (vector & SOFT_GEMINI_SENSE_EVENT_ID) - wl12xx_event_soft_gemini_sense(wl, - mbox->soft_gemini_sense_info); +void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap) +{ + wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID"); + wlcore_disconnect_sta(wl, sta_bitmap); +} +EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure); +void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap) +{ + wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); + wlcore_disconnect_sta(wl, sta_bitmap); +} +EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta); + +void wlcore_event_roc_complete(struct wl1271 *wl) +{ + wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID"); + if (wl->roc_vif) + ieee80211_ready_on_channel(wl->hw); +} +EXPORT_SYMBOL_GPL(wlcore_event_roc_complete); + +void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap) +{ /* * We are HW_MONITOR device. On beacon loss - queue * connection loss work. Cancel it on REGAINED event. */ - if (vector & BSS_LOSE_EVENT_ID) { - /* TODO: check for multi-role */ - int delay = wl->conf.conn.synch_fail_thold * - wl->conf.conn.bss_lose_timeout; - wl1271_info("Beacon loss detected."); + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; + int delay = wl->conf.conn.synch_fail_thold * + wl->conf.conn.bss_lose_timeout; + + wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap); + + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || + !test_bit(wlvif->role_id , &roles_bitmap)) + continue; /* - * if the work is already queued, it should take place. We - * don't want to delay the connection loss indication - * any more. + * if the work is already queued, it should take place. + * We don't want to delay the connection loss + * indication any more. */ - ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work, + ieee80211_queue_delayed_work(wl->hw, + &wlvif->connection_loss_work, msecs_to_jiffies(delay)); - wl12xx_for_each_wlvif_sta(wl, wlvif) { - vif = wl12xx_wlvif_to_vif(wlvif); - - ieee80211_cqm_rssi_notify( - vif, - NL80211_CQM_RSSI_BEACON_LOSS_EVENT, - GFP_KERNEL); - } + vif = wl12xx_wlvif_to_vif(wlvif); + ieee80211_cqm_rssi_notify( + vif, + NL80211_CQM_RSSI_BEACON_LOSS_EVENT, + GFP_KERNEL); } - - if (vector & REGAINED_BSS_EVENT_ID) { - /* TODO: check for multi-role */ - wl1271_info("Beacon regained."); - cancel_delayed_work(&wl->connection_loss_work); - - /* sanity check - we can't lose and gain the beacon together */ - WARN(vector & BSS_LOSE_EVENT_ID, - "Concurrent beacon loss and gain from FW"); - } - - if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { - /* TODO: check actual multi-role support */ - wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); - wl12xx_for_each_wlvif_sta(wl, wlvif) { - wl1271_event_rssi_trigger(wl, wlvif, mbox); - } - } - - if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) { - u8 role_id = mbox->role_id; - wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. " - "ba_allowed = 0x%x, role_id=%d", - mbox->rx_ba_allowed, role_id); - - wl12xx_for_each_wlvif(wl, wlvif) { - if (role_id != 0xff && role_id != wlvif->role_id) - continue; - - wlvif->ba_allowed = !!mbox->rx_ba_allowed; - if (!wlvif->ba_allowed) - wl1271_stop_ba_event(wl, wlvif); - } - } - - if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. " - "status = 0x%x", - mbox->channel_switch_status); - /* - * That event uses for two cases: - * 1) channel switch complete with status=0 - * 2) channel switch failed status=1 - */ - - /* TODO: configure only the relevant vif */ - wl12xx_for_each_wlvif_sta(wl, wlvif) { - bool success; - - if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, - &wlvif->flags)) - continue; - - success = mbox->channel_switch_status ? false : true; - vif = wl12xx_wlvif_to_vif(wlvif); - - ieee80211_chswitch_done(vif, success); - } - } - - if ((vector & DUMMY_PACKET_EVENT_ID)) { - wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); - ret = wl1271_tx_dummy_packet(wl); - if (ret < 0) - return ret; - } - - /* - * "TX retries exceeded" has a different meaning according to mode. - * In AP mode the offending station is disconnected. - */ - if (vector & MAX_TX_RETRY_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); - sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); - disconnect_sta = true; - } - - if (vector & INACTIVE_STA_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); - sta_bitmap |= le16_to_cpu(mbox->sta_aging_status); - disconnect_sta = true; - } - - if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, - "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID"); - if (wl->roc_vif) - ieee80211_ready_on_channel(wl->hw); - } - - if (disconnect_sta) { - u32 num_packets = wl->conf.tx.max_tx_retries; - struct ieee80211_sta *sta; - const u8 *addr; - int h; - - for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) { - bool found = false; - /* find the ap vif connected to this sta */ - wl12xx_for_each_wlvif_ap(wl, wlvif) { - if (!test_bit(h, wlvif->ap.sta_hlid_map)) - continue; - found = true; - break; - } - if (!found) - continue; - - vif = wl12xx_wlvif_to_vif(wlvif); - addr = wl->links[h].addr; - - rcu_read_lock(); - sta = ieee80211_find_sta(vif, addr); - if (sta) { - wl1271_debug(DEBUG_EVENT, "remove sta %d", h); - ieee80211_report_low_ack(sta, num_packets); - } - rcu_read_unlock(); - } - } - return 0; } +EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss); int wl1271_event_unmask(struct wl1271 *wl) { @@ -314,12 +286,12 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) /* first we read the mbox descriptor */ ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox, - sizeof(*wl->mbox), false); + wl->mbox_size, false); if (ret < 0) return ret; /* process the descriptor */ - ret = wl1271_event_process(wl); + ret = wl->ops->process_mailbox_events(wl); if (ret < 0) return ret; diff --git a/drivers/net/wireless/ti/wlcore/event.h b/drivers/net/wireless/ti/wlcore/event.h index 8adf18d6c58f..766b02f15780 100644 --- a/drivers/net/wireless/ti/wlcore/event.h +++ b/drivers/net/wireless/ti/wlcore/event.h @@ -46,33 +46,16 @@ enum { RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5), RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6), RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7), - MEASUREMENT_START_EVENT_ID = BIT(8), - MEASUREMENT_COMPLETE_EVENT_ID = BIT(9), - SCAN_COMPLETE_EVENT_ID = BIT(10), - WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11), - AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12), - RESERVED1 = BIT(13), - PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14), - ROLE_STOP_COMPLETE_EVENT_ID = BIT(15), - RADAR_DETECTED_EVENT_ID = BIT(16), - CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), - BSS_LOSE_EVENT_ID = BIT(18), - REGAINED_BSS_EVENT_ID = BIT(19), - MAX_TX_RETRY_EVENT_ID = BIT(20), - DUMMY_PACKET_EVENT_ID = BIT(21), - SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), - CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23), - SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), - PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25), - INACTIVE_STA_EVENT_ID = BIT(26), - PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27), - PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28), - PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29), - BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30), - REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31), + EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, }; +/* events the driver might want to wait for */ +enum wlcore_wait_event { + WLCORE_EVENT_ROLE_STOP_COMPLETE, + WLCORE_EVENT_PEER_REMOVE_COMPLETE, +}; + enum { EVENT_ENTER_POWER_SAVE_FAIL = 0, EVENT_ENTER_POWER_SAVE_SUCCESS, @@ -80,61 +63,26 @@ enum { #define NUM_OF_RSSI_SNR_TRIGGERS 8 -struct event_mailbox { - __le32 events_vector; - __le32 events_mask; - __le32 reserved_1; - __le32 reserved_2; - - u8 number_of_scan_results; - u8 scan_tag; - u8 completed_scan_status; - u8 reserved_3; - - u8 soft_gemini_sense_info; - u8 soft_gemini_protective_info; - s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; - u8 change_auto_mode_timeout; - u8 scheduled_scan_status; - u8 reserved4; - /* tuned channel (roc) */ - u8 roc_channel; - - __le16 hlid_removed_bitmap; - - /* bitmap of aged stations (by HLID) */ - __le16 sta_aging_status; - - /* bitmap of stations (by HLID) which exceeded max tx retries */ - __le16 sta_tx_retry_exceeded; - - /* discovery completed results */ - u8 discovery_tag; - u8 number_of_preq_results; - u8 number_of_prsp_results; - u8 reserved_5; - - /* rx ba constraint */ - u8 role_id; /* 0xFF means any role. */ - u8 rx_ba_allowed; - u8 reserved_6[2]; - - /* Channel switch results */ - - u8 channel_switch_role_id; - u8 channel_switch_status; - u8 reserved_7[2]; - - u8 ps_poll_delivery_failure_role_ids; - u8 stopped_role_ids; - u8 started_role_ids; - - u8 reserved_8[9]; -} __packed; - struct wl1271; int wl1271_event_unmask(struct wl1271 *wl); int wl1271_event_handle(struct wl1271 *wl, u8 mbox); +void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable); +void wlcore_event_sched_scan_report(struct wl1271 *wl, + u8 status); +void wlcore_event_sched_scan_completed(struct wl1271 *wl, + u8 status); +void wlcore_event_ba_rx_constraint(struct wl1271 *wl, + unsigned long roles_bitmap, + unsigned long allowed_bitmap); +void wlcore_event_channel_switch(struct wl1271 *wl, + unsigned long roles_bitmap, + bool success); +void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap); +void wlcore_event_dummy_packet(struct wl1271 *wl); +void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap); +void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap); +void wlcore_event_roc_complete(struct wl1271 *wl); +void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr); #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index efb770a02822..043bf7f7c005 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1139,7 +1139,6 @@ int wl1271_plt_stop(struct wl1271 *wl) cancel_work_sync(&wl->recovery_work); cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->tx_watchdog_work); - cancel_delayed_work_sync(&wl->connection_loss_work); mutex_lock(&wl->mutex); wl1271_power_off(wl); @@ -1841,7 +1840,6 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) cancel_work_sync(&wl->tx_work); cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->tx_watchdog_work); - cancel_delayed_work_sync(&wl->connection_loss_work); /* let's notify MAC80211 about the remaining pending TX frames */ wl12xx_tx_reset(wl); @@ -1916,6 +1914,71 @@ static void wlcore_op_stop(struct ieee80211_hw *hw) mutex_unlock(&wl->mutex); } +static void wlcore_channel_switch_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + struct ieee80211_vif *vif; + struct wl12xx_vif *wlvif; + int ret; + + dwork = container_of(work, struct delayed_work, work); + wlvif = container_of(dwork, struct wl12xx_vif, channel_switch_work); + wl = wlvif->wl; + + wl1271_info("channel switch failed (role_id: %d).", wlvif->role_id); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + /* check the channel switch is still ongoing */ + if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) + goto out; + + vif = wl12xx_wlvif_to_vif(wlvif); + ieee80211_chswitch_done(vif, false); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl12xx_cmd_stop_channel_switch(wl, wlvif); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + +static void wlcore_connection_loss_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + struct ieee80211_vif *vif; + struct wl12xx_vif *wlvif; + + dwork = container_of(work, struct delayed_work, work); + wlvif = container_of(dwork, struct wl12xx_vif, connection_loss_work); + wl = wlvif->wl; + + wl1271_info("Connection loss work (role_id: %d).", wlvif->role_id); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + /* Call mac80211 connection loss */ + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + goto out; + + vif = wl12xx_wlvif_to_vif(wlvif); + ieee80211_connection_loss(vif); +out: + mutex_unlock(&wl->mutex); +} + static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx) { u8 policy = find_first_zero_bit(wl->rate_policies_map, @@ -2063,6 +2126,10 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wl1271_rx_streaming_enable_work); INIT_WORK(&wlvif->rx_streaming_disable_work, wl1271_rx_streaming_disable_work); + INIT_DELAYED_WORK(&wlvif->channel_switch_work, + wlcore_channel_switch_work); + INIT_DELAYED_WORK(&wlvif->connection_loss_work, + wlcore_connection_loss_work); INIT_LIST_HEAD(&wlvif->list); setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer, @@ -2312,7 +2379,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl1271_info("down"); if (wl->scan.state != WL1271_SCAN_STATE_IDLE && - wl->scan_vif == vif) { + wl->scan_wlvif == wlvif) { /* * Rearm the tx watchdog just before idling scan. This * prevents just-finished scans from triggering the watchdog @@ -2321,7 +2388,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); - wl->scan_vif = NULL; + wl->scan_wlvif = NULL; wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, true); } @@ -2408,6 +2475,7 @@ unlock: del_timer_sync(&wlvif->rx_streaming_timer); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); + cancel_delayed_work_sync(&wlvif->connection_loss_work); mutex_lock(&wl->mutex); } @@ -2675,6 +2743,7 @@ static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif) wl12xx_cmd_stop_channel_switch(wl, wlvif); ieee80211_chswitch_done(vif, false); + cancel_delayed_work(&wlvif->channel_switch_work); } /* invalidate keep-alive template */ @@ -3296,7 +3365,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw, wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); - wl->scan_vif = NULL; + wl->scan_wlvif = NULL; wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, true); @@ -4087,7 +4156,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, * state changed */ if (!is_ap && (changed & BSS_CHANGED_ASSOC)) - cancel_delayed_work_sync(&wl->connection_loss_work); + cancel_delayed_work_sync(&wlvif->connection_loss_work); if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) @@ -4680,11 +4749,23 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, /* TODO: change mac80211 to pass vif as param */ wl12xx_for_each_wlvif_sta(wl, wlvif) { + unsigned long delay_usec; + ret = wl->ops->channel_switch(wl, wlvif, ch_switch); - if (!ret) - set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); + if (ret) + goto out_sleep; + + set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); + + /* indicate failure 5 seconds after channel switch time */ + delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) * + ch_switch->count; + ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work, + usecs_to_jiffies(delay_usec) + + msecs_to_jiffies(5000)); } +out_sleep: wl1271_ps_elp_sleep(wl); out: @@ -5193,34 +5274,6 @@ static struct bin_attribute fwlog_attr = { .read = wl1271_sysfs_read_fwlog, }; -static void wl1271_connection_loss_work(struct work_struct *work) -{ - struct delayed_work *dwork; - struct wl1271 *wl; - struct ieee80211_vif *vif; - struct wl12xx_vif *wlvif; - - dwork = container_of(work, struct delayed_work, work); - wl = container_of(dwork, struct wl1271, connection_loss_work); - - wl1271_info("Connection loss work."); - - mutex_lock(&wl->mutex); - - if (unlikely(wl->state != WLCORE_STATE_ON)) - goto out; - - /* Call mac80211 connection loss */ - wl12xx_for_each_wlvif_sta(wl, wlvif) { - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - goto out; - vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_connection_loss(vif); - } -out: - mutex_unlock(&wl->mutex); -} - static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic) { int i; @@ -5478,7 +5531,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) #define WL1271_DEFAULT_CHANNEL 0 -struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size) +struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, + u32 mbox_size) { struct ieee80211_hw *hw; struct wl1271 *wl; @@ -5522,8 +5576,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size) INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work); INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work); - INIT_DELAYED_WORK(&wl->connection_loss_work, - wl1271_connection_loss_work); wl->freezable_wq = create_freezable_workqueue("wl12xx_wq"); if (!wl->freezable_wq) { @@ -5586,7 +5638,8 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size) goto err_dummy_packet; } - wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA); + wl->mbox_size = mbox_size; + wl->mbox = kmalloc(wl->mbox_size, GFP_KERNEL | GFP_DMA); if (!wl->mbox) { ret = -ENOMEM; goto err_fwlog; diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index e7d0a02be5b0..eeb61889833f 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -35,7 +35,6 @@ void wl1271_scan_complete_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; - struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; int ret; @@ -52,8 +51,7 @@ void wl1271_scan_complete_work(struct work_struct *work) if (wl->scan.state == WL1271_SCAN_STATE_IDLE) goto out; - vif = wl->scan_vif; - wlvif = wl12xx_vif_to_data(vif); + wlvif = wl->scan_wlvif; /* * Rearm the tx watchdog just before idling scan. This @@ -64,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work) wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; - wl->scan_vif = NULL; + wl->scan_wlvif = NULL; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) @@ -295,7 +293,7 @@ int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif, wl->scan.ssid_len = 0; } - wl->scan_vif = vif; + wl->scan_wlvif = wlvif; wl->scan.req = req; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 1ad49c9ea7a4..d47eb6c19b0a 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -51,6 +51,9 @@ struct wlcore_ops { int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len); int (*ack_event)(struct wl1271 *wl); + int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event, + bool *timeout); + int (*process_mailbox_events)(struct wl1271 *wl); u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks); void (*set_tx_desc_blocks)(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, @@ -85,7 +88,6 @@ struct wlcore_ops { int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_scan_request *req); int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); - void (*scan_completed)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies); @@ -281,22 +283,20 @@ struct wl1271 { bool watchdog_recovery; /* Pointer that holds DMA-friendly block for the mailbox */ - struct event_mailbox *mbox; + void *mbox; /* The mbox event mask */ u32 event_mask; /* Mailbox pointers */ + u32 mbox_size; u32 mbox_ptr[2]; /* Are we currently scanning */ - struct ieee80211_vif *scan_vif; + struct wl12xx_vif *scan_wlvif; struct wl1271_scan scan; struct delayed_work scan_complete_work; - /* Connection loss work */ - struct delayed_work connection_loss_work; - struct ieee80211_vif *roc_vif; struct delayed_work roc_complete_work; @@ -436,7 +436,8 @@ struct wl1271 { int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); int __devexit wlcore_remove(struct platform_device *pdev); -struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size); +struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, + u32 mbox_size); int wlcore_free_hw(struct wl1271 *wl); int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 6678d4b18611..bc3b5f4d1b44 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -421,6 +421,9 @@ struct wl12xx_vif { struct work_struct rx_streaming_disable_work; struct timer_list rx_streaming_timer; + struct delayed_work channel_switch_work; + struct delayed_work connection_loss_work; + /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) From d50529c0d8b7f03012f140349161c29b0f7bd24c Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:20 +0200 Subject: [PATCH 032/478] wlcore: pass wmm configuration to the fw New fields were added to start_role(ap) and set_peer_state commands, so the fw will be able to know whether the sta/ap supports wmm (the fw uses it in order to choose the AC for some of its internally-generated frames) For sta, take this value right from bss_conf->qos. For ap, check for wmm support by looking for the WMM IE in the configured beacon. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 8 +++++++- drivers/net/wireless/ti/wlcore/cmd.h | 20 +++++++++++++++++--- drivers/net/wireless/ti/wlcore/main.c | 11 +++++++++-- drivers/net/wireless/ti/wlcore/wlcore_i.h | 2 ++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 63102f20df63..b2ebe39e5ece 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -570,6 +570,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP; /* FIXME: Change when adding DFS */ cmd->ap.reset_tsf = 1; /* By default reset AP TSF */ + cmd->ap.wmm = wlvif->wmm_enabled; cmd->channel = wlvif->channel; cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type); @@ -1363,7 +1364,8 @@ out: return ret; } -int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid) +int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 hlid) { struct wl12xx_cmd_set_peer_state *cmd; int ret = 0; @@ -1379,6 +1381,10 @@ int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid) cmd->hlid = hlid; cmd->state = WL1271_CMD_STA_STATE_CONNECTED; + /* wmm param is valid only for station role */ + if (wlvif->bss_type == BSS_TYPE_STA_BSS) + cmd->wmm = wlvif->wmm_enabled; + ret = wl1271_cmd_send(wl, CMD_SET_PEER_STATE, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("failed to send set peer state command"); diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 46513dac5fb1..2070a10994c0 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -76,7 +76,8 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32, u16 tx_seq_16); -int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid); +int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 hlid); int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, enum ieee80211_band band, u8 channel); int wl12xx_croc(struct wl1271 *wl, u8 role_id); @@ -355,7 +356,13 @@ struct wl12xx_cmd_role_start { u8 reset_tsf; - u8 padding_1[4]; + /* + * ap supports wmm (note that there is additional + * per-sta wmm configuration) + */ + u8 wmm; + + u8 padding_1[3]; } __packed ap; }; } __packed; @@ -525,7 +532,14 @@ struct wl12xx_cmd_set_peer_state { u8 hlid; u8 state; - u8 padding[2]; + + /* + * wmm is relevant for sta role only. + * ap role configures the per-sta wmm params in + * the add_peer command. + */ + u8 wmm; + u8 padding[1]; } __packed; struct wl12xx_cmd_roc { diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 043bf7f7c005..d7fe07b7156c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -79,7 +79,7 @@ static int wl12xx_set_authorized(struct wl1271 *wl, if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags)) return 0; - ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid); + ret = wl12xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid); if (ret < 0) return ret; @@ -2630,6 +2630,7 @@ static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif, wlvif->aid = bss_conf->aid; wlvif->channel_type = bss_conf->channel_type; wlvif->beacon_int = bss_conf->beacon_int; + wlvif->wmm_enabled = bss_conf->qos; set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); @@ -3692,6 +3693,12 @@ static int wlcore_set_beacon_template(struct wl1271 *wl, goto out; } + wlvif->wmm_enabled = + cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + beacon->data + ieoffset, + beacon->len - ieoffset); + /* * In case we already have a probe-resp beacon set explicitly * by usermode, don't use the beacon data. @@ -4478,7 +4485,7 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, /* Authorize station (AP mode) */ if (is_ap && new_state == IEEE80211_STA_AUTHORIZED) { - ret = wl12xx_cmd_set_peer_state(wl, hlid); + ret = wl12xx_cmd_set_peer_state(wl, wlvif, hlid); if (ret < 0) return ret; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index bc3b5f4d1b44..8478bbce82d5 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -416,6 +416,8 @@ struct wl12xx_vif { bool ba_support; bool ba_allowed; + bool wmm_enabled; + /* Rx Streaming */ struct work_struct rx_streaming_enable_work; struct work_struct rx_streaming_disable_work; From 978cd3a0b82969c1f85942e208f8a00412964ef6 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:21 +0200 Subject: [PATCH 033/478] wlcore: save session_id per-link A new session_id is generated on link allocation. it is saved in a global array and used later, on tx. The new fw api adds new bcast/global_session_id fields to start_role(ap) command, and a new session_id field to add_peer command. align the driver with it. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 30 +++++++++++++---------- drivers/net/wireless/ti/wlcore/cmd.h | 6 +++-- drivers/net/wireless/ti/wlcore/debugfs.c | 1 - drivers/net/wireless/ti/wlcore/main.c | 1 + drivers/net/wireless/ti/wlcore/tx.c | 2 +- drivers/net/wireless/ti/wlcore/wlcore.h | 2 ++ drivers/net/wireless/ti/wlcore/wlcore_i.h | 3 --- 7 files changed, 25 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index b2ebe39e5ece..1735a534a89d 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -266,6 +266,16 @@ out: return ret; } +static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid) +{ + if (wl->session_ids[hlid] >= SESSION_COUNTER_MAX) + wl->session_ids[hlid] = 0; + + wl->session_ids[hlid]++; + + return wl->session_ids[hlid]; +} + int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) { unsigned long flags; @@ -273,6 +283,8 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) if (link >= WL12XX_MAX_LINKS) return -EBUSY; + wl->session_ids[link] = wlcore_get_new_session_id(wl, link); + /* these bits are used by op_tx */ spin_lock_irqsave(&wl->wl_lock, flags); __set_bit(link, wl->links_map); @@ -304,17 +316,6 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) *hlid = WL12XX_INVALID_LINK_ID; } -static int wl12xx_get_new_session_id(struct wl1271 *wl, - struct wl12xx_vif *wlvif) -{ - if (wlvif->session_counter >= SESSION_COUNTER_MAX) - wlvif->session_counter = 0; - - wlvif->session_counter++; - - return wlvif->session_counter; -} - static u8 wlcore_get_native_channel_type(u8 nl_channel_type) { switch (nl_channel_type) { @@ -359,7 +360,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, goto out_free; } cmd->device.hlid = wlvif->dev_hlid; - cmd->device.session = wl12xx_get_new_session_id(wl, wlvif); + cmd->device.session = wl->session_ids[wlvif->dev_hlid]; wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d", cmd->role_id, cmd->device.hlid, cmd->device.session); @@ -460,7 +461,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) goto out_free; } cmd->sta.hlid = wlvif->sta.hlid; - cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif); + cmd->sta.session = wl->session_ids[wlvif->sta.hlid]; /* * We don't have the correct remote rates in this stage, and there * is no way to update them later, so use our supported rates instead. @@ -564,6 +565,8 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) cmd->ap.bss_index = WL1271_AP_BSS_INDEX; cmd->ap.global_hlid = wlvif->ap.global_hlid; cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid; + cmd->ap.global_session_id = wl->session_ids[wlvif->ap.global_hlid]; + cmd->ap.bcast_session_id = wl->session_ids[wlvif->ap.bcast_hlid]; cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set); cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int); cmd->ap.dtim_interval = bss_conf->dtim_period; @@ -1419,6 +1422,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, cmd->hlid = hlid; cmd->sp_len = sta->max_sp; cmd->wmm = sta->wme ? 1 : 0; + cmd->session_id = wl->session_ids[hlid]; for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++) if (sta->wme && (sta->uapsd_queues & BIT(i))) diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 2070a10994c0..c9f826819f97 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -362,7 +362,9 @@ struct wl12xx_cmd_role_start { */ u8 wmm; - u8 padding_1[3]; + u8 bcast_session_id; + u8 global_session_id; + u8 padding_1[1]; } __packed ap; }; } __packed; @@ -582,7 +584,7 @@ struct wl12xx_cmd_add_peer { u8 bss_index; u8 sp_len; u8 wmm; - u8 padding1; + u8 session_id; } __packed; struct wl12xx_cmd_remove_peer { diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 93f801dc94e4..80376d461144 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -589,7 +589,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, VIF_STATE_PRINT_INT(beacon_int); VIF_STATE_PRINT_INT(default_key); VIF_STATE_PRINT_INT(aid); - VIF_STATE_PRINT_INT(session_counter); VIF_STATE_PRINT_INT(psm_entry_retry); VIF_STATE_PRINT_INT(power_level); VIF_STATE_PRINT_INT(rssi_thold); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index d7fe07b7156c..97e4a9c9da70 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1871,6 +1871,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) memset(wl->roles_map, 0, sizeof(wl->roles_map)); memset(wl->links_map, 0, sizeof(wl->links_map)); memset(wl->roc_map, 0, sizeof(wl->roc_map)); + memset(wl->session_ids, 0, sizeof(wl->session_ids)); wl->active_sta_count = 0; /* The system link is always allocated */ diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 2844f305a489..68f73f935b8e 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -294,7 +294,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ; } else if (wlvif) { /* configure the tx attributes */ - tx_attr = wlvif->session_counter << + tx_attr = wl->session_ids[hlid] << TX_HW_ATTR_OFST_SESSION_COUNTER; } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index d47eb6c19b0a..e9254b78bac5 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -215,6 +215,8 @@ struct wl1271 { unsigned long klv_templates_map[ BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)]; + u8 session_ids[WL12XX_MAX_LINKS]; + struct list_head wlvif_list; u8 sta_count; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 8478bbce82d5..6be1e8ef55bc 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -396,9 +396,6 @@ struct wl12xx_vif { /* Our association ID */ u16 aid; - /* Session counter for the chipset */ - int session_counter; - /* retry counter for PSM entries */ u8 psm_entry_retry; From 6b70e7eb70cd6c5ad445ec02f74b84131a4c721a Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Sun, 25 Nov 2012 18:26:59 +0200 Subject: [PATCH 034/478] wlcore: add new reg-domain configuration command In 18xx the calibration process of the PHY Cortex domain requires to perform an active calibration of the channel before it can be used for transmission. To fulfill world wide regulatory restrictions, fw should be always synchronized/updated with current CRDA configuration. Add a new "CMD_DFS_CHANNEL_CONFIG" command to update the fw with current reg-domain, this command passes a bit map of channels that are allowed to be used for transmission. The driver shall update the fw during initialization and after each change in the current reg-domain configuration. The driver will save the channel number of incoming beacons during the scan process, as they might be a result of the passive scan on "IEEE80211_CHAN_PASSIVE_SCAN" channel and will update the fw accordingly once the scan is finished, the purpose of this is to be ready in case of the authentication request on one of these disabled (uncalibrated) channels. The new command requires to wait for the fw completion event "DFS_CHANNELS_CONFIG_COMPLETE_EVENT". No scan commands (including the sched scan) can be executed concurrently with the "CMD_DFS_CHANNEL_CONFIG", wl->mutex ensures that. [Arik - move reset of reg_ch_conf_last to safe place inside op_stop_locked] [Eliad - adjust to new event waiting api] Signed-off-by: Victor Goldenshtein Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/event.c | 4 + drivers/net/wireless/ti/wl18xx/main.c | 4 +- drivers/net/wireless/ti/wlcore/cmd.c | 125 ++++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/cmd.h | 10 ++ drivers/net/wireless/ti/wlcore/event.h | 1 + drivers/net/wireless/ti/wlcore/init.c | 4 + drivers/net/wireless/ti/wlcore/main.c | 34 +++++++ drivers/net/wireless/ti/wlcore/rx.c | 4 + drivers/net/wireless/ti/wlcore/scan.c | 2 + drivers/net/wireless/ti/wlcore/wlcore.h | 10 ++ 10 files changed, 197 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index bf4233c99028..02ef5aa9da6b 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -34,6 +34,10 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, local_event = PEER_REMOVE_COMPLETE_EVENT_ID; break; + case WLCORE_EVENT_DFS_CONFIG_COMPLETE: + local_event = DFS_CHANNELS_CONFIG_COMPLETE_EVENT; + break; + default: /* event not implemented */ return 0; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 0895ffaad5a9..f1eaf9aebb1e 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -616,6 +616,7 @@ static int wl18xx_identify_chip(struct wl1271 *wl) WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN | WLCORE_QUIRK_TX_PAD_LAST_FRAME | + WLCORE_QUIRK_REGDOMAIN_CONF | WLCORE_QUIRK_DUAL_PROBE_TMPL; wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER, @@ -862,7 +863,8 @@ static int wl18xx_boot(struct wl1271 *wl) REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | INACTIVE_STA_EVENT_ID | MAX_TX_FAILURE_EVENT_ID | - CHANNEL_SWITCH_COMPLETE_EVENT_ID; + CHANNEL_SWITCH_COMPLETE_EVENT_ID | + DFS_CHANNELS_CONFIG_COMPLETE_EVENT; ret = wlcore_boot_run_firmware(wl); if (ret < 0) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 1735a534a89d..599c006f4c47 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1503,6 +1503,131 @@ out: return ret; } +static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch) +{ + int idx = -1; + + switch (band) { + case IEEE80211_BAND_5GHZ: + if (ch >= 8 && ch <= 16) + idx = ((ch-8)/4 + 18); + else if (ch >= 34 && ch <= 64) + idx = ((ch-34)/2 + 3 + 18); + else if (ch >= 100 && ch <= 140) + idx = ((ch-100)/4 + 15 + 18); + else if (ch >= 149 && ch <= 165) + idx = ((ch-149)/4 + 26 + 18); + else + idx = -1; + break; + case IEEE80211_BAND_2GHZ: + if (ch >= 1 && ch <= 14) + idx = ch - 1; + else + idx = -1; + break; + default: + wl1271_error("get reg conf ch idx - unknown band: %d", + (int)band); + } + + return idx; +} + +void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, + enum ieee80211_band band) +{ + int ch_bit_idx = 0; + + if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF)) + return; + + ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel); + + if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS) + set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending); +} + +int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) +{ + struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL; + int ret = 0, i, b, ch_bit_idx; + struct ieee80211_channel *channel; + u32 tmp_ch_bitmap[2]; + u16 ch; + struct wiphy *wiphy = wl->hw->wiphy; + struct ieee80211_supported_band *band; + bool timeout = false; + + if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF)) + return 0; + + wl1271_debug(DEBUG_CMD, "cmd reg domain config"); + + memset(tmp_ch_bitmap, 0, sizeof(tmp_ch_bitmap)); + + for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) { + band = wiphy->bands[b]; + for (i = 0; i < band->n_channels; i++) { + channel = &band->channels[i]; + ch = channel->hw_value; + + if (channel->flags & (IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_PASSIVE_SCAN)) + continue; + + ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch); + if (ch_bit_idx < 0) + continue; + + set_bit(ch_bit_idx, (long *)tmp_ch_bitmap); + } + } + + tmp_ch_bitmap[0] |= wl->reg_ch_conf_pending[0]; + tmp_ch_bitmap[1] |= wl->reg_ch_conf_pending[1]; + + if (!memcmp(tmp_ch_bitmap, wl->reg_ch_conf_last, sizeof(tmp_ch_bitmap))) + goto out; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]); + cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]); + + wl1271_debug(DEBUG_CMD, + "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x", + cmd->ch_bit_map1, cmd->ch_bit_map2); + + ret = wl1271_cmd_send(wl, CMD_DFS_CHANNEL_CONFIG, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send reg domain dfs config"); + goto out; + } + + ret = wl->ops->wait_for_event(wl, + WLCORE_EVENT_DFS_CONFIG_COMPLETE, + &timeout); + if (ret < 0 || timeout) { + wl1271_error("reg domain conf %serror", + timeout ? "completion " : ""); + ret = timeout ? -ETIMEDOUT : ret; + goto out; + } + + memcpy(wl->reg_ch_conf_last, tmp_ch_bitmap, sizeof(tmp_ch_bitmap)); + memset(wl->reg_ch_conf_pending, 0, sizeof(wl->reg_ch_conf_pending)); + +out: + kfree(cmd); + return ret; +} + int wl12xx_cmd_config_fwlog(struct wl1271 *wl) { struct wl12xx_cmd_config_fwlog *cmd; diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index c9f826819f97..7f378b73f022 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -84,6 +84,9 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id); int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, u8 hlid); int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid); +void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, + enum ieee80211_band band); +int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl); int wl12xx_cmd_config_fwlog(struct wl1271 *wl); int wl12xx_cmd_start_fwlog(struct wl1271 *wl); int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); @@ -623,6 +626,13 @@ enum wl12xx_fwlogger_output { WL12XX_FWLOG_OUTPUT_HOST, }; +struct wl12xx_cmd_regdomain_dfs_config { + struct wl1271_cmd_header header; + + __le32 ch_bit_map1; + __le32 ch_bit_map2; +} __packed; + struct wl12xx_cmd_config_fwlog { struct wl1271_cmd_header header; diff --git a/drivers/net/wireless/ti/wlcore/event.h b/drivers/net/wireless/ti/wlcore/event.h index 766b02f15780..b4353be68ddb 100644 --- a/drivers/net/wireless/ti/wlcore/event.h +++ b/drivers/net/wireless/ti/wlcore/event.h @@ -54,6 +54,7 @@ enum { enum wlcore_wait_event { WLCORE_EVENT_ROLE_STOP_COMPLETE, WLCORE_EVENT_PEER_REMOVE_COMPLETE, + WLCORE_EVENT_DFS_CONFIG_COMPLETE }; enum { diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 06e1bf98f44e..68828b4adc06 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -681,6 +681,10 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + ret = wlcore_cmd_regdomain_config_locked(wl); + if (ret < 0) + return ret; + /* Bluetooth WLAN coexistence */ ret = wl1271_init_pta(wl); if (ret < 0) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 97e4a9c9da70..015d769ae9f7 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -93,6 +93,8 @@ static int wl1271_reg_notify(struct wiphy *wiphy, struct ieee80211_supported_band *band; struct ieee80211_channel *ch; int i; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct wl1271 *wl = hw->priv; band = wiphy->bands[IEEE80211_BAND_5GHZ]; for (i = 0; i < band->n_channels; i++) { @@ -106,6 +108,9 @@ static int wl1271_reg_notify(struct wiphy *wiphy, } + if (likely(wl->state == WLCORE_STATE_ON)) + wlcore_regdomain_config(wl); + return 0; } @@ -1900,6 +1905,12 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) wl->tx_res_if = NULL; kfree(wl->target_mem_map); wl->target_mem_map = NULL; + + /* + * FW channels must be re-calibrated after recovery, + * clear the last Reg-Domain channel configuration. + */ + memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last)); } static void wlcore_op_stop(struct ieee80211_hw *hw) @@ -3284,6 +3295,29 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, } EXPORT_SYMBOL_GPL(wlcore_set_key); +void wlcore_regdomain_config(struct wl1271 *wl) +{ + int ret; + + if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF)) + return; + + mutex_lock(&wl->mutex); + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wlcore_cmd_regdomain_config_locked(wl); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static int wl1271_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 9ee0ec6fd1db..4665b9615dc9 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -97,6 +97,10 @@ static void wl1271_rx_status(struct wl1271 *wl, wl1271_warning("Michael MIC error"); } } + + if (beacon) + wlcore_set_pending_regdomain_ch(wl, (u16)desc->channel, + status->band); } static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index eeb61889833f..c9137004982e 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -80,6 +80,8 @@ void wl1271_scan_complete_work(struct work_struct *work) wl12xx_queue_recovery_work(wl); } + wlcore_cmd_regdomain_config_locked(wl); + ieee80211_scan_completed(wl->hw, false); out: diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index e9254b78bac5..d3818ef1bc98 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -284,6 +284,11 @@ struct wl1271 { struct work_struct recovery_work; bool watchdog_recovery; + /* Reg domain last configuration */ + u32 reg_ch_conf_last[2]; + /* Reg domain pending configuration */ + u32 reg_ch_conf_pending[2]; + /* Pointer that holds DMA-friendly block for the mailbox */ void *mbox; @@ -445,6 +450,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); +void wlcore_regdomain_config(struct wl1271 *wl); static inline void wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, @@ -503,6 +509,10 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, /* separate probe response templates for one-shot and sched scans */ #define WLCORE_QUIRK_DUAL_PROBE_TMPL BIT(10) +/* Firmware requires reg domain configuration for active calibration */ +#define WLCORE_QUIRK_REGDOMAIN_CONF BIT(11) + + /* TODO: move to the lower drivers when all usages are abstracted */ #define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG20 (0x4030111) From 1019975640ec16d1753c06ac99207f4af6a721a2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:23 +0200 Subject: [PATCH 035/478] wlcore: call ieee80211_sched_scan_stopped on interface removal The interface might go down before we got the SCHED_STOPPED event, so make sure to call ieee80211_sched_scan_stopped() if the scanned interface is removed. Replace sched_scanning with sched_vif in order to save the scanned interface. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 1 - drivers/net/wireless/ti/wlcore/event.c | 4 ++-- drivers/net/wireless/ti/wlcore/main.c | 16 +++++++--------- drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 80376d461144..1cf2cdbae292 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -490,7 +490,6 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_HEX(chip.id); DRIVER_STATE_PRINT_STR(chip.fw_ver_str); DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str); - DRIVER_STATE_PRINT_INT(sched_scanning); #undef DRIVER_STATE_PRINT_INT #undef DRIVER_STATE_PRINT_LONG diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 3c20393d255e..cb32c021a9f6 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -109,9 +109,9 @@ void wlcore_event_sched_scan_completed(struct wl1271 *wl, wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)", status); - if (wl->sched_scanning) { + if (wl->sched_vif) { ieee80211_sched_scan_stopped(wl->hw); - wl->sched_scanning = false; + wl->sched_vif = NULL; } } EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 015d769ae9f7..cce73c417dec 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -923,11 +923,6 @@ static void wl1271_recovery_work(struct work_struct *work) /* Prevent spurious TX during FW restart */ wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); - if (wl->sched_scanning) { - ieee80211_sched_scan_stopped(wl->hw); - wl->sched_scanning = false; - } - /* reboot the chipset */ while (!list_empty(&wl->wlvif_list)) { wlvif = list_first_entry(&wl->wlvif_list, @@ -1871,7 +1866,6 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) wl->time_offset = 0; wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; - wl->sched_scanning = false; wl->sleep_auth = WL1271_PSM_ILLEGAL; memset(wl->roles_map, 0, sizeof(wl->roles_map)); memset(wl->links_map, 0, sizeof(wl->links_map)); @@ -2405,6 +2399,11 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, ieee80211_scan_completed(wl->hw, true); } + if (wl->sched_vif == wlvif) { + ieee80211_sched_scan_stopped(wl->hw); + wl->sched_vif = NULL; + } + if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { /* disable active roles */ ret = wl1271_ps_elp_wakeup(wl); @@ -3439,7 +3438,7 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - wl->sched_scanning = true; + wl->sched_vif = wlvif; out_sleep: wl1271_ps_elp_sleep(wl); @@ -3928,7 +3927,7 @@ static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif, wlvif->band); /* we only support sched_scan while not connected */ - if (wl->sched_scanning) + if (wl->sched_vif == wlvif) wl->ops->sched_scan_stop(wl, wlvif); ret = wl1271_acx_sta_rate_policies(wl, wlvif); @@ -5638,7 +5637,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, wl->ap_fw_ps_map = 0; wl->quirks = 0; wl->platform_quirks = 0; - wl->sched_scanning = false; wl->system_hlid = WL12XX_SYSTEM_HLID; wl->active_sta_count = 0; wl->fwlog_size = 0; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index d3818ef1bc98..b31589792dfd 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -307,7 +307,7 @@ struct wl1271 { struct ieee80211_vif *roc_vif; struct delayed_work roc_complete_work; - bool sched_scanning; + struct wl12xx_vif *sched_vif; /* The current band */ enum ieee80211_band band; From 6507babab4f7fe6c51c20abedd66d7449b7a9aca Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 22 Nov 2012 18:06:24 +0200 Subject: [PATCH 036/478] wl18xx: make driver operational again we have done updating the driver to the new fw api, so make the driver operational again. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index f1eaf9aebb1e..bb67d701f8a4 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1415,10 +1415,6 @@ static int wl18xx_setup(struct wl1271 *wl) struct wl18xx_priv *priv = wl->priv; int ret; - wl1271_error("driver is in transitional commit (due to fw api" - "change) and can't be booted!"); - return -EINVAL; - wl->rtable = wl18xx_rtable; wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS; wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS; From 7c482c1040ae54e89a8fd4d6415577070d5a915d Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 26 Nov 2012 18:05:40 +0200 Subject: [PATCH 037/478] wlcore: configure dwell times according to scan type Allow configuring different dwell times to the different scan types (regular and scheduled). Add new configuration entry (dwell_time_dfs) to conf_scan_settings, in order to allow setting different values for normal scan and scheduled scan. Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 4 +- drivers/net/wireless/ti/wl12xx/scan.c | 7 ++-- drivers/net/wireless/ti/wl18xx/main.c | 4 +- drivers/net/wireless/ti/wl18xx/scan.c | 6 ++- drivers/net/wireless/ti/wl18xx/scan.h | 7 ---- drivers/net/wireless/ti/wlcore/conf.h | 18 +++------ drivers/net/wireless/ti/wlcore/scan.c | 57 ++++++++++++++++++--------- drivers/net/wireless/ti/wlcore/scan.h | 9 ++++- 8 files changed, 64 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 7138fe49c63d..17ee0fd14bcc 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -267,8 +267,8 @@ static struct wlcore_conf wl12xx_conf = { .scan = { .min_dwell_time_active = 7500, .max_dwell_time_active = 30000, - .min_dwell_time_passive = 100000, - .max_dwell_time_passive = 100000, + .dwell_time_passive = 100000, + .dwell_time_dfs = 150000, .num_probe_reqs = 2, .split_scan_timeout = 50000, }, diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c index a99e8764162d..affdb3ec6225 100644 --- a/drivers/net/wireless/ti/wl12xx/scan.c +++ b/drivers/net/wireless/ti/wl12xx/scan.c @@ -68,9 +68,9 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, cpu_to_le32(c->max_dwell_time_active); } else { channels[j].min_duration = - cpu_to_le32(c->min_dwell_time_passive); + cpu_to_le32(c->dwell_time_passive); channels[j].max_duration = - cpu_to_le32(c->max_dwell_time_passive); + cpu_to_le32(c->dwell_time_passive); } channels[j].early_termination = 0; channels[j].tx_power_att = req->channels[i]->max_power; @@ -364,7 +364,8 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, } if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels, - req->n_channels, req->n_ssids)) { + req->n_channels, req->n_ssids, + SCAN_TYPE_PERIODIC)) { wl1271_error("scan channel list is empty"); ret = -EINVAL; goto out; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index bb67d701f8a4..c015231d295f 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -394,8 +394,8 @@ static struct wlcore_conf wl18xx_conf = { .scan = { .min_dwell_time_active = 7500, .max_dwell_time_active = 30000, - .min_dwell_time_passive = 100000, - .max_dwell_time_passive = 100000, + .dwell_time_passive = 100000, + .dwell_time_dfs = 150000, .num_probe_reqs = 2, .split_scan_timeout = 50000, }, diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c index f31d0d480d1b..daaebad78e1c 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.c +++ b/drivers/net/wireless/ti/wl18xx/scan.c @@ -84,7 +84,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, } wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, - req->n_channels, req->n_ssids); + req->n_channels, req->n_ssids, + SCAN_TYPE_SEARCH); wl18xx_adjust_channels(cmd, cmd_channels); /* @@ -214,7 +215,8 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl, /* configure channels */ wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, - req->n_channels, req->n_ssids); + req->n_channels, req->n_ssids, + SCAN_TYPE_PERIODIC); wl18xx_adjust_channels(cmd, cmd_channels); cmd->short_cycles_sec = 0; diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h index 0e026ecb311e..5eb8c7fc1567 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.h +++ b/drivers/net/wireless/ti/wl18xx/scan.h @@ -35,13 +35,6 @@ struct tracking_ch_params { u8 padding[2]; } __packed; -enum -{ - SCAN_TYPE_SEARCH = 0, - SCAN_TYPE_PERIODIC = 1, - SCAN_TYPE_TRACKING = 2, -}; - /* probe request rate */ enum { diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index a5d190816f34..c9de4d5c8965 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -1059,19 +1059,11 @@ struct conf_scan_settings { */ u32 max_dwell_time_active; - /* - * The minimum time to wait on each channel for passive scans - * - * Range: u32 tu/1000 - */ - u32 min_dwell_time_passive; + /* time to wait on the channel for passive scans (in TU/1000) */ + u32 dwell_time_passive; - /* - * The maximum time to wait on each channel for passive scans - * - * Range: u32 tu/1000 - */ - u32 max_dwell_time_passive; + /* time to wait on the channel for DFS scans (in TU/1000) */ + u32 dwell_time_dfs; /* * Number of probe requests to transmit on each active scan channel @@ -1281,7 +1273,7 @@ struct conf_hangover_settings { * version, the two LSB are the lower driver's private conf * version. */ -#define WLCORE_CONF_VERSION (0x0002 << 16) +#define WLCORE_CONF_VERSION (0x0003 << 16) #define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ sizeof(struct wlcore_conf)) diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index c9137004982e..7f42f8a4d90b 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -97,29 +97,44 @@ wlcore_scan_get_channels(struct wl1271 *wl, struct conn_scan_ch_params *channels, u32 band, bool radar, bool passive, int start, int max_channels, - u8 *n_pactive_ch) + u8 *n_pactive_ch, + int scan_type) { - struct conf_sched_scan_settings *c = &wl->conf.sched_scan; int i, j; u32 flags; bool force_passive = !n_ssids; - u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe; + u32 min_dwell_time_active, max_dwell_time_active; u32 dwell_time_passive, dwell_time_dfs; - if (band == IEEE80211_BAND_5GHZ) - delta_per_probe = c->dwell_time_delta_per_probe_5; - else - delta_per_probe = c->dwell_time_delta_per_probe; + /* configure dwell times according to scan type */ + if (scan_type == SCAN_TYPE_SEARCH) { + struct conf_scan_settings *c = &wl->conf.scan; - min_dwell_time_active = c->base_dwell_time + - n_ssids * c->num_probe_reqs * delta_per_probe; + min_dwell_time_active = c->min_dwell_time_active; + max_dwell_time_active = c->max_dwell_time_active; + dwell_time_passive = c->dwell_time_passive; + dwell_time_dfs = c->dwell_time_dfs; + } else { + struct conf_sched_scan_settings *c = &wl->conf.sched_scan; + u32 delta_per_probe; - max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta; + if (band == IEEE80211_BAND_5GHZ) + delta_per_probe = c->dwell_time_delta_per_probe_5; + else + delta_per_probe = c->dwell_time_delta_per_probe; + min_dwell_time_active = c->base_dwell_time + + n_ssids * c->num_probe_reqs * delta_per_probe; + + max_dwell_time_active = min_dwell_time_active + + c->max_dwell_time_delta; + dwell_time_passive = c->dwell_time_passive; + dwell_time_dfs = c->dwell_time_dfs; + } min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000); max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000); - dwell_time_passive = DIV_ROUND_UP(c->dwell_time_passive, 1000); - dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000); + dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000); + dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000); for (i = 0, j = start; i < n_channels && j < max_channels; @@ -195,7 +210,8 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, struct wlcore_scan_channels *cfg, struct ieee80211_channel *channels[], u32 n_channels, - u32 n_ssids) + u32 n_ssids, + int scan_type) { u8 n_pactive_ch = 0; @@ -208,7 +224,8 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, IEEE80211_BAND_2GHZ, false, true, 0, MAX_CHANNELS_2GHZ, - &n_pactive_ch); + &n_pactive_ch, + scan_type); cfg->active[0] = wlcore_scan_get_channels(wl, channels, @@ -219,7 +236,8 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, false, false, cfg->passive[0], MAX_CHANNELS_2GHZ, - &n_pactive_ch); + &n_pactive_ch, + scan_type); cfg->passive[1] = wlcore_scan_get_channels(wl, channels, @@ -229,7 +247,8 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, IEEE80211_BAND_5GHZ, false, true, 0, wl->max_channels_5, - &n_pactive_ch); + &n_pactive_ch, + scan_type); cfg->dfs = wlcore_scan_get_channels(wl, channels, @@ -240,7 +259,8 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, true, true, cfg->passive[1], wl->max_channels_5, - &n_pactive_ch); + &n_pactive_ch, + scan_type); cfg->active[1] = wlcore_scan_get_channels(wl, channels, @@ -251,7 +271,8 @@ wlcore_set_scan_chan_params(struct wl1271 *wl, false, false, cfg->passive[1] + cfg->dfs, wl->max_channels_5, - &n_pactive_ch); + &n_pactive_ch, + scan_type); /* 802.11j channels are not supported yet */ cfg->passive[2] = 0; diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index 8465f35c6a06..9ae606555aeb 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -150,12 +150,19 @@ struct wlcore_scan_channels { struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; }; +enum { + SCAN_TYPE_SEARCH = 0, + SCAN_TYPE_PERIODIC = 1, + SCAN_TYPE_TRACKING = 2, +}; + bool wlcore_set_scan_chan_params(struct wl1271 *wl, struct wlcore_scan_channels *cfg, struct ieee80211_channel *channels[], u32 n_channels, - u32 n_ssids); + u32 n_ssids, + int scan_type); int wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, From 5f9b67770be4201f4449b0f180effecaac4e2686 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 26 Nov 2012 18:05:41 +0200 Subject: [PATCH 038/478] wlcore: use new set bandwidth command to adjusting channel BW We support changing the channel BW when we started the STA role on a 40Mhz bandwidth. Otherwise a reconnection is required. Save the started channel width and use it when channel width updates arrive. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/acx.c | 31 ++++++++++++++++++ drivers/net/wireless/ti/wl18xx/acx.h | 14 +++++++++ drivers/net/wireless/ti/wl18xx/main.c | 38 +++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/cmd.c | 1 + drivers/net/wireless/ti/wlcore/hw_ops.h | 8 +++++ drivers/net/wireless/ti/wlcore/main.c | 12 +++++++ drivers/net/wireless/ti/wlcore/wlcore.h | 2 ++ drivers/net/wireless/ti/wlcore/wlcore_i.h | 2 ++ 8 files changed, 108 insertions(+) diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index adff9cde7db7..801d8af5cef2 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -109,3 +109,34 @@ out: kfree(acx); return ret; } + +int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide) +{ + struct wlcore_peer_ht_operation_mode *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d", + hlid, wide); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->hlid = hlid; + acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ; + + ret = wl1271_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx, + sizeof(*acx)); + + if (ret < 0) { + wl1271_warning("acx peer ht operation mode failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; + +} diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index 394d12500834..b57e3483509d 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -284,10 +284,24 @@ struct wl18xx_acx_clear_statistics { struct acx_header header; }; +enum wlcore_bandwidth { + WLCORE_BANDWIDTH_20MHZ, + WLCORE_BANDWIDTH_40MHZ, +}; + +struct wlcore_peer_ht_operation_mode { + struct acx_header header; + + u8 hlid; + u8 bandwidth; /* enum wlcore_bandwidth */ + u8 padding[2]; +}; + int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); int wl18xx_acx_set_checksum_state(struct wl1271 *wl); int wl18xx_acx_clear_statistics(struct wl1271 *wl); +int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide); #endif /* __WL18XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c015231d295f..c616c21bd6b7 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1319,6 +1319,43 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl, return buf_offset; } +static void wl18xx_sta_rc_update(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_sta *sta, + u32 changed) +{ + bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide); + + if (!(changed & IEEE80211_RC_BW_CHANGED)) + return; + + mutex_lock(&wl->mutex); + + /* sanity */ + if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS)) + goto out; + + /* ignore the change before association */ + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + goto out; + + /* + * If we started out as wide, we can change the operation mode. If we + * thought this was a 20mhz AP, we have to reconnect + */ + if (wlvif->sta.role_chan_type == NL80211_CHAN_HT40MINUS || + wlvif->sta.role_chan_type == NL80211_CHAN_HT40PLUS) + wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide); + else + ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif)); + +out: + mutex_unlock(&wl->mutex); +} + + static int wl18xx_setup(struct wl1271 *wl); static struct wlcore_ops wl18xx_ops = { @@ -1354,6 +1391,7 @@ static struct wlcore_ops wl18xx_ops = { .set_key = wl18xx_set_key, .channel_switch = wl18xx_cmd_channel_switch, .pre_pkt_send = wl18xx_pre_pkt_send, + .sta_rc_update = wl18xx_sta_rc_update, }; /* HT cap appropriate for wide channels in 2Ghz */ diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 599c006f4c47..65f071d90727 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -480,6 +480,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) goto err_hlid; } + wlvif->sta.role_chan_type = wlvif->channel_type; goto out_free; err_hlid: diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 2673d783ec1e..0e0b6563a3ff 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -201,4 +201,12 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len) return buf_offset; } +static inline void +wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct ieee80211_sta *sta, u32 changed) +{ + if (wl->ops->sta_rc_update) + wl->ops->sta_rc_update(wl, wlvif, sta, changed); +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index cce73c417dec..59ad288a1b8f 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4948,6 +4948,17 @@ static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw) return 0; } +static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct wl1271 *wl = hw->priv; + + wlcore_hw_sta_rc_update(wl, wlvif, sta, changed); +} + static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; @@ -5146,6 +5157,7 @@ static const struct ieee80211_ops wl1271_ops = { .change_chanctx = wlcore_op_change_chanctx, .assign_vif_chanctx = wlcore_op_assign_vif_chanctx, .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, + .sta_rc_update = wlcore_op_sta_rc_update, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index b31589792dfd..037c56e6cae0 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -101,6 +101,8 @@ struct wlcore_ops { struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len); + void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct ieee80211_sta *sta, u32 changed); }; enum wlcore_partitions { diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 6be1e8ef55bc..e3a77aa932d2 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -341,6 +341,8 @@ struct wl12xx_vif { u8 klv_template_id; bool qos; + /* channel type we started the STA role with */ + enum nl80211_channel_type role_chan_type; } sta; struct { u8 global_hlid; From 847cbebd527c2ba184f1951802d91e043fd81b47 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 26 Nov 2012 18:05:42 +0200 Subject: [PATCH 039/478] wlcore: don't stop fwlog if dbgpins are used Due to a bug, the fw asserts on fw log stop when dbg-pins are used. Don't stop the fw log in this case. Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 59ad288a1b8f..603cafc06a58 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -805,11 +805,13 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) /* * Make sure the chip is awake and the logger isn't active. - * Do not send a stop fwlog command if the fw is hanged. + * Do not send a stop fwlog command if the fw is hanged or if + * dbgpins are used (due to some fw bug). */ if (wl1271_ps_elp_wakeup(wl)) goto out; - if (!wl->watchdog_recovery) + if (!wl->watchdog_recovery && + wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS) wl12xx_cmd_stop_fwlog(wl); /* Read the first memory block address */ From de40750f4b10aa236ae44a9a3f10f50998e1c345 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 26 Nov 2012 18:05:44 +0200 Subject: [PATCH 040/478] wlcore/wl18xx/wl12xx: separate channel count between chips 18xx chips are capable of staying on 2 channels at the same time. Introduce a chip-family specific parameter to set the number of channels in the interface-combinations published by the driver. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/main.c | 1 + drivers/net/wireless/ti/wlcore/main.c | 4 ++-- drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 17ee0fd14bcc..97cc79c1bcf0 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1676,6 +1676,7 @@ static int wl12xx_setup(struct wl1271 *wl) wl->rtable = wl12xx_rtable; wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS; wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS; + wl->num_channels = 1; wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES; wl->band_rate_to_idx = wl12xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c616c21bd6b7..18a01b404ea5 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1456,6 +1456,7 @@ static int wl18xx_setup(struct wl1271 *wl) wl->rtable = wl18xx_rtable; wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS; wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS; + wl->num_channels = 2; wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES; wl->band_rate_to_idx = wl18xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 603cafc06a58..4bdf3d7b947e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5464,10 +5464,9 @@ static const struct ieee80211_iface_limit wlcore_iface_limits[] = { }, }; -static const struct ieee80211_iface_combination +static struct ieee80211_iface_combination wlcore_iface_combinations[] = { { - .num_different_channels = 1, .max_interfaces = 3, .limits = wlcore_iface_limits, .n_limits = ARRAY_SIZE(wlcore_iface_limits), @@ -5570,6 +5569,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; /* allowed interface combinations */ + wlcore_iface_combinations[0].num_different_channels = wl->num_channels; wl->hw->wiphy->iface_combinations = wlcore_iface_combinations; wl->hw->wiphy->n_iface_combinations = ARRAY_SIZE(wlcore_iface_combinations); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 037c56e6cae0..5f580e56a19b 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -441,6 +441,9 @@ struct wl1271 { unsigned int min_fw_ver[NUM_FW_VER]; struct completion nvs_loading_complete; + + /* number of concurrent channels the HW supports */ + u32 num_channels; }; int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); From 426001a6aaffce040226a19ad2795ac26c10f942 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 26 Nov 2012 18:05:45 +0200 Subject: [PATCH 041/478] wlcore: use sta_state-based ROCs for AP mode Try an opportunistic ROC when a STA is first added and stop the ROC when the STA is removed or successfully authenticated. This would ensure we don't miss auth/assoc/EAPOL packets during connection Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 45 +++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/wlcore_i.h | 4 ++ 2 files changed, 49 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 4bdf3d7b947e..99ac6c518de3 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4486,6 +4486,45 @@ static int wl12xx_sta_remove(struct wl1271 *wl, return ret; } +static void wlcore_roc_if_possible(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + if (find_first_bit(wl->roc_map, + WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) + return; + + if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID)) + return; + + wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel); +} + +static void wlcore_update_inconn_sta(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct wl1271_station *wl_sta, + bool in_connection) +{ + if (in_connection) { + if (WARN_ON(wl_sta->in_connection)) + return; + wl_sta->in_connection = true; + if (!wlvif->inconn_count++) + wlcore_roc_if_possible(wl, wlvif); + } else { + if (!wl_sta->in_connection) + return; + + wl_sta->in_connection = false; + wlvif->inconn_count--; + if (WARN_ON(wlvif->inconn_count < 0)) + return; + + if (!wlvif->inconn_count) + if (test_bit(wlvif->role_id, wl->roc_map)) + wl12xx_croc(wl, wlvif->role_id); + } +} + static int wl12xx_update_sta_state(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, @@ -4508,6 +4547,8 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, ret = wl12xx_sta_add(wl, wlvif, sta); if (ret) return ret; + + wlcore_update_inconn_sta(wl, wlvif, wl_sta, true); } /* Remove station (AP mode) */ @@ -4516,6 +4557,8 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, new_state == IEEE80211_STA_NOTEXIST) { /* must not fail */ wl12xx_sta_remove(wl, wlvif, sta); + + wlcore_update_inconn_sta(wl, wlvif, wl_sta, false); } /* Authorize station (AP mode) */ @@ -4529,6 +4572,8 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, hlid); if (ret) return ret; + + wlcore_update_inconn_sta(wl, wlvif, wl_sta, false); } /* Authorize station */ diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index e3a77aa932d2..5a92cb211f29 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -315,6 +315,7 @@ struct wl12xx_rx_filter { struct wl1271_station { u8 hlid; + bool in_connection; }; struct wl12xx_vif { @@ -425,6 +426,9 @@ struct wl12xx_vif { struct delayed_work channel_switch_work; struct delayed_work connection_loss_work; + /* number of in connection stations */ + int inconn_count; + /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) From 873d2a403462b3b495603c0a9a39bd857575aa82 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 26 Nov 2012 18:05:46 +0200 Subject: [PATCH 042/478] wlcore: mask out CCK rates when starting GO mask out CCK rates from the AP's local rates when the interace is p2p interface (GO). Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 65f071d90727..fc508ee5517e 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -591,6 +591,8 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); + if (wlvif->p2p) + supported_rates &= ~CONF_TX_CCK_RATES; wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x", supported_rates); From 518b680a8eb3ef953c80c9ef1bd6502e107b36fb Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 26 Nov 2012 18:05:47 +0200 Subject: [PATCH 043/478] wlcore: move ps change handling to .bss_info_changed() Adapt the new mac80211 BSS_CHANGED_PS notification, and do the ps handling in mac80211's per-vif callback (.bss_info_changed), rather than in the per-device (.config) callback. Make sure to configure it only after association. Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 70 ++++++++++++--------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 99ac6c518de3..5d3ca6ce9d20 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2781,46 +2781,8 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif) static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_conf *conf, u32 changed) { - bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret; - if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) { - - if ((conf->flags & IEEE80211_CONF_PS) && - test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && - !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { - - int ps_mode; - char *ps_mode_str; - - if (wl->conf.conn.forced_ps) { - ps_mode = STATION_POWER_SAVE_MODE; - ps_mode_str = "forced"; - } else { - ps_mode = STATION_AUTO_PS_MODE; - ps_mode_str = "auto"; - } - - wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str); - - ret = wl1271_ps_set_mode(wl, wlvif, ps_mode); - - if (ret < 0) - wl1271_warning("enter %s ps failed %d", - ps_mode_str, ret); - - } else if (!(conf->flags & IEEE80211_CONF_PS) && - test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { - - wl1271_debug(DEBUG_PSM, "auto ps disabled"); - - ret = wl1271_ps_set_mode(wl, wlvif, - STATION_ACTIVE_MODE); - if (ret < 0) - wl1271_warning("exit auto ps failed %d", ret); - } - } - if (conf->power_level != wlvif->power_level) { ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level); if (ret < 0) @@ -4115,6 +4077,38 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } } + if (changed & BSS_CHANGED_PS) { + if ((bss_conf->ps) && + test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && + !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { + int ps_mode; + char *ps_mode_str; + + if (wl->conf.conn.forced_ps) { + ps_mode = STATION_POWER_SAVE_MODE; + ps_mode_str = "forced"; + } else { + ps_mode = STATION_AUTO_PS_MODE; + ps_mode_str = "auto"; + } + + wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str); + + ret = wl1271_ps_set_mode(wl, wlvif, ps_mode); + if (ret < 0) + wl1271_warning("enter %s ps failed %d", + ps_mode_str, ret); + } else if (!bss_conf->ps && + test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { + wl1271_debug(DEBUG_PSM, "auto ps disabled"); + + ret = wl1271_ps_set_mode(wl, wlvif, + STATION_ACTIVE_MODE); + if (ret < 0) + wl1271_warning("exit auto ps failed %d", ret); + } + } + /* Handle new association with HT. Do this after join. */ if (sta_exists && (changed & BSS_CHANGED_HT)) { From c108c90535ae29099de88187a90b8411bc1fe9a1 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 26 Nov 2012 18:05:49 +0200 Subject: [PATCH 044/478] wlcore: gather information about firmware stability It's sometimes useful to gather information about the firmware stability in long test runs, especially to see if problems are recurring frequently or not. With this commit we count the number of times a hardware recovery was issued and print it out during recovery and in the driver_state in debugfs. Signed-off-by: Luciano Coelho Signed-off-by: Igal Chernobelsky Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 1 + drivers/net/wireless/ti/wlcore/main.c | 4 +++- drivers/net/wireless/ti/wlcore/wlcore.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 1cf2cdbae292..e61fc2b01046 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -490,6 +490,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_HEX(chip.id); DRIVER_STATE_PRINT_STR(chip.fw_ver_str); DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str); + DRIVER_STATE_PRINT_INT(recovery_count); #undef DRIVER_STATE_PRINT_INT #undef DRIVER_STATE_PRINT_LONG diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 5d3ca6ce9d20..615b40dc981e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -879,7 +879,8 @@ static void wlcore_print_recovery(struct wl1271 *wl) if (ret < 0) return; - wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts); + wl1271_info("pc: 0x%x, hint_sts: 0x%08x count: %d", + pc, hint_sts, ++wl->recovery_count); wlcore_set_partition(wl, &wl->ptable[PART_WORK]); } @@ -5685,6 +5686,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, wl->flags = 0; wl->sg_enabled = true; wl->sleep_auth = WL1271_PSM_ILLEGAL; + wl->recovery_count = 0; wl->hw_pg_ver = -1; wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 5f580e56a19b..4d5c69ecb022 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -337,6 +337,8 @@ struct wl1271 { bool enable_11a; + int recovery_count; + /* Most recently reported noise in dBm */ s8 noise; From 7230341f254c9bce39e9576362f18b94854bc779 Mon Sep 17 00:00:00 2001 From: Yair Shapira Date: Mon, 26 Nov 2012 18:05:50 +0200 Subject: [PATCH 045/478] wlcore/wl18xx/wl12xx: add recovery settings to conf add support for recovery settings including bug_on_recovery and no_recovery options. These options can now be set using wl18xx-conf.bin file and wlconf tool. Signed-off-by: Yair Shapira Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 4 ++++ drivers/net/wireless/ti/wl18xx/main.c | 4 ++++ drivers/net/wireless/ti/wlcore/conf.h | 11 ++++++++++- drivers/net/wireless/ti/wlcore/main.c | 19 +++++++++++++------ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 97cc79c1bcf0..aa73ada06463 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -370,6 +370,10 @@ static struct wlcore_conf wl12xx_conf = { .increase_time = 1, .window_size = 16, }, + .recovery = { + .bug_on_recovery = 0, + .no_recovery = 0, + }, }; static struct wl12xx_priv_conf wl12xx_default_priv_conf = { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 18a01b404ea5..98d034b4530d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -492,6 +492,10 @@ static struct wlcore_conf wl18xx_conf = { .increase_time = 1, .window_size = 16, }, + .recovery = { + .bug_on_recovery = 0, + .no_recovery = 0, + }, }; static struct wl18xx_priv_conf wl18xx_default_priv_conf = { diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index c9de4d5c8965..1bd831c9e843 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -1268,12 +1268,20 @@ struct conf_hangover_settings { u8 window_size; } __packed; +struct conf_recovery_settings { + /* BUG() on fw recovery */ + u8 bug_on_recovery; + + /* Prevent HW recovery. FW will remain stuck. */ + u8 no_recovery; +} __packed; + /* * The conf version consists of 4 bytes. The two MSB are the wlcore * version, the two LSB are the lower driver's private conf * version. */ -#define WLCORE_CONF_VERSION (0x0003 << 16) +#define WLCORE_CONF_VERSION (0x0004 << 16) #define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ sizeof(struct wlcore_conf)) @@ -1301,6 +1309,7 @@ struct wlcore_conf { struct conf_fwlog fwlog; struct conf_rate_policy_settings rate; struct conf_hangover_settings hangover; + struct conf_recovery_settings recovery; } __packed; struct wlcore_conf_file { diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 615b40dc981e..4dfd533a81c1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -56,8 +56,8 @@ #define WL1271_BOOT_RETRIES 3 static char *fwlog_param; -static bool bug_on_recovery; -static bool no_recovery; +static int bug_on_recovery = -1; +static int no_recovery = -1; static void __wl1271_op_remove_interface(struct wl1271 *wl, struct ieee80211_vif *vif, @@ -306,6 +306,7 @@ out: static void wlcore_adjust_conf(struct wl1271 *wl) { /* Adjust settings according to optional module parameters */ + if (fwlog_param) { if (!strcmp(fwlog_param, "continuous")) { wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; @@ -321,6 +322,12 @@ static void wlcore_adjust_conf(struct wl1271 *wl) wl1271_error("Unknown fwlog parameter %s", fwlog_param); } } + + if (bug_on_recovery != -1) + wl->conf.recovery.bug_on_recovery = (u8) bug_on_recovery; + + if (no_recovery != -1) + wl->conf.recovery.no_recovery = (u8) no_recovery; } static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, @@ -903,10 +910,10 @@ static void wl1271_recovery_work(struct work_struct *work) wlcore_print_recovery(wl); } - BUG_ON(bug_on_recovery && + BUG_ON(wl->conf.recovery.bug_on_recovery && !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); - if (no_recovery) { + if (wl->conf.recovery.no_recovery) { wl1271_info("No recovery (chosen on module load). Fw will remain stuck."); goto out_unlock; } @@ -6013,10 +6020,10 @@ module_param_named(fwlog, fwlog_param, charp, 0); MODULE_PARM_DESC(fwlog, "FW logger options: continuous, ondemand, dbgpins or disable"); -module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR); +module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery"); -module_param(no_recovery, bool, S_IRUSR | S_IWUSR); +module_param(no_recovery, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck."); MODULE_LICENSE("GPL"); From 986f3aa1666b4ef1e9e30672c73e19742b8abfd9 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 27 Nov 2012 15:51:56 +0200 Subject: [PATCH 046/478] wlcore/wl12xx: move wl12xx chip ID defines to the lower driver Move wl12xx-specific chip ID macros to the wl12xx driver and rename them to 127X and 128X for clarity since both the "1" (2.4GHz) and the "3" (2.4GHz and 5GHz) variants use the same chip ID. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 26 ++++++++++++------------- drivers/net/wireless/ti/wl12xx/wl12xx.h | 6 ++++++ drivers/net/wireless/ti/wlcore/wlcore.h | 6 ------ 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index aa73ada06463..82440e1eb59c 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -607,7 +607,7 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) { int ret; - if (wl->chip.id != CHIP_ID_1283_PG20) { + if (wl->chip.id != CHIP_ID_128X_PG20) { struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; struct wl127x_rx_mem_pool_addr rx_mem_addr; @@ -637,7 +637,7 @@ static int wl12xx_identify_chip(struct wl1271 *wl) int ret = 0; switch (wl->chip.id) { - case CHIP_ID_1271_PG10: + case CHIP_ID_127X_PG10: wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", wl->chip.id); @@ -658,7 +658,7 @@ static int wl12xx_identify_chip(struct wl1271 *wl) WL127X_MINOR_VER); break; - case CHIP_ID_1271_PG20: + case CHIP_ID_127X_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", wl->chip.id); @@ -680,7 +680,7 @@ static int wl12xx_identify_chip(struct wl1271 *wl) WL127X_MINOR_VER); break; - case CHIP_ID_1283_PG20: + case CHIP_ID_128X_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)", wl->chip.id); wl->plt_fw_name = WL128X_PLT_FW_NAME; @@ -697,7 +697,7 @@ static int wl12xx_identify_chip(struct wl1271 *wl) WL128X_MAJOR_VER, WL128X_SUBTYPE_VER, WL128X_MINOR_VER); break; - case CHIP_ID_1283_PG10: + case CHIP_ID_128X_PG10: default: wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; @@ -1082,7 +1082,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl) u32 clk; int selected_clock = -1; - if (wl->chip.id == CHIP_ID_1283_PG20) { + if (wl->chip.id == CHIP_ID_128X_PG20) { ret = wl128x_boot_clk(wl, &selected_clock); if (ret < 0) goto out; @@ -1113,7 +1113,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); - if (wl->chip.id == CHIP_ID_1283_PG20) + if (wl->chip.id == CHIP_ID_128X_PG20) clk |= ((selected_clock & 0x3) << 1) << 4; else clk |= (priv->ref_clock << 1) << 4; @@ -1167,7 +1167,7 @@ static int wl12xx_pre_upload(struct wl1271 *wl) /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ - if (wl->chip.id == CHIP_ID_1283_PG20) { + if (wl->chip.id == CHIP_ID_128X_PG20) { ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); if (ret < 0) goto out; @@ -1293,7 +1293,7 @@ static void wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, u32 blks, u32 spare_blks) { - if (wl->chip.id == CHIP_ID_1283_PG20) { + if (wl->chip.id == CHIP_ID_128X_PG20) { desc->wl128x_mem.total_mem_blocks = blks; } else { desc->wl127x_mem.extra_blocks = spare_blks; @@ -1307,7 +1307,7 @@ wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, { u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len); - if (wl->chip.id == CHIP_ID_1283_PG20) { + if (wl->chip.id == CHIP_ID_128X_PG20) { desc->wl128x_mem.extra_bytes = aligned_len - skb->len; desc->length = cpu_to_le16(aligned_len >> 2); @@ -1371,7 +1371,7 @@ static int wl12xx_hw_init(struct wl1271 *wl) { int ret; - if (wl->chip.id == CHIP_ID_1283_PG20) { + if (wl->chip.id == CHIP_ID_128X_PG20) { u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; ret = wl128x_cmd_general_parms(wl); @@ -1458,7 +1458,7 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl) bool supported = false; u8 major, minor; - if (wl->chip.id == CHIP_ID_1283_PG20) { + if (wl->chip.id == CHIP_ID_128X_PG20) { major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver); minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver); @@ -1514,7 +1514,7 @@ static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver) u16 die_info; int ret; - if (wl->chip.id == CHIP_ID_1283_PG20) + if (wl->chip.id == CHIP_ID_128X_PG20) ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1, &die_info); else diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index 7182bbf6625d..634ad0086c56 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -24,6 +24,12 @@ #include "conf.h" +/* WiLink 6/7 chip IDs */ +#define CHIP_ID_127X_PG10 (0x04030101) +#define CHIP_ID_127X_PG20 (0x04030111) +#define CHIP_ID_128X_PG10 (0x05030101) +#define CHIP_ID_128X_PG20 (0x05030111) + /* minimum FW required for driver for wl127x */ #define WL127X_CHIP_VER 6 #define WL127X_IFTYPE_VER 3 diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 4d5c69ecb022..c28e89190ac6 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -520,12 +520,6 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, #define WLCORE_QUIRK_REGDOMAIN_CONF BIT(11) -/* TODO: move to the lower drivers when all usages are abstracted */ -#define CHIP_ID_1271_PG10 (0x4030101) -#define CHIP_ID_1271_PG20 (0x4030111) -#define CHIP_ID_1283_PG10 (0x05030101) -#define CHIP_ID_1283_PG20 (0x05030111) - /* TODO: move all these common registers and values elsewhere */ #define HW_ACCESS_ELP_CTRL_REG 0x1FFFC From c3552c06256cf7ac787ab8db0d3c8317be48bba1 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 27 Nov 2012 15:51:57 +0200 Subject: [PATCH 047/478] wlcore/wl12xx: remove deprecated FW version check We don't distinguish between STA and AP firmwares anymore, so the firmware version checking and quirks setting in wl12xx isn't needed anymore. Remove implementation of .identify_fw in wl12xx and deprecated definitions. Don't remove the op entirely from wlcore, because it may be needed for more fine-grained checking later. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 17 ----------------- drivers/net/wireless/ti/wlcore/wlcore_i.h | 11 ----------- 2 files changed, 28 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 82440e1eb59c..07f7f76a0783 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1426,22 +1426,6 @@ static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl, return wlvif->rate_set; } -static int wl12xx_identify_fw(struct wl1271 *wl) -{ - unsigned int *fw_ver = wl->chip.fw_ver; - - /* Only new station firmwares support routing fw logs to the host */ - if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && - (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN)) - wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED; - - /* This feature is not yet supported for AP mode */ - if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) - wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED; - - return 0; -} - static void wl12xx_conf_init(struct wl1271 *wl) { struct wl12xx_priv *priv = wl->priv; @@ -1626,7 +1610,6 @@ static int wl12xx_setup(struct wl1271 *wl); static struct wlcore_ops wl12xx_ops = { .setup = wl12xx_setup, .identify_chip = wl12xx_identify_chip, - .identify_fw = wl12xx_identify_fw, .boot = wl12xx_boot, .plt_init = wl12xx_plt_init, .trigger_cmd = wl12xx_trigger_cmd, diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 5a92cb211f29..d6854b9991af 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -109,17 +109,6 @@ enum { NUM_FW_VER }; -#define FW_VER_CHIP_WL127X 6 -#define FW_VER_CHIP_WL128X 7 - -#define FW_VER_IF_TYPE_STA 1 -#define FW_VER_IF_TYPE_AP 2 - -#define FW_VER_MINOR_1_SPARE_STA_MIN 58 -#define FW_VER_MINOR_1_SPARE_AP_MIN 47 - -#define FW_VER_MINOR_FWLOG_STA_MIN 70 - struct wl1271_chip { u32 id; char fw_ver_str[ETHTOOL_BUSINFO_LEN]; From af4e94c56581b446b2010854a983ef722ad9ed1f Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 27 Nov 2012 15:51:58 +0200 Subject: [PATCH 048/478] wlcore: change way of checking the firmware version The firmwares version string contain 5 integers. We used to consider all the digits (except for the first one, which indicates the chip) as linearly increasing version numbers. This is not correct, because some of the integers indicate type of firmware (eg. single-role vs. multi-role) or the internal project it was created for. Besides, this varies a bit from chip to chip, so we need to make the firmware version checks more flexible (eg. allow the lower driver to ignore some of the integers). Additionally, we need to change the code so that we only check for a linearly increasing number on the fields where this actually makes sense. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/boot.c | 55 ++++++++++++++----------- drivers/net/wireless/ti/wlcore/wlcore.h | 3 ++ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 230c765ab401..2c57246b6a85 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -85,46 +85,55 @@ static int wlcore_validate_fw_ver(struct wl1271 *wl) { unsigned int *fw_ver = wl->chip.fw_ver; unsigned int *min_ver = wl->min_fw_ver; + char min_fw_str[32] = ""; + int i; /* the chip must be exactly equal */ - if (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]) + if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP])) goto fail; - /* always check the next digit if all previous ones are equal */ - - if (min_ver[FW_VER_IF_TYPE] < fw_ver[FW_VER_IF_TYPE]) - goto out; - else if (min_ver[FW_VER_IF_TYPE] > fw_ver[FW_VER_IF_TYPE]) + /* the firmware type must be equal */ + if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE])) goto fail; - if (min_ver[FW_VER_MAJOR] < fw_ver[FW_VER_MAJOR]) - goto out; - else if (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]) + /* the project number must be equal */ + if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE])) goto fail; - if (min_ver[FW_VER_SUBTYPE] < fw_ver[FW_VER_SUBTYPE]) - goto out; - else if (min_ver[FW_VER_SUBTYPE] > fw_ver[FW_VER_SUBTYPE]) + /* the API version must be greater or equal */ + if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR])) goto fail; - if (min_ver[FW_VER_MINOR] < fw_ver[FW_VER_MINOR]) - goto out; - else if (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR]) + /* if the API version is equal... */ + if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) || + (min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) && + /* ...the minor must be greater or equal */ + ((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) && + (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR]))) goto fail; -out: return 0; fail: - wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is outdated.\n" - "Please use at least FW %u.%u.%u.%u.%u.\n" - "You can get more information at:\n" - "http://wireless.kernel.org/en/users/Drivers/wl12xx", + for (i = 0; i < NUM_FW_VER; i++) + if (min_ver[i] == WLCORE_FW_VER_IGNORE) + snprintf(min_fw_str, sizeof(min_fw_str), + "%s*.", min_fw_str); + else + snprintf(min_fw_str, sizeof(min_fw_str), + "%s%u.", min_fw_str, min_ver[i]); + + wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n" + "Please use at least FW %s\n" + "You can get the latest firmwares at:\n" + "git://github.com/TI-OpenLink/firmwares.git", fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE], fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE], - fw_ver[FW_VER_MINOR], min_ver[FW_VER_CHIP], - min_ver[FW_VER_IF_TYPE], min_ver[FW_VER_MAJOR], - min_ver[FW_VER_SUBTYPE], min_ver[FW_VER_MINOR]); + fw_ver[FW_VER_MINOR], min_fw_str); return -EINVAL; } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index c28e89190ac6..66b8306883d2 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -466,6 +466,9 @@ wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap)); } +/* Tell wlcore not to care about this element when checking the version */ +#define WLCORE_FW_VER_IGNORE -1 + static inline void wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, unsigned int iftype, unsigned int major, From b3ec9cf2052981171364c4f354f53a64089ecc75 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 27 Nov 2012 15:51:59 +0200 Subject: [PATCH 049/478] wl12xx: ignore some of the firmware version fields The firmware type and the project number fields in the firmware version number, cannot be checked as if they increase sequentially, because the former is the firmware type and the latter is an internal project number. There's no guarantee that these numbers will remain incremental, so use WLCORE_FW_VER_IGNORE. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/wl12xx.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index 634ad0086c56..310df5200c26 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -32,16 +32,16 @@ /* minimum FW required for driver for wl127x */ #define WL127X_CHIP_VER 6 -#define WL127X_IFTYPE_VER 3 +#define WL127X_IFTYPE_VER WLCORE_FW_VER_IGNORE #define WL127X_MAJOR_VER 10 -#define WL127X_SUBTYPE_VER 2 +#define WL127X_SUBTYPE_VER WLCORE_FW_VER_IGNORE #define WL127X_MINOR_VER 115 /* minimum FW required for driver for wl128x */ #define WL128X_CHIP_VER 7 -#define WL128X_IFTYPE_VER 3 +#define WL128X_IFTYPE_VER WLCORE_FW_VER_IGNORE #define WL128X_MAJOR_VER 10 -#define WL128X_SUBTYPE_VER 2 +#define WL128X_SUBTYPE_VER WLCORE_FW_VER_IGNORE #define WL128X_MINOR_VER 115 #define WL12XX_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) From 8675f9abdf5b67a3f621fa99e1f0e0c8d8ae2531 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 27 Nov 2012 15:52:00 +0200 Subject: [PATCH 050/478] wlcore/wl12xx/wl18xx: verify multi-role and single-role fw versions Previously we were only checking the single-role firmware version. Now add code to check for the firmware versions separately for each firmware type. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 24 ++++++++++++------- drivers/net/wireless/ti/wl12xx/wl12xx.h | 32 +++++++++++++++++-------- drivers/net/wireless/ti/wl18xx/main.c | 8 ++++--- drivers/net/wireless/ti/wlcore/boot.c | 3 ++- drivers/net/wireless/ti/wlcore/wlcore.h | 29 +++++++++++++++------- 5 files changed, 64 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 07f7f76a0783..37e577ff17cb 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -653,9 +653,11 @@ static int wl12xx_identify_chip(struct wl1271 *wl) /* read data preparation is only needed by wl127x */ wl->ops->prepare_read = wl127x_prepare_read; - wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER, - WL127X_MAJOR_VER, WL127X_SUBTYPE_VER, - WL127X_MINOR_VER); + wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, + WL127X_IFTYPE_SR_VER, WL127X_MAJOR_SR_VER, + WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER, + WL127X_IFTYPE_MR_VER, WL127X_MAJOR_MR_VER, + WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER); break; case CHIP_ID_127X_PG20: @@ -675,9 +677,11 @@ static int wl12xx_identify_chip(struct wl1271 *wl) /* read data preparation is only needed by wl127x */ wl->ops->prepare_read = wl127x_prepare_read; - wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER, - WL127X_MAJOR_VER, WL127X_SUBTYPE_VER, - WL127X_MINOR_VER); + wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, + WL127X_IFTYPE_SR_VER, WL127X_MAJOR_SR_VER, + WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER, + WL127X_IFTYPE_MR_VER, WL127X_MAJOR_MR_VER, + WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER); break; case CHIP_ID_128X_PG20: @@ -693,9 +697,11 @@ static int wl12xx_identify_chip(struct wl1271 *wl) WLCORE_QUIRK_TKIP_HEADER_SPACE | WLCORE_QUIRK_START_STA_FAILS; - wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER, - WL128X_MAJOR_VER, WL128X_SUBTYPE_VER, - WL128X_MINOR_VER); + wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, + WL128X_IFTYPE_SR_VER, WL128X_MAJOR_SR_VER, + WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER, + WL128X_IFTYPE_MR_VER, WL128X_MAJOR_MR_VER, + WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER); break; case CHIP_ID_128X_PG10: default: diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index 310df5200c26..a07be5e022fb 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -30,19 +30,31 @@ #define CHIP_ID_128X_PG10 (0x05030101) #define CHIP_ID_128X_PG20 (0x05030111) -/* minimum FW required for driver for wl127x */ +/* FW chip version for wl127x */ #define WL127X_CHIP_VER 6 -#define WL127X_IFTYPE_VER WLCORE_FW_VER_IGNORE -#define WL127X_MAJOR_VER 10 -#define WL127X_SUBTYPE_VER WLCORE_FW_VER_IGNORE -#define WL127X_MINOR_VER 115 +/* minimum single-role FW version for wl127x */ +#define WL127X_IFTYPE_SR_VER 3 +#define WL127X_MAJOR_SR_VER 10 +#define WL127X_SUBTYPE_SR_VER WLCORE_FW_VER_IGNORE +#define WL127X_MINOR_SR_VER 115 +/* minimum multi-role FW version for wl127x */ +#define WL127X_IFTYPE_MR_VER 5 +#define WL127X_MAJOR_MR_VER 7 +#define WL127X_SUBTYPE_MR_VER WLCORE_FW_VER_IGNORE +#define WL127X_MINOR_MR_VER 115 -/* minimum FW required for driver for wl128x */ +/* FW chip version for wl128x */ #define WL128X_CHIP_VER 7 -#define WL128X_IFTYPE_VER WLCORE_FW_VER_IGNORE -#define WL128X_MAJOR_VER 10 -#define WL128X_SUBTYPE_VER WLCORE_FW_VER_IGNORE -#define WL128X_MINOR_VER 115 +/* minimum single-role FW version for wl128x */ +#define WL128X_IFTYPE_SR_VER 3 +#define WL128X_MAJOR_SR_VER 10 +#define WL128X_SUBTYPE_SR_VER WLCORE_FW_VER_IGNORE +#define WL128X_MINOR_SR_VER 115 +/* minimum multi-role FW version for wl128x */ +#define WL128X_IFTYPE_MR_VER 5 +#define WL128X_MAJOR_MR_VER 7 +#define WL128X_SUBTYPE_MR_VER WLCORE_FW_VER_IGNORE +#define WL128X_MINOR_MR_VER 42 #define WL12XX_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 98d034b4530d..08c3a2fc427e 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -623,9 +623,11 @@ static int wl18xx_identify_chip(struct wl1271 *wl) WLCORE_QUIRK_REGDOMAIN_CONF | WLCORE_QUIRK_DUAL_PROBE_TMPL; - wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER, - WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER, - WL18XX_MINOR_VER); + wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, + WL18XX_IFTYPE_VER, WL18XX_MAJOR_VER, + WL18XX_SUBTYPE_VER, WL18XX_MINOR_VER, + /* there's no separate multi-role FW */ + 0, 0, 0, 0); break; case CHIP_ID_185x_PG10: wl1271_warning("chip id 0x%x (185x PG10) is deprecated", diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 2c57246b6a85..b58ae5fc1487 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -84,7 +84,8 @@ out: static int wlcore_validate_fw_ver(struct wl1271 *wl) { unsigned int *fw_ver = wl->chip.fw_ver; - unsigned int *min_ver = wl->min_fw_ver; + unsigned int *min_ver = (wl->fw_type == WL12XX_FW_TYPE_NORMAL) ? + wl->min_sr_fw_ver : wl->min_mr_fw_ver; char min_fw_str[32] = ""; int i; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 66b8306883d2..32987d0a5f73 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -439,8 +439,11 @@ struct wl1271 { /* the number of allocated MAC addresses in this chip */ int num_mac_addr; - /* the minimum FW version required for the driver to work */ - unsigned int min_fw_ver[NUM_FW_VER]; + /* minimum FW version required for the driver to work in single-role */ + unsigned int min_sr_fw_ver[NUM_FW_VER]; + + /* minimum FW version required for the driver to work in multi-role */ + unsigned int min_mr_fw_ver[NUM_FW_VER]; struct completion nvs_loading_complete; @@ -471,14 +474,22 @@ wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, static inline void wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, - unsigned int iftype, unsigned int major, - unsigned int subtype, unsigned int minor) + unsigned int iftype_sr, unsigned int major_sr, + unsigned int subtype_sr, unsigned int minor_sr, + unsigned int iftype_mr, unsigned int major_mr, + unsigned int subtype_mr, unsigned int minor_mr) { - wl->min_fw_ver[FW_VER_CHIP] = chip; - wl->min_fw_ver[FW_VER_IF_TYPE] = iftype; - wl->min_fw_ver[FW_VER_MAJOR] = major; - wl->min_fw_ver[FW_VER_SUBTYPE] = subtype; - wl->min_fw_ver[FW_VER_MINOR] = minor; + wl->min_sr_fw_ver[FW_VER_CHIP] = chip; + wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr; + wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr; + wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr; + wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr; + + wl->min_mr_fw_ver[FW_VER_CHIP] = chip; + wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr; + wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr; + wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr; + wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr; } /* Firmware image load chunk size */ From 2f24456149110f4d9f0547429f1249d40143561d Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 27 Nov 2012 15:52:01 +0200 Subject: [PATCH 051/478] wl18xx: ignore irrelevant firmware version fields For wl18xx, only the chip ID and the minor version number are relevant. Ignore the other numbers which are either not used or relate to internal projects or internal branches. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/wl18xx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 4d295a574a87..3105fbf79ae4 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -27,8 +27,8 @@ /* minimum FW required for driver */ #define WL18XX_CHIP_VER 8 #define WL18XX_IFTYPE_VER 5 -#define WL18XX_MAJOR_VER 0 -#define WL18XX_SUBTYPE_VER 0 +#define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE +#define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE #define WL18XX_MINOR_VER 28 #define WL18XX_CMD_MAX_SIZE 740 From 3ea186d137f79da5e6af283e5803715da1eb6259 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 30 Nov 2012 00:50:06 +0200 Subject: [PATCH 052/478] wlcore/wl12xx: make sure session id is always zero for AP The 12xx FW doesn't support non-zero session ids for AP-mode. Introduce an appropriate quirk to make sure the session id is always zero when needed. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 9 ++++++--- drivers/net/wireless/ti/wlcore/tx.c | 9 +++++++-- drivers/net/wireless/ti/wlcore/wlcore.h | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 37e577ff17cb..d22b52f90d42 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -644,7 +644,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_TKIP_HEADER_SPACE | - WLCORE_QUIRK_START_STA_FAILS; + WLCORE_QUIRK_START_STA_FAILS | + WLCORE_QUIRK_AP_ZERO_SESSION_ID; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, @@ -667,7 +668,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_TKIP_HEADER_SPACE | - WLCORE_QUIRK_START_STA_FAILS; + WLCORE_QUIRK_START_STA_FAILS | + WLCORE_QUIRK_AP_ZERO_SESSION_ID; wl->plt_fw_name = WL127X_PLT_FW_NAME; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; @@ -695,7 +697,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_DUAL_PROBE_TMPL | WLCORE_QUIRK_TKIP_HEADER_SPACE | - WLCORE_QUIRK_START_STA_FAILS; + WLCORE_QUIRK_START_STA_FAILS | + WLCORE_QUIRK_AP_ZERO_SESSION_ID; wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_SR_VER, WL128X_MAJOR_SR_VER, diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 68f73f935b8e..c3607f09284b 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -293,9 +293,14 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ; } else if (wlvif) { + u8 session_id = wl->session_ids[hlid]; + + if ((wl->quirks & WLCORE_QUIRK_AP_ZERO_SESSION_ID) && + (wlvif->bss_type == BSS_TYPE_AP_BSS)) + session_id = 0; + /* configure the tx attributes */ - tx_attr = wl->session_ids[hlid] << - TX_HW_ATTR_OFST_SESSION_COUNTER; + tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER; } desc->hlid = hlid; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 32987d0a5f73..d90436dc9c0d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -533,6 +533,8 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, /* Firmware requires reg domain configuration for active calibration */ #define WLCORE_QUIRK_REGDOMAIN_CONF BIT(11) +/* The FW only support a zero session id for AP */ +#define WLCORE_QUIRK_AP_ZERO_SESSION_ID BIT(12) /* TODO: move all these common registers and values elsewhere */ #define HW_ACCESS_ELP_CTRL_REG 0x1FFFC From ec4f4b76a622822d5d47e99bf4062fc308a7d424 Mon Sep 17 00:00:00 2001 From: Ido Reis Date: Tue, 27 Nov 2012 08:44:42 +0200 Subject: [PATCH 053/478] wl18xx: update default mac/phy parameters Update mac/phy paramters according to the default HP SISO boards. Signed-off-by: Ido Reis Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/conf.h | 17 +++++++++++------ drivers/net/wireless/ti/wl18xx/main.c | 5 +++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index 4d426cc20274..1cb313fe368b 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -23,20 +23,21 @@ #define __WL18XX_CONF_H__ #define WL18XX_CONF_MAGIC 0x10e100ca -#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0003) +#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0004) #define WL18XX_CONF_MASK 0x0000ffff #define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \ sizeof(struct wl18xx_priv_conf)) #define NUM_OF_CHANNELS_11_ABG 150 #define NUM_OF_CHANNELS_11_P 7 -#define WL18XX_NUM_OF_SUB_BANDS 9 #define SRF_TABLE_LEN 16 #define PIN_MUXING_SIZE 2 +#define WL18XX_TRACE_LOSS_GAPS_TX 10 +#define WL18XX_TRACE_LOSS_GAPS_RX 18 struct wl18xx_mac_and_phy_params { u8 phy_standalone; - u8 rdl; + u8 spare0; u8 enable_clpc; u8 enable_tx_low_pwr_on_siso_rdl; u8 auto_detect; @@ -69,8 +70,8 @@ struct wl18xx_mac_and_phy_params { u8 pwr_limit_reference_11_abg; u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P]; u8 pwr_limit_reference_11p; - u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; - u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; + u8 spare1[9]; + u8 spare2[9]; u8 primary_clock_setting_time; u8 clock_valid_on_wake_up; u8 secondary_clock_setting_time; @@ -81,7 +82,11 @@ struct wl18xx_mac_and_phy_params { s8 low_power_val; s8 med_power_val; s8 high_power_val; - u8 padding[1]; + s8 per_sub_band_tx_trace_loss[WL18XX_TRACE_LOSS_GAPS_TX]; + s8 per_sub_band_rx_trace_loss[WL18XX_TRACE_LOSS_GAPS_RX]; + u8 tx_rf_margin; + + u8 padding[4]; } __packed; enum wl18xx_ht_mode { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 08c3a2fc427e..3588e340634a 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -508,7 +508,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .clock_valid_on_wake_up = 0x00, .secondary_clock_setting_time = 0x05, .board_type = BOARD_TYPE_HDK_18XX, - .rdl = 0x01, .auto_detect = 0x00, .dedicated_fem = FEM_NONE, .low_band_component = COMPONENT_3_WAY_SWITCH, @@ -525,13 +524,15 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .enable_tx_low_pwr_on_siso_rdl = 0x00, .rx_profile = 0x00, .pwr_limit_reference_11_abg = 0xc8, + .pwr_limit_reference_11p = 0xc8, .psat = 0, .low_power_val = 0x00, .med_power_val = 0x0a, - .high_power_val = 0x1e, + .high_power_val = 0x11, .external_pa_dc2dc = 0, .number_of_assembled_ant2_4 = 1, .number_of_assembled_ant5 = 1, + .tx_rf_margin = 1, }, }; From d88949b7def1e982871406f3ad3efa361ffa6ffc Mon Sep 17 00:00:00 2001 From: Yair Shapira Date: Tue, 27 Nov 2012 08:44:43 +0200 Subject: [PATCH 054/478] wl18xx: support 2nd set of mac/phy tx-power params First set (low, medium and high TX power values) is used for STA-HP background role. The 2nd set is used for other roles. Update other mac/phy parameters according to new FW. Signed-off-by: Yair Shapira Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho Conflicts: drivers/net/wireless/ti/wl18xx/main.c --- drivers/net/wireless/ti/wl18xx/conf.h | 10 +++++--- drivers/net/wireless/ti/wl18xx/main.c | 35 ++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index 1cb313fe368b..b5f114857191 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -23,7 +23,7 @@ #define __WL18XX_CONF_H__ #define WL18XX_CONF_MAGIC 0x10e100ca -#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0004) +#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0005) #define WL18XX_CONF_MASK 0x0000ffff #define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \ sizeof(struct wl18xx_priv_conf)) @@ -78,15 +78,19 @@ struct wl18xx_mac_and_phy_params { u8 board_type; /* enable point saturation */ u8 psat; - /* low/medium/high Tx power in dBm */ + /* low/medium/high Tx power in dBm for STA-HP BG */ s8 low_power_val; s8 med_power_val; s8 high_power_val; s8 per_sub_band_tx_trace_loss[WL18XX_TRACE_LOSS_GAPS_TX]; s8 per_sub_band_rx_trace_loss[WL18XX_TRACE_LOSS_GAPS_RX]; u8 tx_rf_margin; + /* low/medium/high Tx power in dBm for other role */ + s8 low_power_val_2nd; + s8 med_power_val_2nd; + s8 high_power_val_2nd; - u8 padding[4]; + u8 padding[1]; } __packed; enum wl18xx_ht_mode { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 3588e340634a..a45ecffef01a 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -523,14 +523,37 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .enable_clpc = 0x00, .enable_tx_low_pwr_on_siso_rdl = 0x00, .rx_profile = 0x00, - .pwr_limit_reference_11_abg = 0xc8, - .pwr_limit_reference_11p = 0xc8, + .pwr_limit_reference_11_abg = 0x64, + .per_chan_pwr_limit_arr_11abg = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + .pwr_limit_reference_11p = 0x64, + .per_chan_pwr_limit_arr_11p = { 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff }, .psat = 0, - .low_power_val = 0x00, - .med_power_val = 0x0a, - .high_power_val = 0x11, + .low_power_val = 0x08, + .med_power_val = 0x12, + .high_power_val = 0x18, + .low_power_val_2nd = 0x05, + .med_power_val_2nd = 0x0a, + .high_power_val_2nd = 0x14, .external_pa_dc2dc = 0, - .number_of_assembled_ant2_4 = 1, + .number_of_assembled_ant2_4 = 2, .number_of_assembled_ant5 = 1, .tx_rf_margin = 1, }, From ea5084356fb05093da9ff225fd2c2fcfd363371c Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 27 Nov 2012 08:44:45 +0200 Subject: [PATCH 055/478] wlcore: allow fw commands to fail Currently, each fw command/acx that return a status code different than CMD_STATUS_SUCCESS will trigger a recovery in the driver. However, it is a valid for some fw commands to fail (e.g. due to temporary lack of resources), so add new functions that allow passing bitmap of valid error return values. (make the current wl1271_cmd_send/wl1271_cmd_configure wrappers around the new functions, in order to avoid changing the whole driver) Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 85 ++++++++++++++++++++++------ drivers/net/wireless/ti/wlcore/cmd.h | 7 ++- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index fc508ee5517e..ead19c9c9ea6 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -48,14 +48,15 @@ * @id: command id * @buf: buffer containing the command, must work with dma * @len: length of the buffer + * return the cmd status code on success. */ -int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, - size_t res_len) +static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf, + size_t len, size_t res_len) { struct wl1271_cmd_header *cmd; unsigned long timeout; u32 intr; - int ret = 0; + int ret; u16 status; u16 poll_count = 0; @@ -71,7 +72,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false); if (ret < 0) - goto fail; + return ret; /* * TODO: we just need this because one bit is in a different @@ -79,19 +80,18 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, */ ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len); if (ret < 0) - goto fail; + return ret; timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); if (ret < 0) - goto fail; + return ret; while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); - ret = -ETIMEDOUT; - goto fail; + return -ETIMEDOUT; } poll_count++; @@ -102,7 +102,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); if (ret < 0) - goto fail; + return ret; } /* read back the status code of the command */ @@ -111,28 +111,60 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false); if (ret < 0) - goto fail; + return ret; status = le16_to_cpu(cmd->status); - if (status != CMD_STATUS_SUCCESS) { - wl1271_error("command execute failure %d", status); - ret = -EIO; - goto fail; - } ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); + if (ret < 0) + return ret; + + return status; +} + +/* + * send command to fw and return cmd status on success + * valid_rets contains a bitmap of allowed error codes + */ +int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len, unsigned long valid_rets) +{ + int ret = __wlcore_cmd_send(wl, id, buf, len, res_len); + if (ret < 0) goto fail; - return 0; + /* success is always a valid status */ + valid_rets |= BIT(CMD_STATUS_SUCCESS); + if (ret >= MAX_COMMAND_STATUS || + !test_bit(ret, &valid_rets)) { + wl1271_error("command execute failure %d", ret); + ret = -EIO; + goto fail; + } + return ret; fail: wl12xx_queue_recovery_work(wl); return ret; } EXPORT_SYMBOL_GPL(wl1271_cmd_send); +/* + * wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS + * return 0 on success. + */ +int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len) +{ + int ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0); + + if (ret < 0) + return ret; + return 0; +} + /* * Poll the mailbox event field until any of the bits in the mask is set or a * timeout occurs (WL1271_EVENT_TIMEOUT in msecs) @@ -791,8 +823,11 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) * @id: acx id * @buf: buffer containing acx, including all headers, must work with dma * @len: length of buf + * @valid_rets: bitmap of valid cmd status codes (i.e. return values). + * return the cmd status on success. */ -int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) +int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf, + size_t len, unsigned long valid_rets) { struct acx_header *acx = buf; int ret; @@ -804,12 +839,26 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) /* payload length, does not include any headers */ acx->len = cpu_to_le16(len - sizeof(*acx)); - ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0); + ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0, + valid_rets); if (ret < 0) { wl1271_warning("CONFIGURE command NOK"); return ret; } + return ret; +} + +/* + * wrapper for wlcore_cmd_configure that accepts only success status. + * return 0 on success + */ +int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) +{ + int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0); + + if (ret < 0) + return ret; return 0; } EXPORT_SYMBOL_GPL(wl1271_cmd_configure); diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 7f378b73f022..fd34123047cd 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -31,6 +31,8 @@ struct acx_header; int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, size_t res_len); +int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len, + size_t res_len, unsigned long valid_rets); int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type, u8 *role_id); int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id); @@ -45,6 +47,8 @@ int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); +int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf, + size_t len, unsigned long valid_rets); int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 ps_mode, u16 auto_ps_timeout); @@ -234,7 +238,8 @@ enum { CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/ CMD_STATUS_TEMPLATE_OOM = 23, CMD_STATUS_NO_RX_BA_SESSION = 24, - MAX_COMMAND_STATUS = 0xff + + MAX_COMMAND_STATUS }; #define CMDMBOX_HEADER_LEN 4 From 3b44b3b3687534be24171db6ff80669b3a6dcbab Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 27 Nov 2012 08:44:46 +0200 Subject: [PATCH 056/478] wlcore: allow ACX_BA_SESSION_RX_SETUP to fail Under some circumstances, that fw might be asked to remove a rx ba sessions it doesn't know about. In this case, instead of triggering a recovery, accept the error code (CMD_STATUS_NO_RX_BA_SESSION) and ignore it. [Arik - indicate failure up when the BA session cannot be setup] Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/acx.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index ce108a736bd0..9c32f0c840d7 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1433,13 +1433,22 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, acx->win_size = wl->conf.ht.rx_ba_win_size; acx->ssn = ssn; - ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx, - sizeof(*acx)); + ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx, + sizeof(*acx), + BIT(CMD_STATUS_NO_RX_BA_SESSION)); if (ret < 0) { wl1271_warning("acx ba receiver session failed: %d", ret); goto out; } + /* sometimes we can't start the session */ + if (ret == CMD_STATUS_NO_RX_BA_SESSION) { + wl1271_warning("no fw rx ba on tid %d", tid_index); + ret = -EBUSY; + goto out; + } + + ret = 0; out: kfree(acx); return ret; From 2718bf409937655f9b0bbc174faee3ac2ecf1062 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 27 Nov 2012 08:44:47 +0200 Subject: [PATCH 057/478] wlcore: remove WLCORE_QUIRK_NO_ELP all the current firmwares support elp, so we can safely remove WLCORE_QUIRK_NO_ELP. Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 3 +-- drivers/net/wireless/ti/wlcore/init.c | 3 --- drivers/net/wireless/ti/wlcore/main.c | 3 --- drivers/net/wireless/ti/wlcore/wlcore.h | 3 --- 4 files changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index a45ecffef01a..4621d67cfe2d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -639,8 +639,7 @@ static int wl18xx_identify_chip(struct wl1271 *wl) wl->sr_fw_name = WL18XX_FW_NAME; /* wl18xx uses the same firmware for PLT */ wl->plt_fw_name = WL18XX_FW_NAME; - wl->quirks |= WLCORE_QUIRK_NO_ELP | - WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | + wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN | WLCORE_QUIRK_TX_PAD_LAST_FRAME | diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 68828b4adc06..5c6f11e157d9 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -577,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) /* Configure for power according to debugfs */ if (sta_auth != WL1271_PSM_ILLEGAL) ret = wl1271_acx_sleep_auth(wl, sta_auth); - /* Configure for power always on */ - else if (wl->quirks & WLCORE_QUIRK_NO_ELP) - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); /* Configure for ELP power saving */ else ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index a163b0900b77..1b42cb3d472a 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2482,9 +2482,6 @@ deinit: /* Configure for power according to debugfs */ if (sta_auth != WL1271_PSM_ILLEGAL) wl1271_acx_sleep_auth(wl, sta_auth); - /* Configure for power always on */ - else if (wl->quirks & WLCORE_QUIRK_NO_ELP) - wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); /* Configure for ELP power saving */ else wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index d90436dc9c0d..5aa8b3f60005 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -515,9 +515,6 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip, /* Older firmwares use an old NVS format */ #define WLCORE_QUIRK_LEGACY_NVS BIT(5) -/* Some firmwares may not support ELP */ -#define WLCORE_QUIRK_NO_ELP BIT(6) - /* pad only the last frame in the aggregate buffer */ #define WLCORE_QUIRK_TX_PAD_LAST_FRAME BIT(7) From 5d979f35179c365a6455302bd5a1fa0063a4065e Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 27 Nov 2012 08:44:48 +0200 Subject: [PATCH 058/478] wlcore: clear roc_vif on iface removal When removing an interface currently performing a ROC operation, clear the current ROC state. This is useful especially during recovery and keeps mac80211 in sync to our state. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1b42cb3d472a..33392168bf39 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2414,6 +2414,11 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl->sched_vif = NULL; } + if (wl->roc_vif == vif) { + wl->roc_vif = NULL; + ieee80211_remain_on_channel_expired(wl->hw); + } + if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { /* disable active roles */ ret = wl1271_ps_elp_wakeup(wl); From 0330ee1f5bb65bd2fd3c4b3980f4ba999405a153 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 27 Nov 2012 08:44:49 +0200 Subject: [PATCH 059/478] wl18xx: declare support for greenfield ht_cap The 18xx fw supports greenfield so add the IEEE80211_HT_CAP_GRN_FLD flag to the supported ht capabilities flags. Signed-off-by: Eliad Peller Signed-off-by: Ido Reis Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 4621d67cfe2d..f6c2bac9f640 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1426,7 +1426,8 @@ static struct wlcore_ops wl18xx_ops = { /* HT cap appropriate for wide channels in 2Ghz */ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = { .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40, + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 | + IEEE80211_HT_CAP_GRN_FLD, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, @@ -1440,7 +1441,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = { /* HT cap appropriate for wide channels in 5Ghz */ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = { .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_SUP_WIDTH_20_40, + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, @@ -1453,7 +1455,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = { /* HT cap appropriate for SISO 20 */ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = { - .cap = IEEE80211_HT_CAP_SGI_20, + .cap = IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_GRN_FLD, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, @@ -1466,7 +1469,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = { /* HT cap appropriate for MIMO rates in 20mhz channel */ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = { - .cap = IEEE80211_HT_CAP_SGI_20, + .cap = IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_GRN_FLD, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, From 4c145185175dcca660265d2fcdd4feffc0249f8e Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Tue, 27 Nov 2012 08:44:52 +0200 Subject: [PATCH 060/478] wlcore: remove unnecessary WARN_ON in wl12xx_tx_reset Sometimes the driver can perform a recovery while Tx is active, this will trigger unnecessary warning which might delay the recovery for more than 100 mS. Signed-off-by: Victor Goldenshtein Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index c3607f09284b..b5b9bd6d4f06 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -1028,7 +1028,7 @@ void wl12xx_tx_reset(struct wl1271 *wl) struct ieee80211_tx_info *info; /* only reset the queues if something bad happened */ - if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) { + if (wl1271_tx_total_queue_count(wl) != 0) { for (i = 0; i < WL12XX_MAX_LINKS; i++) wl1271_tx_reset_link_queues(wl, i); From d935e385f88616fa867406de97521e07fe41539d Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 27 Nov 2012 08:44:53 +0200 Subject: [PATCH 061/478] wlcore: take the mutex before resetting Tx queues Otherwise we risk contention for private members of our global structure while op_stop_locked is running. Reported-by: Ido Yariv Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 33392168bf39..63d3413d6002 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1852,8 +1852,8 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) cancel_delayed_work_sync(&wl->tx_watchdog_work); /* let's notify MAC80211 about the remaining pending TX frames */ - wl12xx_tx_reset(wl); mutex_lock(&wl->mutex); + wl12xx_tx_reset(wl); wl1271_power_off(wl); /* From 6c4c45346289ec1c8a6a204e2c81325a4cf96924 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 27 Nov 2012 08:44:54 +0200 Subject: [PATCH 062/478] wlcore: consolidate free_link and always call it Make sure free_link is always called when removing an interface. This ensures all skbs belonging to this interface are returned to mac80211. Otherwise these dangling skbs might crash the system on the next call to wl1271_tx_reset_link_queues(). This happens on recovery/stop or an unsuccessful Tx flush. Signed-off-by: Ido Yariv Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 5 +++++ drivers/net/wireless/ti/wlcore/main.c | 2 -- drivers/net/wireless/ti/wlcore/tx.c | 11 ++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index ead19c9c9ea6..38243aa8614f 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -339,6 +339,11 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) __clear_bit(*hlid, wlvif->links_map); spin_unlock_irqrestore(&wl->wl_lock, flags); + wl->links[*hlid].allocated_pkts = 0; + wl->links[*hlid].prev_freed_pkts = 0; + wl->links[*hlid].ba_bitmap = 0; + memset(wl->links[*hlid].addr, 0, ETH_ALEN); + /* * At this point op_tx() will not add more packets to the queues. We * can purge them. diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 63d3413d6002..6dcd7ecb3bec 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4429,8 +4429,6 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) return; clear_bit(hlid, wlvif->ap.sta_hlid_map); - memset(wl->links[hlid].addr, 0, ETH_ALEN); - wl->links[hlid].ba_bitmap = 0; __clear_bit(hlid, &wl->ap_ps_map); __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); wl12xx_free_link(wl, wlvif, &hlid); diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index b5b9bd6d4f06..c711c91a0cc6 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -1009,13 +1009,14 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) /* TX failure */ for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) { - if (wlvif->bss_type == BSS_TYPE_AP_BSS) + if (wlvif->bss_type == BSS_TYPE_AP_BSS) { + /* this calls wl12xx_free_link */ wl1271_free_sta(wl, wlvif, i); - else + } else { + u8 hlid = i; wlvif->sta.ba_rx_bitmap = 0; - - wl->links[i].allocated_pkts = 0; - wl->links[i].prev_freed_pkts = 0; + wl12xx_free_link(wl, wlvif, &hlid); + } } wlvif->last_tx_hlid = 0; From 583f81644ab34a1314827a1dffc7fc8fbad7dc0f Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Tue, 27 Nov 2012 08:44:55 +0200 Subject: [PATCH 063/478] wlcore: restore default channel configuration wlcore allocates two static structs wl1271_band_2ghz & wl1271_band_5ghz which are used/modified by Reg-Domain e.g. some channel might be marked as passive at some point. Make sure we don't keep stale settings around if the HW is unregistered/registered during operation. [Arik - use Tx-power constant and tweak commit message] Signed-off-by: Victor Goldenshtein Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 113 ++++++++++++++---------- drivers/net/wireless/ti/wlcore/wlcore.h | 3 + 2 files changed, 68 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 6dcd7ecb3bec..7b1fc9148da9 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5070,20 +5070,20 @@ static struct ieee80211_rate wl1271_rates[] = { /* can't be const, mac80211 writes to this */ static struct ieee80211_channel wl1271_channels[] = { - { .hw_value = 1, .center_freq = 2412, .max_power = 25 }, - { .hw_value = 2, .center_freq = 2417, .max_power = 25 }, - { .hw_value = 3, .center_freq = 2422, .max_power = 25 }, - { .hw_value = 4, .center_freq = 2427, .max_power = 25 }, - { .hw_value = 5, .center_freq = 2432, .max_power = 25 }, - { .hw_value = 6, .center_freq = 2437, .max_power = 25 }, - { .hw_value = 7, .center_freq = 2442, .max_power = 25 }, - { .hw_value = 8, .center_freq = 2447, .max_power = 25 }, - { .hw_value = 9, .center_freq = 2452, .max_power = 25 }, - { .hw_value = 10, .center_freq = 2457, .max_power = 25 }, - { .hw_value = 11, .center_freq = 2462, .max_power = 25 }, - { .hw_value = 12, .center_freq = 2467, .max_power = 25 }, - { .hw_value = 13, .center_freq = 2472, .max_power = 25 }, - { .hw_value = 14, .center_freq = 2484, .max_power = 25 }, + { .hw_value = 1, .center_freq = 2412, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 2, .center_freq = 2417, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 3, .center_freq = 2422, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 4, .center_freq = 2427, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 5, .center_freq = 2432, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 6, .center_freq = 2437, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 7, .center_freq = 2442, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 8, .center_freq = 2447, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 9, .center_freq = 2452, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 10, .center_freq = 2457, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR }, }; /* can't be const, mac80211 writes to this */ @@ -5124,40 +5124,40 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = { /* 5 GHz band channels for WL1273 */ static struct ieee80211_channel wl1271_channels_5ghz[] = { - { .hw_value = 7, .center_freq = 5035, .max_power = 25 }, - { .hw_value = 8, .center_freq = 5040, .max_power = 25 }, - { .hw_value = 9, .center_freq = 5045, .max_power = 25 }, - { .hw_value = 11, .center_freq = 5055, .max_power = 25 }, - { .hw_value = 12, .center_freq = 5060, .max_power = 25 }, - { .hw_value = 16, .center_freq = 5080, .max_power = 25 }, - { .hw_value = 34, .center_freq = 5170, .max_power = 25 }, - { .hw_value = 36, .center_freq = 5180, .max_power = 25 }, - { .hw_value = 38, .center_freq = 5190, .max_power = 25 }, - { .hw_value = 40, .center_freq = 5200, .max_power = 25 }, - { .hw_value = 42, .center_freq = 5210, .max_power = 25 }, - { .hw_value = 44, .center_freq = 5220, .max_power = 25 }, - { .hw_value = 46, .center_freq = 5230, .max_power = 25 }, - { .hw_value = 48, .center_freq = 5240, .max_power = 25 }, - { .hw_value = 52, .center_freq = 5260, .max_power = 25 }, - { .hw_value = 56, .center_freq = 5280, .max_power = 25 }, - { .hw_value = 60, .center_freq = 5300, .max_power = 25 }, - { .hw_value = 64, .center_freq = 5320, .max_power = 25 }, - { .hw_value = 100, .center_freq = 5500, .max_power = 25 }, - { .hw_value = 104, .center_freq = 5520, .max_power = 25 }, - { .hw_value = 108, .center_freq = 5540, .max_power = 25 }, - { .hw_value = 112, .center_freq = 5560, .max_power = 25 }, - { .hw_value = 116, .center_freq = 5580, .max_power = 25 }, - { .hw_value = 120, .center_freq = 5600, .max_power = 25 }, - { .hw_value = 124, .center_freq = 5620, .max_power = 25 }, - { .hw_value = 128, .center_freq = 5640, .max_power = 25 }, - { .hw_value = 132, .center_freq = 5660, .max_power = 25 }, - { .hw_value = 136, .center_freq = 5680, .max_power = 25 }, - { .hw_value = 140, .center_freq = 5700, .max_power = 25 }, - { .hw_value = 149, .center_freq = 5745, .max_power = 25 }, - { .hw_value = 153, .center_freq = 5765, .max_power = 25 }, - { .hw_value = 157, .center_freq = 5785, .max_power = 25 }, - { .hw_value = 161, .center_freq = 5805, .max_power = 25 }, - { .hw_value = 165, .center_freq = 5825, .max_power = 25 }, + { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 36, .center_freq = 5180, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 38, .center_freq = 5190, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 40, .center_freq = 5200, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 42, .center_freq = 5210, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 44, .center_freq = 5220, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 46, .center_freq = 5230, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 48, .center_freq = 5240, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 52, .center_freq = 5260, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 56, .center_freq = 5280, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 60, .center_freq = 5300, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 64, .center_freq = 5320, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 100, .center_freq = 5500, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 104, .center_freq = 5520, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 108, .center_freq = 5540, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 112, .center_freq = 5560, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 116, .center_freq = 5580, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 120, .center_freq = 5600, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 124, .center_freq = 5620, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 128, .center_freq = 5640, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 132, .center_freq = 5660, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 136, .center_freq = 5680, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 140, .center_freq = 5700, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 149, .center_freq = 5745, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 153, .center_freq = 5765, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 157, .center_freq = 5785, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 161, .center_freq = 5805, .max_power = WLCORE_MAX_TXPWR }, + { .hw_value = 165, .center_freq = 5825, .max_power = WLCORE_MAX_TXPWR }, }; static struct ieee80211_supported_band wl1271_band_5ghz = { @@ -5521,6 +5521,7 @@ wlcore_iface_combinations[] = { static int wl1271_init_ieee80211(struct wl1271 *wl) { + int i; static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, @@ -5582,6 +5583,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + ARRAY_SIZE(wl1271_channels_5ghz) > WL1271_MAX_CHANNELS); + /* + * clear channel flags from the previous usage + * and restore max_power & max_antenna_gain values. + */ + for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) { + wl1271_band_2ghz.channels[i].flags = 0; + wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR; + wl1271_band_2ghz.channels[i].max_antenna_gain = 0; + } + + for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) { + wl1271_band_5ghz.channels[i].flags = 0; + wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR; + wl1271_band_5ghz.channels[i].max_antenna_gain = 0; + } + /* * We keep local copies of the band structs because we need to * modify them on a per-device basis. diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 5aa8b3f60005..8703ae976081 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -37,6 +37,9 @@ */ #define WLCORE_NUM_MAC_ADDRESSES 3 +/* wl12xx/wl18xx maximum transmission power (in dBm) */ +#define WLCORE_MAX_TXPWR 25 + /* forward declaration */ struct wl1271_tx_hw_descr; enum wl_rx_buf_align; From b50a62bb39c66e6ed5f1f581bb32644936f08cf3 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 27 Nov 2012 08:44:56 +0200 Subject: [PATCH 064/478] wlcore: initialize per-link FW freed blocks correctly When a link is allocated, sometimes the "freed packets" counter in FW is non zero, but we always assumed it is. This caused us to incorrectly account FW allocated blocks in some cases. When operating in AP mode, this bug caused some stations to never come back from PSM. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 38243aa8614f..e00d64be8d75 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -322,6 +322,10 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) __set_bit(link, wl->links_map); __set_bit(link, wlvif->links_map); spin_unlock_irqrestore(&wl->wl_lock, flags); + + /* take the last "freed packets" value from the current FW status */ + wl->links[link].prev_freed_pkts = + wl->fw_status_2->counters.tx_lnk_free_pkts[link]; *hlid = link; return 0; } From 1e0708a9574aee0136611edc5d3a7a9b2775a113 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 27 Nov 2012 08:44:57 +0200 Subject: [PATCH 065/478] wlcore: track wlvif inside per-link structure This allows us to pass only the link as a parameter to various functions and deduce the wlvif. Note that this member will be NULL for global links. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 2 ++ drivers/net/wireless/ti/wlcore/wlcore_i.h | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index e00d64be8d75..56432c8cda00 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -326,6 +326,7 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) /* take the last "freed packets" value from the current FW status */ wl->links[link].prev_freed_pkts = wl->fw_status_2->counters.tx_lnk_free_pkts[link]; + wl->links[link].wlvif = wlvif; *hlid = link; return 0; } @@ -353,6 +354,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) * can purge them. */ wl1271_tx_reset_link_queues(wl, *hlid); + wl->links[*hlid].wlvif = NULL; *hlid = WL12XX_INVALID_LINK_ID; } diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index d6854b9991af..9ffbfc19af57 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -249,6 +249,8 @@ enum wl12xx_vif_flags { WLVIF_FLAG_IN_USE, }; +struct wl12xx_vif; + struct wl1271_link { /* AP-mode - TX queue per AC in link */ struct sk_buff_head tx_queue[NUM_TX_QUEUES]; @@ -261,6 +263,9 @@ struct wl1271_link { /* bitmap of TIDs where RX BA sessions are active for this link */ u8 ba_bitmap; + + /* The wlvif this link belongs to. Might be null for global links */ + struct wl12xx_vif *wlvif; }; #define WL1271_MAX_RX_FILTERS 5 From 8591d42452f16b1888419da4456142864b08ef9e Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 27 Nov 2012 08:44:58 +0200 Subject: [PATCH 066/478] wlcore: count packets held per AC in each vif This accounting will help find a vif that has data in a specific AC. Otherwise we have to traverse all the links, which can be lengthy for the AP case. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 4 ++++ drivers/net/wireless/ti/wlcore/main.c | 2 ++ drivers/net/wireless/ti/wlcore/ps.c | 8 ++++++-- drivers/net/wireless/ti/wlcore/tx.c | 16 ++++++++++++++-- drivers/net/wireless/ti/wlcore/wlcore_i.h | 3 +++ 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index e61fc2b01046..f115fba41ad7 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -577,6 +577,10 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]); } VIF_STATE_PRINT_INT(last_tx_hlid); + VIF_STATE_PRINT_INT(tx_queue_count[0]); + VIF_STATE_PRINT_INT(tx_queue_count[1]); + VIF_STATE_PRINT_INT(tx_queue_count[2]); + VIF_STATE_PRINT_INT(tx_queue_count[3]); VIF_STATE_PRINT_LHEX(links_map[0]); VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len); VIF_STATE_PRINT_INT(band); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 7b1fc9148da9..56ed37933022 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1206,6 +1206,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); wl->tx_queue_count[q]++; + if (wlvif) + wlvif->tx_queue_count[q]++; /* * The workqueue is slow to process the tx_queue and we need stop diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index ffcd84336f04..9b7b6e2e4fbc 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -239,11 +239,12 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) struct ieee80211_tx_info *info; unsigned long flags; int filtered[NUM_TX_QUEUES]; + struct wl1271_link *lnk = &wl->links[hlid]; /* filter all frames currently in the low level queues for this hlid */ for (i = 0; i < NUM_TX_QUEUES; i++) { filtered[i] = 0; - while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + while ((skb = skb_dequeue(&lnk->tx_queue[i]))) { filtered[i]++; if (WARN_ON(wl12xx_is_dummy_packet(wl, skb))) @@ -257,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) } spin_lock_irqsave(&wl->wl_lock, flags); - for (i = 0; i < NUM_TX_QUEUES; i++) + for (i = 0; i < NUM_TX_QUEUES; i++) { wl->tx_queue_count[i] -= filtered[i]; + if (lnk->wlvif) + lnk->wlvif->tx_queue_count[i] -= filtered[i]; + } spin_unlock_irqrestore(&wl->wl_lock, flags); wl1271_handle_tx_low_watermark(wl); diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index c711c91a0cc6..1ac8681732f2 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -514,6 +514,10 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl, spin_lock_irqsave(&wl->wl_lock, flags); WARN_ON_ONCE(wl->tx_queue_count[q] <= 0); wl->tx_queue_count[q]--; + if (lnk->wlvif) { + WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0); + lnk->wlvif->tx_queue_count[q]--; + } spin_unlock_irqrestore(&wl->wl_lock, flags); } @@ -628,6 +632,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif, spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count[q]++; + if (wlvif) + wlvif->tx_queue_count[q]++; spin_unlock_irqrestore(&wl->wl_lock, flags); } @@ -977,10 +983,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) unsigned long flags; struct ieee80211_tx_info *info; int total[NUM_TX_QUEUES]; + struct wl1271_link *lnk = &wl->links[hlid]; for (i = 0; i < NUM_TX_QUEUES; i++) { total[i] = 0; - while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + while ((skb = skb_dequeue(&lnk->tx_queue[i]))) { wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb); if (!wl12xx_is_dummy_packet(wl, skb)) { @@ -995,8 +1002,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) } spin_lock_irqsave(&wl->wl_lock, flags); - for (i = 0; i < NUM_TX_QUEUES; i++) + for (i = 0; i < NUM_TX_QUEUES; i++) { wl->tx_queue_count[i] -= total[i]; + if (lnk->wlvif) + lnk->wlvif->tx_queue_count[i] -= total[i]; + } spin_unlock_irqrestore(&wl->wl_lock, flags); wl1271_handle_tx_low_watermark(wl); @@ -1020,6 +1030,8 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) } wlvif->last_tx_hlid = 0; + for (i = 0; i < NUM_TX_QUEUES; i++) + wlvif->tx_queue_count[i] = 0; } /* caller must hold wl->mutex and TX must be stopped */ void wl12xx_tx_reset(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 9ffbfc19af57..670d12f3cd50 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -359,6 +359,9 @@ struct wl12xx_vif { /* the hlid of the last transmitted skb */ int last_tx_hlid; + /* counters of packets per AC, across all links in the vif */ + int tx_queue_count[NUM_TX_QUEUES]; + unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)]; u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; From 9ebcb232158c737db21e22b7bfdc4fc6d661ea8c Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 27 Nov 2012 08:44:59 +0200 Subject: [PATCH 067/478] wlcore: track FW-allocated packets per link Move FW-allocation tracking code to the fw_status function and track allocations made by all links. These will be incorporated in the improved Tx scheduling algorithm. Manually zero the system link counters on op_stop, as this link is not allocated the normal way. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 29 ++++++++++++++++----------- drivers/net/wireless/ti/wlcore/tx.c | 4 +--- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 56ed37933022..33a9559f4d84 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -359,9 +359,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct wl_fw_status_2 *status) { - struct wl1271_link *lnk; u32 cur_fw_ps_map; - u8 hlid, cnt; + u8 hlid; /* TODO: also use link_fast_bitmap here */ @@ -375,17 +374,9 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, wl->ap_fw_ps_map = cur_fw_ps_map; } - for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) { - lnk = &wl->links[hlid]; - cnt = status->counters.tx_lnk_free_pkts[hlid] - - lnk->prev_freed_pkts; - - lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[hlid]; - lnk->allocated_pkts -= cnt; - + for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) wl12xx_irq_ps_regulate_link(wl, wlvif, hlid, - lnk->allocated_pkts); - } + wl->links[hlid].allocated_pkts); } static int wlcore_fw_status(struct wl1271 *wl, @@ -399,6 +390,7 @@ static int wlcore_fw_status(struct wl1271 *wl, int i; size_t status_len; int ret; + struct wl1271_link *lnk; status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + sizeof(*status_2) + wl->fw_status_priv_len; @@ -424,6 +416,17 @@ static int wlcore_fw_status(struct wl1271 *wl, wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i]; } + + for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) { + lnk = &wl->links[i]; + /* prevent wrap-around in freed-packets counter */ + lnk->allocated_pkts -= + (status_2->counters.tx_lnk_free_pkts[i] - + lnk->prev_freed_pkts) & 0xff; + + lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i]; + } + /* prevent wrap-around in total blocks counter */ if (likely(wl->tx_blocks_freed <= le32_to_cpu(status_2->total_released_blks))) @@ -1886,6 +1889,8 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) wl->active_sta_count = 0; /* The system link is always allocated */ + wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0; + wl->links[WL12XX_SYSTEM_HLID].prev_freed_pkts = 0; __set_bit(WL12XX_SYSTEM_HLID, wl->links_map); /* diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 1ac8681732f2..076983b05b4d 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -224,9 +224,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); wl->tx_allocated_pkts[ac]++; - if (!wl12xx_is_dummy_packet(wl, skb) && wlvif && - wlvif->bss_type == BSS_TYPE_AP_BSS && - test_bit(hlid, wlvif->ap.sta_hlid_map)) + if (test_bit(hlid, wl->links_map)) wl->links[hlid].allocated_pkts++; ret = 0; From 0e81047996fdde7fc9e8a1c01d532df1f53586fa Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 27 Nov 2012 08:45:00 +0200 Subject: [PATCH 068/478] wlcore: improved Tx scheduling algorithm Prioritize EDCA by choosing the AC before anything else. Use the fast/slow link bitmap in FW to improve the scheduling algorithm for the multi-link scenario. Set packet thresholds to determine if a given link is high or low priority according to its speed. A slow link will be given high priority if the amount of packets queued for it in the FW is lower than the slow-threshold. Similarly, a fast link will be given high priority if the number of its packets queued in FW is smaller than the high-threshold. The improved algorithm: 1. Choose optimal AC according to FW utilization 2. Traversing the VIFs in a round-robin fashion, try to choose a high priority link. Links are traversed in a round-robin fashion inside a VIF. 3. If no high priority links are found, choose the first non-empty (low priority) link found in the round robin. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 2 + drivers/net/wireless/ti/wl18xx/main.c | 2 + drivers/net/wireless/ti/wlcore/conf.h | 14 ++- drivers/net/wireless/ti/wlcore/main.c | 4 +- drivers/net/wireless/ti/wlcore/tx.c | 124 +++++++++++++++++------- drivers/net/wireless/ti/wlcore/wlcore.h | 3 + 6 files changed, 112 insertions(+), 37 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index d22b52f90d42..64dffafa35fb 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -210,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = { .tmpl_short_retry_limit = 10, .tmpl_long_retry_limit = 10, .tx_watchdog_timeout = 5000, + .slow_link_thold = 3, + .fast_link_thold = 10, }, .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index f6c2bac9f640..a5119d3d5e70 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -337,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = { .tmpl_short_retry_limit = 10, .tmpl_long_retry_limit = 10, .tx_watchdog_timeout = 5000, + .slow_link_thold = 3, + .fast_link_thold = 30, }, .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 1bd831c9e843..552ac8182475 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -677,6 +677,18 @@ struct conf_tx_settings { /* Time in ms for Tx watchdog timer to expire */ u32 tx_watchdog_timeout; + + /* + * when a slow link has this much packets pending, it becomes a low + * priority link, scheduling-wise + */ + u8 slow_link_thold; + + /* + * when a fast link has this much packets pending, it becomes a low + * priority link, scheduling-wise + */ + u8 fast_link_thold; } __packed; enum { @@ -1281,7 +1293,7 @@ struct conf_recovery_settings { * version, the two LSB are the lower driver's private conf * version. */ -#define WLCORE_CONF_VERSION (0x0004 << 16) +#define WLCORE_CONF_VERSION (0x0005 << 16) #define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ sizeof(struct wlcore_conf)) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 33a9559f4d84..486b7fa0259c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -362,8 +362,6 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, u32 cur_fw_ps_map; u8 hlid; - /* TODO: also use link_fast_bitmap here */ - cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap); if (wl->ap_fw_ps_map != cur_fw_ps_map) { wl1271_debug(DEBUG_PSM, @@ -479,6 +477,8 @@ static int wlcore_fw_status(struct wl1271 *wl, wl->time_offset = (timespec_to_ns(&ts) >> 10) - (s64)le32_to_cpu(status_2->fw_localtime); + wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap); + return 0; } diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 076983b05b4d..fbda3cbca7e7 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -467,8 +467,7 @@ void wl1271_handle_tx_low_watermark(struct wl1271 *wl) } } -static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl, - struct sk_buff_head *queues) +static int wlcore_select_ac(struct wl1271 *wl) { int i, q = -1, ac; u32 min_pkts = 0xffffffff; @@ -482,33 +481,24 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl, */ for (i = 0; i < NUM_TX_QUEUES; i++) { ac = wl1271_tx_get_queue(i); - if (!skb_queue_empty(&queues[ac]) && - (wl->tx_allocated_pkts[ac] < min_pkts)) { + if (wl->tx_queue_count[ac] && + wl->tx_allocated_pkts[ac] < min_pkts) { q = ac; min_pkts = wl->tx_allocated_pkts[q]; } } - if (q == -1) - return NULL; - - return &queues[q]; + return q; } -static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl, - struct wl1271_link *lnk) +static struct sk_buff *wlcore_lnk_dequeue(struct wl1271 *wl, + struct wl1271_link *lnk, u8 q) { struct sk_buff *skb; unsigned long flags; - struct sk_buff_head *queue; - queue = wl1271_select_queue(wl, lnk->tx_queue); - if (!queue) - return NULL; - - skb = skb_dequeue(queue); + skb = skb_dequeue(&lnk->tx_queue[q]); if (skb) { - int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); spin_lock_irqsave(&wl->wl_lock, flags); WARN_ON_ONCE(wl->tx_queue_count[q] <= 0); wl->tx_queue_count[q]--; @@ -522,9 +512,41 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl, return skb; } -static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - u8 *hlid) +static bool wlcore_lnk_high_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + u8 thold; + + if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) + thold = wl->conf.tx.fast_link_thold; + else + thold = wl->conf.tx.slow_link_thold; + + return lnk->allocated_pkts < thold; +} + +static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct wl1271 *wl, + u8 hlid, u8 ac, + u8 *low_prio_hlid) +{ + struct wl1271_link *lnk = &wl->links[hlid]; + + if (!wlcore_lnk_high_prio(wl, hlid, lnk)) { + if (*low_prio_hlid == WL12XX_INVALID_LINK_ID && + !skb_queue_empty(&lnk->tx_queue[ac])) + /* we found the first non-empty low priority queue */ + *low_prio_hlid = hlid; + + return NULL; + } + + return wlcore_lnk_dequeue(wl, lnk, ac); +} + +static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + u8 ac, u8 *hlid, + u8 *low_prio_hlid) { struct sk_buff *skb = NULL; int i, h, start_hlid; @@ -540,7 +562,8 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl, if (!test_bit(h, wlvif->links_map)) continue; - skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]); + skb = wlcore_lnk_dequeue_high_prio(wl, h, ac, + low_prio_hlid); if (!skb) continue; @@ -560,42 +583,74 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid) unsigned long flags; struct wl12xx_vif *wlvif = wl->last_wlvif; struct sk_buff *skb = NULL; + int ac; + u8 low_prio_hlid = WL12XX_INVALID_LINK_ID; + + ac = wlcore_select_ac(wl); + if (ac < 0) + goto out; /* continue from last wlvif (round robin) */ if (wlvif) { wl12xx_for_each_wlvif_continue(wl, wlvif) { - skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid); - if (skb) { - wl->last_wlvif = wlvif; - break; - } + if (!wlvif->tx_queue_count[ac]) + continue; + + skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid, + &low_prio_hlid); + if (!skb) + continue; + + wl->last_wlvif = wlvif; + break; } } /* dequeue from the system HLID before the restarting wlvif list */ if (!skb) { - skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]); - *hlid = wl->system_hlid; + skb = wlcore_lnk_dequeue_high_prio(wl, wl->system_hlid, + ac, &low_prio_hlid); + if (skb) { + *hlid = wl->system_hlid; + wl->last_wlvif = NULL; + } } - /* do a new pass over the wlvif list */ + /* Do a new pass over the wlvif list. But no need to continue + * after last_wlvif. The previous pass should have found it. */ if (!skb) { wl12xx_for_each_wlvif(wl, wlvif) { - skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid); + if (!wlvif->tx_queue_count[ac]) + goto next; + + skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid, + &low_prio_hlid); if (skb) { wl->last_wlvif = wlvif; break; } - /* - * No need to continue after last_wlvif. The previous - * pass should have found it. - */ +next: if (wlvif == wl->last_wlvif) break; } } + /* no high priority skbs found - but maybe a low priority one? */ + if (!skb && low_prio_hlid != WL12XX_INVALID_LINK_ID) { + struct wl1271_link *lnk = &wl->links[low_prio_hlid]; + skb = wlcore_lnk_dequeue(wl, lnk, ac); + + WARN_ON(!skb); /* we checked this before */ + *hlid = low_prio_hlid; + + /* ensure proper round robin in the vif/link levels */ + wl->last_wlvif = lnk->wlvif; + if (lnk->wlvif) + lnk->wlvif->last_tx_hlid = low_prio_hlid; + + } + if (!skb && test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) { int q; @@ -609,6 +664,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid) spin_unlock_irqrestore(&wl->wl_lock, flags); } +out: return skb; } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 8703ae976081..e8245f36a67e 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -361,6 +361,9 @@ struct wl1271 { */ struct wl1271_link links[WL12XX_MAX_LINKS]; + /* Fast/slow links bitmap according to FW */ + u32 fw_fast_lnk_map; + /* AP-mode - a bitmap of links currently in PS mode according to FW */ u32 ap_fw_ps_map; From 4d703847617fa053689b119e43122088c81ef86d Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 30 Nov 2012 00:46:48 +0200 Subject: [PATCH 069/478] wl18xx: set last Tx rate from FW status Obtain the last Tx rate from the FW status and translate it to the mac80211 rate+flag format before sending it up via the Tx status. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/tx.c | 54 +++++++++++++++++++-- drivers/net/wireless/ti/wlcore/conf.h | 57 +++++++++++++++++------ drivers/net/wireless/ti/wlcore/wlcore_i.h | 5 +- 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c index 5b1fb10d9fd7..57c694396647 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.c +++ b/drivers/net/wireless/ti/wl18xx/tx.c @@ -28,6 +28,49 @@ #include "wl18xx.h" #include "tx.h" +static +void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif, + struct ieee80211_tx_rate *rate) +{ + u8 fw_rate = wl->fw_status_2->counters.tx_last_rate; + + if (fw_rate > CONF_HW_RATE_INDEX_MAX) { + wl1271_error("last Tx rate invalid: %d", fw_rate); + rate->idx = 0; + rate->flags = 0; + return; + } + + if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) { + rate->idx = fw_rate; + rate->flags = 0; + } else { + rate->flags = IEEE80211_TX_RC_MCS; + rate->idx = fw_rate - CONF_HW_RATE_INDEX_MCS0; + + /* SGI modifier is counted as a separate rate */ + if (fw_rate >= CONF_HW_RATE_INDEX_MCS7_SGI) + (rate->idx)--; + if (fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI) + (rate->idx)--; + + /* this also covers the 40Mhz SGI case (= MCS15) */ + if (fw_rate == CONF_HW_RATE_INDEX_MCS7_SGI || + fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI) + rate->flags |= IEEE80211_TX_RC_SHORT_GI; + + if (fw_rate > CONF_HW_RATE_INDEX_MCS7_SGI && vif) { + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + if (wlvif->channel_type == NL80211_CHAN_HT40MINUS || + wlvif->channel_type == NL80211_CHAN_HT40PLUS) { + /* adjustment needed for range 0-7 */ + rate->idx -= 8; + rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + } + } + } +} + static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) { struct ieee80211_tx_info *info; @@ -44,7 +87,6 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) /* a zero bit indicates Tx success */ tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX)); - skb = wl->tx_frames[id]; info = IEEE80211_SKB_CB(skb); @@ -56,11 +98,13 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) /* update the TX status info */ if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) info->flags |= IEEE80211_TX_STAT_ACK; + /* + * first pass info->control.vif while it's valid, and then fill out + * the info->status structures + */ + wl18xx_get_last_tx_rate(wl, info->control.vif, &info->status.rates[0]); - /* no real data about Tx completion */ - info->status.rates[0].idx = -1; - info->status.rates[0].count = 0; - info->status.rates[0].flags = 0; + info->status.rates[0].count = 1; /* no data about retries */ info->status.ack_signal = -1; if (!tx_success) diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 552ac8182475..fd173b9ca46a 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -57,20 +57,49 @@ enum { }; enum { - CONF_HW_RATE_INDEX_1MBPS = 0, - CONF_HW_RATE_INDEX_2MBPS = 1, - CONF_HW_RATE_INDEX_5_5MBPS = 2, - CONF_HW_RATE_INDEX_6MBPS = 3, - CONF_HW_RATE_INDEX_9MBPS = 4, - CONF_HW_RATE_INDEX_11MBPS = 5, - CONF_HW_RATE_INDEX_12MBPS = 6, - CONF_HW_RATE_INDEX_18MBPS = 7, - CONF_HW_RATE_INDEX_22MBPS = 8, - CONF_HW_RATE_INDEX_24MBPS = 9, - CONF_HW_RATE_INDEX_36MBPS = 10, - CONF_HW_RATE_INDEX_48MBPS = 11, - CONF_HW_RATE_INDEX_54MBPS = 12, - CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS, + CONF_HW_RATE_INDEX_1MBPS = 0, + CONF_HW_RATE_INDEX_2MBPS = 1, + CONF_HW_RATE_INDEX_5_5MBPS = 2, + CONF_HW_RATE_INDEX_11MBPS = 3, + CONF_HW_RATE_INDEX_6MBPS = 4, + CONF_HW_RATE_INDEX_9MBPS = 5, + CONF_HW_RATE_INDEX_12MBPS = 6, + CONF_HW_RATE_INDEX_18MBPS = 7, + CONF_HW_RATE_INDEX_24MBPS = 8, + CONF_HW_RATE_INDEX_36MBPS = 9, + CONF_HW_RATE_INDEX_48MBPS = 10, + CONF_HW_RATE_INDEX_54MBPS = 11, + CONF_HW_RATE_INDEX_MCS0 = 12, + CONF_HW_RATE_INDEX_MCS1 = 13, + CONF_HW_RATE_INDEX_MCS2 = 14, + CONF_HW_RATE_INDEX_MCS3 = 15, + CONF_HW_RATE_INDEX_MCS4 = 16, + CONF_HW_RATE_INDEX_MCS5 = 17, + CONF_HW_RATE_INDEX_MCS6 = 18, + CONF_HW_RATE_INDEX_MCS7 = 19, + CONF_HW_RATE_INDEX_MCS7_SGI = 20, + CONF_HW_RATE_INDEX_MCS0_40MHZ = 21, + CONF_HW_RATE_INDEX_MCS1_40MHZ = 22, + CONF_HW_RATE_INDEX_MCS2_40MHZ = 23, + CONF_HW_RATE_INDEX_MCS3_40MHZ = 24, + CONF_HW_RATE_INDEX_MCS4_40MHZ = 25, + CONF_HW_RATE_INDEX_MCS5_40MHZ = 26, + CONF_HW_RATE_INDEX_MCS6_40MHZ = 27, + CONF_HW_RATE_INDEX_MCS7_40MHZ = 28, + CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI = 29, + + /* MCS8+ rates overlap with 40Mhz rates */ + CONF_HW_RATE_INDEX_MCS8 = 21, + CONF_HW_RATE_INDEX_MCS9 = 22, + CONF_HW_RATE_INDEX_MCS10 = 23, + CONF_HW_RATE_INDEX_MCS11 = 24, + CONF_HW_RATE_INDEX_MCS12 = 25, + CONF_HW_RATE_INDEX_MCS13 = 26, + CONF_HW_RATE_INDEX_MCS14 = 27, + CONF_HW_RATE_INDEX_MCS15 = 28, + CONF_HW_RATE_INDEX_MCS15_SGI = 29, + + CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI, }; #define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 670d12f3cd50..0194f5575acf 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -130,7 +130,10 @@ struct wl_fw_packet_counters { /* Cumulative counter of released Voice memory blocks */ u8 tx_voice_released_blks; - u8 padding[3]; + /* Tx rate of the last transmitted packet */ + u8 tx_last_rate; + + u8 padding[2]; } __packed; /* FW status registers */ From 530abe195df6918d43c9381fd9a70b7e16d55036 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 28 Nov 2012 11:42:31 +0200 Subject: [PATCH 070/478] wlcore: add ACX_PEER_CAP command ACX_PEER_CAP command is just ACX_PEER_HT_CAP, but allows configuring the peer's support rates as well. this is needed because we start the station role when the remote rates are not known yet. the two commands should be unified in future fw versions, but for now add a new set_peer_cap per-hw op, that will use ACX_PEER_CAP for 18xx, and ACX_PEER_HT_CAP for 12xx. Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 10 +++++ drivers/net/wireless/ti/wl18xx/acx.c | 54 +++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/acx.h | 33 +++++++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 9 +++++ drivers/net/wireless/ti/wlcore/acx.c | 2 + drivers/net/wireless/ti/wlcore/cmd.c | 7 ++-- drivers/net/wireless/ti/wlcore/hw_ops.h | 13 ++++++ drivers/net/wireless/ti/wlcore/main.c | 9 +++-- drivers/net/wireless/ti/wlcore/wlcore.h | 5 +++ 9 files changed, 134 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 64dffafa35fb..8b4827f47deb 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1616,6 +1616,15 @@ static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd, return wlcore_set_key(wl, cmd, vif, sta, key_conf); } +static int wl12xx_set_peer_cap(struct wl1271 *wl, + struct ieee80211_sta_ht_cap *ht_cap, + bool allow_ht_operation, + u32 rate_set, u8 hlid) +{ + return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation, + hlid); +} + static int wl12xx_setup(struct wl1271 *wl); static struct wlcore_ops wl12xx_ops = { @@ -1651,6 +1660,7 @@ static struct wlcore_ops wl12xx_ops = { .set_key = wl12xx_set_key, .channel_switch = wl12xx_cmd_channel_switch, .pre_pkt_send = NULL, + .set_peer_cap = wl12xx_set_peer_cap, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index 801d8af5cef2..a169bb5a5dbf 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -140,3 +140,57 @@ out: return ret; } + +/* + * this command is basically the same as wl1271_acx_ht_capabilities, + * with the addition of supported rates. they should be unified in + * the next fw api change + */ +int wl18xx_acx_set_peer_cap(struct wl1271 *wl, + struct ieee80211_sta_ht_cap *ht_cap, + bool allow_ht_operation, + u32 rate_set, u8 hlid) +{ + struct wlcore_acx_peer_cap *acx; + int ret = 0; + u32 ht_capabilites = 0; + + wl1271_debug(DEBUG_ACX, + "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x", + ht_cap->ht_supported, ht_cap->cap, rate_set); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + if (allow_ht_operation && ht_cap->ht_supported) { + /* no need to translate capabilities - use the spec values */ + ht_capabilites = ht_cap->cap; + + /* + * this bit is not employed by the spec but only by FW to + * indicate peer HT support + */ + ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION; + + /* get data from A-MPDU parameters field */ + acx->ampdu_max_length = ht_cap->ampdu_factor; + acx->ampdu_min_spacing = ht_cap->ampdu_density; + } + + acx->hlid = hlid; + acx->ht_capabilites = cpu_to_le32(ht_capabilites); + acx->supported_rates = cpu_to_le32(rate_set); + + ret = wl1271_cmd_configure(wl, ACX_PEER_CAP, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx ht capabilities setting failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index b57e3483509d..0e636def1217 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -297,11 +297,44 @@ struct wlcore_peer_ht_operation_mode { u8 padding[2]; }; +/* + * ACX_PEER_CAP + * this struct is very similar to wl1271_acx_ht_capabilities, with the + * addition of supported rates + */ +struct wlcore_acx_peer_cap { + struct acx_header header; + + /* bitmask of capability bits supported by the peer */ + __le32 ht_capabilites; + + /* rates supported by the remote peer */ + __le32 supported_rates; + + /* Indicates to which link these capabilities apply. */ + u8 hlid; + + /* + * This the maximum A-MPDU length supported by the AP. The FW may not + * exceed this length when sending A-MPDUs + */ + u8 ampdu_max_length; + + /* This is the minimal spacing required when sending A-MPDUs to the AP*/ + u8 ampdu_min_spacing; + + u8 padding; +} __packed; + int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); int wl18xx_acx_set_checksum_state(struct wl1271 *wl); int wl18xx_acx_clear_statistics(struct wl1271 *wl); int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide); +int wl18xx_acx_set_peer_cap(struct wl1271 *wl, + struct ieee80211_sta_ht_cap *ht_cap, + bool allow_ht_operation, + u32 rate_set, u8 hlid); #endif /* __WL18XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index a5119d3d5e70..55fd46b9628d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1386,6 +1386,14 @@ out: mutex_unlock(&wl->mutex); } +static int wl18xx_set_peer_cap(struct wl1271 *wl, + struct ieee80211_sta_ht_cap *ht_cap, + bool allow_ht_operation, + u32 rate_set, u8 hlid) +{ + return wl18xx_acx_set_peer_cap(wl, ht_cap, allow_ht_operation, + rate_set, hlid); +} static int wl18xx_setup(struct wl1271 *wl); @@ -1423,6 +1431,7 @@ static struct wlcore_ops wl18xx_ops = { .channel_switch = wl18xx_cmd_channel_switch, .pre_pkt_send = wl18xx_pre_pkt_send, .sta_rc_update = wl18xx_sta_rc_update, + .set_peer_cap = wl18xx_set_peer_cap, }; /* HT cap appropriate for wide channels in 2Ghz */ diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index 9c32f0c840d7..c79654323396 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1340,6 +1340,8 @@ out: kfree(acx); return ret; } +EXPORT_SYMBOL_GPL(wl1271_acx_set_ht_capabilities); + int wl1271_acx_set_ht_information(struct wl1271 *wl, struct wl12xx_vif *wlvif, diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 56432c8cda00..1fbee0d51872 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -506,11 +506,10 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) cmd->sta.hlid = wlvif->sta.hlid; cmd->sta.session = wl->session_ids[wlvif->sta.hlid]; /* - * We don't have the correct remote rates in this stage, and there - * is no way to update them later, so use our supported rates instead. - * The fw will take the configured rate policies into account anyway. + * We don't have the correct remote rates in this stage. the rates + * will be reconfigured later, after authorization. */ - cmd->sta.remote_rates = cpu_to_le32(supported_rates); + cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set); wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d " "basic_rate_set: 0x%x, remote_rates: 0x%x", diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 0e0b6563a3ff..4db03e14acf7 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -209,4 +209,17 @@ wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl->ops->sta_rc_update(wl, wlvif, sta, changed); } +static inline int +wlcore_hw_set_peer_cap(struct wl1271 *wl, + struct ieee80211_sta_ht_cap *ht_cap, + bool allow_ht_operation, + u32 rate_set, u8 hlid) +{ + if (wl->ops->set_peer_cap) + return wl->ops->set_peer_cap(wl, ht_cap, allow_ht_operation, + rate_set, hlid); + + return 0; +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 486b7fa0259c..fed227c88a8a 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4132,10 +4132,11 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, bool enabled = bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT; - ret = wl1271_acx_set_ht_capabilities(wl, - &sta_ht_cap, - enabled, - wlvif->sta.hlid); + ret = wlcore_hw_set_peer_cap(wl, + &sta_ht_cap, + enabled, + wlvif->rate_set, + wlvif->sta.hlid); if (ret < 0) { wl1271_warning("Set ht cap failed %d", ret); goto out; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index e8245f36a67e..a4441164bb4b 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -106,6 +106,11 @@ struct wlcore_ops { u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len); void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, u32 changed); + int (*set_peer_cap)(struct wl1271 *wl, + struct ieee80211_sta_ht_cap *ht_cap, + bool allow_ht_operation, + u32 rate_set, u8 hlid); + }; enum wlcore_partitions { From f4d02007cdd56c59bdb9362c699875cb2d02c0fe Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:33 +0200 Subject: [PATCH 071/478] wlcore: remove support for injected Tx Require each incoming packet to have a valid vif. The injected Tx code path was buggy (and unused), so disallow it altogether. Cleanup a few places and add a warning so we can better discover anomalies (corrupted skbs?) masquerading as injected Tx. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 13 ++++++++----- drivers/net/wireless/ti/wlcore/tx.c | 5 +---- drivers/net/wireless/ti/wlcore/wlcore_i.h | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index fed227c88a8a..ba9f0019f20e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1179,9 +1179,13 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, int q, mapping; u8 hlid; - if (vif) - wlvif = wl12xx_vif_to_data(vif); + if (!vif) { + wl1271_debug(DEBUG_TX, "DROP skb with no vif"); + ieee80211_free_txskb(hw, skb); + return; + } + wlvif = wl12xx_vif_to_data(vif); mapping = skb_get_queue_mapping(skb); q = wl1271_tx_get_queue(mapping); @@ -1195,7 +1199,7 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, * allow these packets through. */ if (hlid == WL12XX_INVALID_LINK_ID || - (wlvif && !test_bit(hlid, wlvif->links_map)) || + (!test_bit(hlid, wlvif->links_map)) || (wlcore_is_queue_stopped(wl, q) && !wlcore_is_queue_stopped_by_reason(wl, q, WLCORE_QUEUE_STOP_REASON_WATERMARK))) { @@ -1209,8 +1213,7 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); wl->tx_queue_count[q]++; - if (wlvif) - wlvif->tx_queue_count[q]++; + wlvif->tx_queue_count[q]++; /* * The workqueue is slow to process the tx_queue and we need stop diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index fbda3cbca7e7..8f5ea89b03b1 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -157,9 +157,6 @@ u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif, { struct ieee80211_tx_info *control; - if (!wlvif || wl12xx_is_dummy_packet(wl, skb)) - return wl->system_hlid; - if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta); @@ -764,7 +761,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl) bool has_data = false; wlvif = NULL; - if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif) + if (!wl12xx_is_dummy_packet(wl, skb)) wlvif = wl12xx_vif_to_data(info->control.vif); else hlid = wl->system_hlid; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 0194f5575acf..1857b8ba87e4 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -451,6 +451,7 @@ struct wl12xx_vif { static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif) { + WARN_ON(!vif); return (struct wl12xx_vif *)vif->drv_priv; } From 5d3a160365306c4161b7064d482c26a85829f170 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Sat, 8 Dec 2012 02:58:23 +0200 Subject: [PATCH 072/478] wlcore: increase scan dwell times if no activity There's a limit on scan dwell times of max 30ms in order to avoid degrading voip traffic which could be going on while scanning. However these dwell times increase the chance of missing out on nearby APs leading to partial scan results. Allow configuration of longer dwell times in case there no active interface (i.e. no STA associated or AP up). [Arik - count started vifs using an in-driver function] [Fixed some new checkpatch warnings regarding comments in the networking subsystem. -- Luca] Signed-off-by: Eyal Shapira Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 2 ++ drivers/net/wireless/ti/wl18xx/main.c | 2 ++ drivers/net/wireless/ti/wlcore/conf.h | 22 +++++++++++++++++++++ drivers/net/wireless/ti/wlcore/scan.c | 28 +++++++++++++++++++++++++-- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 8b4827f47deb..08b67625f7ff 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -269,6 +269,8 @@ static struct wlcore_conf wl12xx_conf = { .scan = { .min_dwell_time_active = 7500, .max_dwell_time_active = 30000, + .min_dwell_time_active_long = 25000, + .max_dwell_time_active_long = 50000, .dwell_time_passive = 100000, .dwell_time_dfs = 150000, .num_probe_reqs = 2, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 55fd46b9628d..62f431a2ed34 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -396,6 +396,8 @@ static struct wlcore_conf wl18xx_conf = { .scan = { .min_dwell_time_active = 7500, .max_dwell_time_active = 30000, + .min_dwell_time_active_long = 25000, + .max_dwell_time_active_long = 50000, .dwell_time_passive = 100000, .dwell_time_dfs = 150000, .num_probe_reqs = 2, diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index fd173b9ca46a..2b96ff821341 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -1088,6 +1088,7 @@ struct conf_roam_trigger_settings { struct conf_scan_settings { /* * The minimum time to wait on each channel for active scans + * This value will be used whenever there's a connected interface. * * Range: u32 tu/1000 */ @@ -1095,11 +1096,32 @@ struct conf_scan_settings { /* * The maximum time to wait on each channel for active scans + * This value will be currently used whenever there's a + * connected interface. It shouldn't exceed 30000 (~30ms) to avoid + * possible interference of voip traffic going on while scanning. * * Range: u32 tu/1000 */ u32 max_dwell_time_active; + /* The minimum time to wait on each channel for active scans + * when it's possible to have longer scan dwell times. + * Currently this is used whenever we're idle on all interfaces. + * Longer dwell times improve detection of networks within a + * single scan. + * + * Range: u32 tu/1000 + */ + u32 min_dwell_time_active_long; + + /* The maximum time to wait on each channel for active scans + * when it's possible to have longer scan dwell times. + * See min_dwell_time_active_long + * + * Range: u32 tu/1000 + */ + u32 max_dwell_time_active_long; + /* time to wait on the channel for passive scans (in TU/1000) */ u32 dwell_time_passive; diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index 7f42f8a4d90b..6cfdeaea12f0 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -89,6 +89,25 @@ out: } +static void wlcore_started_vifs_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + int *count = (int *)data; + + if (!vif->bss_conf.idle) + (*count)++; +} + +static int wlcore_count_started_vifs(struct wl1271 *wl) +{ + int count = 0; + + ieee80211_iterate_active_interfaces_atomic(wl->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + wlcore_started_vifs_iter, &count); + return count; +} + static int wlcore_scan_get_channels(struct wl1271 *wl, struct ieee80211_channel *req_channels[], @@ -109,9 +128,14 @@ wlcore_scan_get_channels(struct wl1271 *wl, /* configure dwell times according to scan type */ if (scan_type == SCAN_TYPE_SEARCH) { struct conf_scan_settings *c = &wl->conf.scan; + bool active_vif_exists = !!wlcore_count_started_vifs(wl); - min_dwell_time_active = c->min_dwell_time_active; - max_dwell_time_active = c->max_dwell_time_active; + min_dwell_time_active = active_vif_exists ? + c->min_dwell_time_active : + c->min_dwell_time_active_long; + max_dwell_time_active = active_vif_exists ? + c->max_dwell_time_active : + c->max_dwell_time_active_long; dwell_time_passive = c->dwell_time_passive; dwell_time_dfs = c->dwell_time_dfs; } else { From 387116b89e987521653b7e9b70a13a9dc1ea6a85 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:35 +0200 Subject: [PATCH 073/478] wlcore: improve handling for Rx errors Treat Rx error code as a bitmask. This allows sending MIC failures when other error bit are on. Align Rx descriptor status mask to the FW definition. Ease debugging in case FW reports failure to decrypt on packets. Discard corrupted packets early in Rx path to avoid reporting other abnormalities with corrupted packets that also have other failure bytes on. Namely - we don't want to get a MIC failure on a corrupted packet. This is mandated by the WiFi specification - see section 11.4.2.4.1 in 802.11-2012. Signed-off-by: Eyal Shapira Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/rx.c | 29 ++++++++++++++--------------- drivers/net/wireless/ti/wlcore/rx.h | 3 +-- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 4665b9615dc9..6791a1a6afba 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -92,9 +92,10 @@ static void wl1271_rx_status(struct wl1271 *wl, status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED | RX_FLAG_DECRYPTED; - if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) { + if (unlikely(desc_err_code & WL1271_RX_DESC_MIC_FAIL)) { status->flag |= RX_FLAG_MMIC_ERROR; - wl1271_warning("Michael MIC error"); + wl1271_warning("Michael MIC error. Desc: 0x%x", + desc_err_code); } } @@ -112,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, u8 *buf; u8 beacon = 0; u8 is_data = 0; - u8 reserved = 0; + u8 reserved = 0, offset_to_data = 0; u16 seq_num; u32 pkt_data_len; @@ -132,6 +133,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, if (rx_align == WLCORE_RX_BUF_UNALIGNED) reserved = RX_BUF_ALIGN; + else if (rx_align == WLCORE_RX_BUF_PADDED) + offset_to_data = RX_BUF_ALIGN; /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) data; @@ -143,19 +146,15 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, return 0; } - switch (desc->status & WL1271_RX_DESC_STATUS_MASK) { /* discard corrupted packets */ - case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: - case WL1271_RX_DESC_DECRYPT_FAIL: - wl1271_warning("corrupted packet in RX with status: 0x%x", - desc->status & WL1271_RX_DESC_STATUS_MASK); - return -EINVAL; - case WL1271_RX_DESC_SUCCESS: - case WL1271_RX_DESC_MIC_FAIL: - break; - default: - wl1271_error("invalid RX descriptor status: 0x%x", - desc->status & WL1271_RX_DESC_STATUS_MASK); + if (desc->status & WL1271_RX_DESC_DECRYPT_FAIL) { + hdr = (void *)(data + sizeof(*desc) + offset_to_data); + wl1271_warning("corrupted packet in RX: status: 0x%x len: %d", + desc->status & WL1271_RX_DESC_STATUS_MASK, + pkt_data_len); + wl1271_dump((DEBUG_RX|DEBUG_CMD), "PKT: ", data + sizeof(*desc), + min(pkt_data_len, + ieee80211_hdrlen(hdr->frame_control))); return -EINVAL; } diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index 71eba1899915..3363f60fb7da 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -84,12 +84,11 @@ * Bits 3-5 - process_id tag (AP mode FW) * Bits 6-7 - reserved */ -#define WL1271_RX_DESC_STATUS_MASK 0x03 +#define WL1271_RX_DESC_STATUS_MASK 0x07 #define WL1271_RX_DESC_SUCCESS 0x00 #define WL1271_RX_DESC_DECRYPT_FAIL 0x01 #define WL1271_RX_DESC_MIC_FAIL 0x02 -#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03 #define RX_MEM_BLOCK_MASK 0xFF #define RX_BUF_SIZE_MASK 0xFFF00 From c23280eb1436edd30814e5e40e4c46ae4cdb3cf4 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:36 +0200 Subject: [PATCH 074/478] wlcore: set 5Ghz probe-req template for DFS channels Even passive scans on DFS channels require us to send probe requests, so configure the probe-req in this case. Also use this opportunity to prevent the code from crashing in case no SSIDs are sent from above. This will likely happen in the DFS case introduced. Even a passive scan might need the probe request configured because of DFS channels. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/scan.c | 52 +++++++++++++-------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c index daaebad78e1c..1df88d5336f3 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.c +++ b/drivers/net/wireless/ti/wl18xx/scan.c @@ -108,27 +108,27 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (cmd->active[0]) { u8 band = IEEE80211_BAND_2GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, - cmd->role_id, band, - req->ssids[0].ssid, - req->ssids[0].ssid_len, - req->ie, - req->ie_len, - false); + cmd->role_id, band, + req->ssids ? req->ssids[0].ssid : NULL, + req->ssids ? req->ssids[0].ssid_len : 0, + req->ie, + req->ie_len, + false); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); goto out; } } - if (cmd->active[1]) { + if (cmd->active[1] || cmd->dfs) { u8 band = IEEE80211_BAND_5GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, - cmd->role_id, band, - req->ssids[0].ssid, - req->ssids[0].ssid_len, - req->ie, - req->ie_len, - false); + cmd->role_id, band, + req->ssids ? req->ssids[0].ssid : NULL, + req->ssids ? req->ssids[0].ssid_len : 0, + req->ie, + req->ie_len, + false); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); goto out; @@ -230,27 +230,27 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl, if (cmd->active[0]) { u8 band = IEEE80211_BAND_2GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, - cmd->role_id, band, - req->ssids[0].ssid, - req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], - true); + cmd->role_id, band, + req->ssids ? req->ssids[0].ssid : NULL, + req->ssids ? req->ssids[0].ssid_len : 0, + ies->ie[band], + ies->len[band], + true); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); goto out; } } - if (cmd->active[1]) { + if (cmd->active[1] || cmd->dfs) { u8 band = IEEE80211_BAND_5GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, - cmd->role_id, band, - req->ssids[0].ssid, - req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], - true); + cmd->role_id, band, + req->ssids ? req->ssids[0].ssid : NULL, + req->ssids ? req->ssids[0].ssid_len : 0, + ies->ie[band], + ies->len[band], + true); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); goto out; From c91ec5f3ada86807ea4857fc5793a4efe99c9de3 Mon Sep 17 00:00:00 2001 From: Yair Shapira Date: Wed, 28 Nov 2012 11:42:39 +0200 Subject: [PATCH 075/478] wl18xx: fix a bug in wl->num_rx_desc initialization wl->num_rx_desc was mistakenly initialized with WL18XX_NUM_TX_DESCRIPTORS but it should use WL18XX_NUM_RX_DESCRIPTORS instead. This bug was passed unnoticed because currently both RX and TX descriptors are initialized to the same value (32). Signed-off-by: Yair Shapira Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 62f431a2ed34..a35eba2b8d8b 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1501,7 +1501,7 @@ static int wl18xx_setup(struct wl1271 *wl) wl->rtable = wl18xx_rtable; wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS; - wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS; + wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS; wl->num_channels = 2; wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES; wl->band_rate_to_idx = wl18xx_band_rate_to_idx; From f1626fd8983a5bc68ce2879865cce297eb96c0b4 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:40 +0200 Subject: [PATCH 076/478] wlcore/wl18xx: change priority calculations for links Update the 18xx FW status private part to include Tx related link priorities. Introduce new HW ops to determine link priority per chip family. For 18xx the changes are: - Suspended links are at most low priority and Tx for them is stopped beyond the suspend threshold. - Active links now get their thresholds directly from FW - There's a new "stop" threshold for active links, at which point a link stops receiving new packets. Update the min 18xx FW version required to make sure suspended links bitmap is advertised by the FW. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 22 +++++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 41 +++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/wl18xx.h | 38 ++++++++++++++++++++++- drivers/net/wireless/ti/wlcore/hw_ops.h | 20 ++++++++++++ drivers/net/wireless/ti/wlcore/tx.c | 18 ++--------- drivers/net/wireless/ti/wlcore/wlcore.h | 5 ++- 6 files changed, 127 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 08b67625f7ff..6d4a02171abc 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1627,6 +1627,26 @@ static int wl12xx_set_peer_cap(struct wl1271 *wl, hlid); } +static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + u8 thold; + + if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) + thold = wl->conf.tx.fast_link_thold; + else + thold = wl->conf.tx.slow_link_thold; + + return lnk->allocated_pkts < thold; +} + +static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + /* any link is good for low priority */ + return true; +} + static int wl12xx_setup(struct wl1271 *wl); static struct wlcore_ops wl12xx_ops = { @@ -1663,6 +1683,8 @@ static struct wlcore_ops wl12xx_ops = { .channel_switch = wl12xx_cmd_channel_switch, .pre_pkt_send = NULL, .set_peer_cap = wl12xx_set_peer_cap, + .lnk_high_prio = wl12xx_lnk_high_prio, + .lnk_low_prio = wl12xx_lnk_low_prio, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index a35eba2b8d8b..ed34a7f3391c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1397,6 +1397,45 @@ static int wl18xx_set_peer_cap(struct wl1271 *wl, rate_set, hlid); } +static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + u8 thold; + struct wl18xx_fw_status_priv *status_priv = + (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; + u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); + + /* suspended links are never high priority */ + if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) + return false; + + /* the priority thresholds are taken from FW */ + if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) + thold = status_priv->tx_fast_link_prio_threshold; + else + thold = status_priv->tx_slow_link_prio_threshold; + + return lnk->allocated_pkts < thold; +} + +static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + u8 thold; + struct wl18xx_fw_status_priv *status_priv = + (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; + u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); + + if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) + thold = status_priv->tx_suspend_threshold; + else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) + thold = status_priv->tx_fast_stop_threshold; + else + thold = status_priv->tx_slow_stop_threshold; + + return lnk->allocated_pkts < thold; +} + static int wl18xx_setup(struct wl1271 *wl); static struct wlcore_ops wl18xx_ops = { @@ -1434,6 +1473,8 @@ static struct wlcore_ops wl18xx_ops = { .pre_pkt_send = wl18xx_pre_pkt_send, .sta_rc_update = wl18xx_sta_rc_update, .set_peer_cap = wl18xx_set_peer_cap, + .lnk_high_prio = wl18xx_lnk_high_prio, + .lnk_low_prio = wl18xx_lnk_low_prio, }; /* HT cap appropriate for wide channels in 2Ghz */ diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 3105fbf79ae4..e6ea78c7da36 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -68,7 +68,43 @@ struct wl18xx_fw_status_priv { */ u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC]; - u8 padding[2]; + /* A bitmap representing the currently suspended links. The suspend + * is short lived, for multi-channel Tx requirements. + */ + __le32 link_suspend_bitmap; + + /* packet threshold for an "almost empty" AC, + * for Tx schedulng purposes + */ + u8 tx_ac_threshold; + + /* number of packets to queue up for a link in PS */ + u8 tx_ps_threshold; + + /* number of packet to queue up for a suspended link */ + u8 tx_suspend_threshold; + + /* Should have less than this number of packets in queue of a slow + * link to qualify as high priority link + */ + u8 tx_slow_link_prio_threshold; + + /* Should have less than this number of packets in queue of a fast + * link to qualify as high priority link + */ + u8 tx_fast_link_prio_threshold; + + /* Should have less than this number of packets in queue of a slow + * link before we stop queuing up packets for it. + */ + u8 tx_slow_stop_threshold; + + /* Should have less than this number of packets in queue of a fast + * link before we stop queuing up packets for it. + */ + u8 tx_fast_stop_threshold; + + u8 padding[3]; }; #define WL18XX_PHY_VERSION_MAX_LEN 20 diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 4db03e14acf7..7fd260c02a0a 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -222,4 +222,24 @@ wlcore_hw_set_peer_cap(struct wl1271 *wl, return 0; } +static inline bool +wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + if (!wl->ops->lnk_high_prio) + BUG_ON(1); + + return wl->ops->lnk_high_prio(wl, hlid, lnk); +} + +static inline bool +wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk) +{ + if (!wl->ops->lnk_low_prio) + BUG_ON(1); + + return wl->ops->lnk_low_prio(wl, hlid, lnk); +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 8f5ea89b03b1..25cfd35ab1a2 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -509,28 +509,16 @@ static struct sk_buff *wlcore_lnk_dequeue(struct wl1271 *wl, return skb; } -static bool wlcore_lnk_high_prio(struct wl1271 *wl, u8 hlid, - struct wl1271_link *lnk) -{ - u8 thold; - - if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) - thold = wl->conf.tx.fast_link_thold; - else - thold = wl->conf.tx.slow_link_thold; - - return lnk->allocated_pkts < thold; -} - static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct wl1271 *wl, u8 hlid, u8 ac, u8 *low_prio_hlid) { struct wl1271_link *lnk = &wl->links[hlid]; - if (!wlcore_lnk_high_prio(wl, hlid, lnk)) { + if (!wlcore_hw_lnk_high_prio(wl, hlid, lnk)) { if (*low_prio_hlid == WL12XX_INVALID_LINK_ID && - !skb_queue_empty(&lnk->tx_queue[ac])) + !skb_queue_empty(&lnk->tx_queue[ac]) && + wlcore_hw_lnk_low_prio(wl, hlid, lnk)) /* we found the first non-empty low priority queue */ *low_prio_hlid = hlid; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index a4441164bb4b..58e52505ec17 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -110,7 +110,10 @@ struct wlcore_ops { struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, u32 rate_set, u8 hlid); - + bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk); + bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid, + struct wl1271_link *lnk); }; enum wlcore_partitions { From 98323895c021ff010685ba77773af876eff503a5 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:41 +0200 Subject: [PATCH 077/478] wl18xx: limit Tx for the AP single-STA-in-PSM case Treat a single connected STA in PSM as a slow link and regulate Tx speed according to slow link priority/stop thresholds. This allows us to avoid flooding the FW, while delivering decent throughput to a peer in forced-PSM. [Small simplification of the if statements -- Luca] Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index ed34a7f3391c..3f6762c9b9a7 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1410,7 +1410,8 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, return false; /* the priority thresholds are taken from FW */ - if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) + if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) && + !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map)) thold = status_priv->tx_fast_link_prio_threshold; else thold = status_priv->tx_slow_link_prio_threshold; @@ -1428,7 +1429,8 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) thold = status_priv->tx_suspend_threshold; - else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) + else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) && + !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map)) thold = status_priv->tx_fast_stop_threshold; else thold = status_priv->tx_slow_stop_threshold; From 9a1009684df5295883ba2eb85066a23ed3c3f6a6 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:42 +0200 Subject: [PATCH 078/478] wlcore: use link count for single-STA-PSM optimization Only allow a PSM STA to congest FW memory when it is the single active link. Being a single STA doesn't imply a single link - there might be other links on other roles. [Changed WARN_ON to WARN_ON_ONCE -- Luca] Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 4 ++++ drivers/net/wireless/ti/wlcore/main.c | 12 +++++++----- drivers/net/wireless/ti/wlcore/tx.c | 10 +++++----- drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 1fbee0d51872..14734b7fc6e8 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -328,6 +328,8 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) wl->fw_status_2->counters.tx_lnk_free_pkts[link]; wl->links[link].wlvif = wlvif; *hlid = link; + + wl->active_link_count++; return 0; } @@ -357,6 +359,8 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) wl->links[*hlid].wlvif = NULL; *hlid = WL12XX_INVALID_LINK_ID; + wl->active_link_count--; + WARN_ON_ONCE(wl->active_link_count < 0); } static u8 wlcore_get_native_channel_type(u8 nl_channel_type) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ba9f0019f20e..b78c47dc0164 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -334,10 +334,10 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid, u8 tx_pkts) { - bool fw_ps, single_sta; + bool fw_ps, single_link; fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); - single_sta = (wl->active_sta_count == 1); + single_link = (wl->active_link_count == 1); /* * Wake up from high level PS if the STA is asleep with too little @@ -348,10 +348,10 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, /* * Start high-level PS if the STA is asleep with enough blocks in FW. - * Make an exception if this is the only connected station. In this - * case FW-memory congestion is not a problem. + * Make an exception if this is the only connected link. In this + * case FW-memory congestion is less of a problem. */ - else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) + else if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_start(wl, wlvif, hlid, true); } @@ -1890,6 +1890,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) memset(wl->roc_map, 0, sizeof(wl->roc_map)); memset(wl->session_ids, 0, sizeof(wl->session_ids)); wl->active_sta_count = 0; + wl->active_link_count = 0; /* The system link is always allocated */ wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0; @@ -5728,6 +5729,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, wl->platform_quirks = 0; wl->system_hlid = WL12XX_SYSTEM_HLID; wl->active_sta_count = 0; + wl->active_link_count = 0; wl->fwlog_size = 0; init_waitqueue_head(&wl->fwlog_waitq); diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 25cfd35ab1a2..76dbb6f66d17 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -104,7 +104,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) { - bool fw_ps, single_sta; + bool fw_ps, single_link; u8 tx_pkts; if (WARN_ON(!test_bit(hlid, wlvif->links_map))) @@ -112,15 +112,15 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl, fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); tx_pkts = wl->links[hlid].allocated_pkts; - single_sta = (wl->active_sta_count == 1); + single_link = (wl->active_link_count == 1); /* * if in FW PS and there is enough data in FW we can put the link * into high-level PS and clean out its TX queues. - * Make an exception if this is the only connected station. In this - * case FW-memory congestion is not a problem. + * Make an exception if this is the only connected link. In this + * case FW-memory congestion is less of a problem. */ - if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) + if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_start(wl, wlvif, hlid, true); } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 58e52505ec17..22347e864f51 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -369,6 +369,9 @@ struct wl1271 { */ struct wl1271_link links[WL12XX_MAX_LINKS]; + /* number of currently active links */ + int active_link_count; + /* Fast/slow links bitmap according to FW */ u32 fw_fast_lnk_map; From 1c33db782d1d0d9be83feacbb065cd4956f485e7 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 30 Nov 2012 00:48:03 +0200 Subject: [PATCH 079/478] wlcore: use separate HW queue for each AC in each vif Start using the new hw_queue mechanism in mac80211 and give each AC in each vif its own hw_queue number. This allows us to stop an AC in a vif independently from other vifs. Change the Tx watermark handling functions to count packets per AC in vif. From now on fast links should not be able to hurt the throughput of slow links on the same AC but on different vifs. Change internal queue mgmt functions to operate per vif, to support the new Tx watermark granularity. Make the global versions of the queue stop/start functions to use the global mac80211 API for queue mgmt. This helps in situations where the driver currently doesn't know all the vifs that reside in mac80211. Recovery is a good example for such a case. [Moved hw_base_queue addition into the wlcore_tx_get_mac80211_queue() function; changed WARN_ONs to WARN_ON_ONCEs; simplified for loops; fixed new checkpatch warnings. -- Luca] Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 101 ++++++++++++++++++++-- drivers/net/wireless/ti/wlcore/tx.c | 96 +++++++++++--------- drivers/net/wireless/ti/wlcore/tx.h | 30 ++++--- drivers/net/wireless/ti/wlcore/wlcore.h | 3 +- drivers/net/wireless/ti/wlcore/wlcore_i.h | 9 ++ 5 files changed, 176 insertions(+), 63 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index b78c47dc0164..89f69d949443 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1200,8 +1200,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, */ if (hlid == WL12XX_INVALID_LINK_ID || (!test_bit(hlid, wlvif->links_map)) || - (wlcore_is_queue_stopped(wl, q) && - !wlcore_is_queue_stopped_by_reason(wl, q, + (wlcore_is_queue_stopped(wl, wlvif, q) && + !wlcore_is_queue_stopped_by_reason(wl, wlvif, q, WLCORE_QUEUE_STOP_REASON_WATERMARK))) { wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q); ieee80211_free_txskb(hw, skb); @@ -1219,11 +1219,11 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ - if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK && - !wlcore_is_queue_stopped_by_reason(wl, q, + if (wlvif->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK && + !wlcore_is_queue_stopped_by_reason(wl, wlvif, q, WLCORE_QUEUE_STOP_REASON_WATERMARK)) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q); - wlcore_stop_queue_locked(wl, q, + wlcore_stop_queue_locked(wl, wlvif, q, WLCORE_QUEUE_STOP_REASON_WATERMARK); } @@ -2290,6 +2290,81 @@ static void wl12xx_force_active_psm(struct wl1271 *wl) } } +struct wlcore_hw_queue_iter_data { + unsigned long hw_queue_map[BITS_TO_LONGS(WLCORE_NUM_MAC_ADDRESSES)]; + /* current vif */ + struct ieee80211_vif *vif; + /* is the current vif among those iterated */ + bool cur_running; +}; + +static void wlcore_hw_queue_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct wlcore_hw_queue_iter_data *iter_data = data; + + if (WARN_ON_ONCE(vif->hw_queue[0] == IEEE80211_INVAL_HW_QUEUE)) + return; + + if (iter_data->cur_running || vif == iter_data->vif) { + iter_data->cur_running = true; + return; + } + + __set_bit(vif->hw_queue[0] / NUM_TX_QUEUES, iter_data->hw_queue_map); +} + +static int wlcore_allocate_hw_queue_base(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + struct wlcore_hw_queue_iter_data iter_data = {}; + int i, q_base; + + iter_data.vif = vif; + + /* mark all bits taken by active interfaces */ + ieee80211_iterate_active_interfaces_atomic(wl->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + wlcore_hw_queue_iter, &iter_data); + + /* the current vif is already running in mac80211 (resume/recovery) */ + if (iter_data.cur_running) { + wlvif->hw_queue_base = vif->hw_queue[0]; + wl1271_debug(DEBUG_MAC80211, + "using pre-allocated hw queue base %d", + wlvif->hw_queue_base); + + /* interface type might have changed type */ + goto adjust_cab_queue; + } + + q_base = find_first_zero_bit(iter_data.hw_queue_map, + WLCORE_NUM_MAC_ADDRESSES); + if (q_base >= WLCORE_NUM_MAC_ADDRESSES) + return -EBUSY; + + wlvif->hw_queue_base = q_base * NUM_TX_QUEUES; + wl1271_debug(DEBUG_MAC80211, "allocating hw queue base: %d", + wlvif->hw_queue_base); + + for (i = 0; i < NUM_TX_QUEUES; i++) { + wl->queue_stop_reasons[wlvif->hw_queue_base + i] = 0; + /* register hw queues in mac80211 */ + vif->hw_queue[i] = wlvif->hw_queue_base + i; + } + +adjust_cab_queue: + /* the last places are reserved for cab queues per interface */ + if (wlvif->bss_type == BSS_TYPE_AP_BSS) + vif->cab_queue = NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES + + wlvif->hw_queue_base / NUM_TX_QUEUES; + else + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; + + return 0; +} + static int wl1271_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -2336,6 +2411,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, goto out; } + ret = wlcore_allocate_hw_queue_base(wl, wlvif); + if (ret < 0) + goto out; + if (wl12xx_need_fw_change(wl, vif_count, true)) { wl12xx_force_active_psm(wl); set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags); @@ -5564,7 +5643,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_AP_LINK_PS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | - IEEE80211_HW_SCAN_WHILE_IDLE; + IEEE80211_HW_SCAN_WHILE_IDLE | + IEEE80211_HW_QUEUE_CONTROL; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); @@ -5631,7 +5711,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl->bands[IEEE80211_BAND_5GHZ]; - wl->hw->queues = 4; + /* + * allow 4 queues per mac address we support + + * 1 cab queue per mac + one global offchannel Tx queue + */ + wl->hw->queues = (NUM_TX_QUEUES + 1) * WLCORE_NUM_MAC_ADDRESSES + 1; + + /* the last queue is the offchannel queue */ + wl->hw->offchannel_tx_hw_queue = wl->hw->queues - 1; wl->hw->max_rates = 1; wl->hw->wiphy->reg_notifier = wl1271_reg_notify; diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 76dbb6f66d17..d464a8eba7b0 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -452,14 +452,17 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, void wl1271_handle_tx_low_watermark(struct wl1271 *wl) { int i; + struct wl12xx_vif *wlvif; - for (i = 0; i < NUM_TX_QUEUES; i++) { - if (wlcore_is_queue_stopped_by_reason(wl, i, - WLCORE_QUEUE_STOP_REASON_WATERMARK) && - wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) { - /* firmware buffer has space, restart queues */ - wlcore_wake_queue(wl, i, - WLCORE_QUEUE_STOP_REASON_WATERMARK); + wl12xx_for_each_wlvif(wl, wlvif) { + for (i = 0; i < NUM_TX_QUEUES; i++) { + if (wlcore_is_queue_stopped_by_reason(wl, wlvif, i, + WLCORE_QUEUE_STOP_REASON_WATERMARK) && + wlvif->tx_queue_count[i] <= + WL1271_TX_QUEUE_LOW_WATERMARK) + /* firmware buffer has space, restart queues */ + wlcore_wake_queue(wl, wlvif, i, + WLCORE_QUEUE_STOP_REASON_WATERMARK); } } } @@ -1194,44 +1197,46 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) } EXPORT_SYMBOL_GPL(wl1271_tx_min_rate_get); -void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, - enum wlcore_queue_stop_reason reason) +void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue, enum wlcore_queue_stop_reason reason) { - bool stopped = !!wl->queue_stop_reasons[queue]; + int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); + bool stopped = !!wl->queue_stop_reasons[hwq]; /* queue should not be stopped for this reason */ - WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue])); + WARN_ON_ONCE(test_and_set_bit(reason, &wl->queue_stop_reasons[hwq])); if (stopped) return; - ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); + ieee80211_stop_queue(wl->hw, hwq); } -void wlcore_stop_queue(struct wl1271 *wl, u8 queue, +void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason) { unsigned long flags; spin_lock_irqsave(&wl->wl_lock, flags); - wlcore_stop_queue_locked(wl, queue, reason); + wlcore_stop_queue_locked(wl, wlvif, queue, reason); spin_unlock_irqrestore(&wl->wl_lock, flags); } -void wlcore_wake_queue(struct wl1271 *wl, u8 queue, +void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason) { unsigned long flags; + int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); spin_lock_irqsave(&wl->wl_lock, flags); /* queue should not be clear for this reason */ - WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue])); + WARN_ON_ONCE(!test_and_clear_bit(reason, &wl->queue_stop_reasons[hwq])); - if (wl->queue_stop_reasons[queue]) + if (wl->queue_stop_reasons[hwq]) goto out; - ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); + ieee80211_wake_queue(wl->hw, hwq); out: spin_unlock_irqrestore(&wl->wl_lock, flags); @@ -1241,48 +1246,55 @@ void wlcore_stop_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason) { int i; + unsigned long flags; - for (i = 0; i < NUM_TX_QUEUES; i++) - wlcore_stop_queue(wl, i, reason); + spin_lock_irqsave(&wl->wl_lock, flags); + + /* mark all possible queues as stopped */ + for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++) + WARN_ON_ONCE(test_and_set_bit(reason, + &wl->queue_stop_reasons[i])); + + /* use the global version to make sure all vifs in mac80211 we don't + * know are stopped. + */ + ieee80211_stop_queues(wl->hw); + + spin_unlock_irqrestore(&wl->wl_lock, flags); } -EXPORT_SYMBOL_GPL(wlcore_stop_queues); void wlcore_wake_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason) -{ - int i; - - for (i = 0; i < NUM_TX_QUEUES; i++) - wlcore_wake_queue(wl, i, reason); -} -EXPORT_SYMBOL_GPL(wlcore_wake_queues); - -void wlcore_reset_stopped_queues(struct wl1271 *wl) { int i; unsigned long flags; spin_lock_irqsave(&wl->wl_lock, flags); - for (i = 0; i < NUM_TX_QUEUES; i++) { - if (!wl->queue_stop_reasons[i]) - continue; + /* mark all possible queues as awake */ + for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++) + WARN_ON_ONCE(!test_and_clear_bit(reason, + &wl->queue_stop_reasons[i])); - wl->queue_stop_reasons[i] = 0; - ieee80211_wake_queue(wl->hw, - wl1271_tx_get_mac80211_queue(i)); - } + /* use the global version to make sure all vifs in mac80211 we don't + * know are woken up. + */ + ieee80211_wake_queues(wl->hw); spin_unlock_irqrestore(&wl->wl_lock, flags); } -bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, - enum wlcore_queue_stop_reason reason) +bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, + struct wl12xx_vif *wlvif, u8 queue, + enum wlcore_queue_stop_reason reason) { - return test_bit(reason, &wl->queue_stop_reasons[queue]); + int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); + return test_bit(reason, &wl->queue_stop_reasons[hwq]); } -bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue) +bool wlcore_is_queue_stopped(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue) { - return !!wl->queue_stop_reasons[queue]; + int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); + return !!wl->queue_stop_reasons[hwq]; } diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 349520d8b724..751bb6f46cbf 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -207,19 +207,22 @@ static inline int wl1271_tx_get_queue(int queue) } } -static inline int wl1271_tx_get_mac80211_queue(int queue) +static inline +int wlcore_tx_get_mac80211_queue(struct wl12xx_vif *wlvif, int queue) { + int mac_queue = wlvif->hw_queue_base; + switch (queue) { case CONF_TX_AC_VO: - return 0; + return mac_queue + 0; case CONF_TX_AC_VI: - return 1; + return mac_queue + 1; case CONF_TX_AC_BE: - return 2; + return mac_queue + 2; case CONF_TX_AC_BK: - return 3; + return mac_queue + 3; default: - return 2; + return mac_queue + 2; } } @@ -252,20 +255,21 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids); unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, unsigned int packet_length); void wl1271_free_tx_id(struct wl1271 *wl, int id); -void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, - enum wlcore_queue_stop_reason reason); -void wlcore_stop_queue(struct wl1271 *wl, u8 queue, +void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue, enum wlcore_queue_stop_reason reason); +void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason); -void wlcore_wake_queue(struct wl1271 *wl, u8 queue, +void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason); void wlcore_stop_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason); void wlcore_wake_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason); -void wlcore_reset_stopped_queues(struct wl1271 *wl); -bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, +bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, + struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason); -bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue); +bool wlcore_is_queue_stopped(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue); /* from main.c */ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 22347e864f51..6c2fd0398e9d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -255,7 +255,8 @@ struct wl1271 { /* Frames scheduled for transmission, not handled yet */ int tx_queue_count[NUM_TX_QUEUES]; - unsigned long queue_stop_reasons[NUM_TX_QUEUES]; + unsigned long queue_stop_reasons[ + NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES]; /* Frames received, not handled yet by mac80211 */ struct sk_buff_head deferred_rx_queue; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 1857b8ba87e4..ee49676de9ec 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -429,6 +429,15 @@ struct wl12xx_vif { /* number of in connection stations */ int inconn_count; + /* + * This vif's queues are mapped to mac80211 HW queues as: + * VO - hw_queue_base + * VI - hw_queue_base + 1 + * BE - hw_queue_base + 2 + * BK - hw_queue_base + 3 + */ + int hw_queue_base; + /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) From d6037d22f30738e942ddfd29e3fef17deb075420 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:44 +0200 Subject: [PATCH 080/478] wlcore: don't take mutex before stopping queues Protect all functions touching queue_stop_reasons by spin-lock, since they are accessed by op_tx. Now there's no need to take the mutex before caling wlcore_queue_xxx functions. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 9 +++------ drivers/net/wireless/ti/wlcore/tx.c | 23 +++++++++++++++++++++-- drivers/net/wireless/ti/wlcore/tx.h | 9 +++++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 89f69d949443..56a5308b5b03 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1200,8 +1200,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, */ if (hlid == WL12XX_INVALID_LINK_ID || (!test_bit(hlid, wlvif->links_map)) || - (wlcore_is_queue_stopped(wl, wlvif, q) && - !wlcore_is_queue_stopped_by_reason(wl, wlvif, q, + (wlcore_is_queue_stopped_locked(wl, wlvif, q) && + !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q, WLCORE_QUEUE_STOP_REASON_WATERMARK))) { wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q); ieee80211_free_txskb(hw, skb); @@ -1220,7 +1220,7 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, * the queue here, otherwise the queue will get too long. */ if (wlvif->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK && - !wlcore_is_queue_stopped_by_reason(wl, wlvif, q, + !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q, WLCORE_QUEUE_STOP_REASON_WATERMARK)) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q); wlcore_stop_queue_locked(wl, wlvif, q, @@ -3229,10 +3229,7 @@ static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, * stop the queues and flush to ensure the next packets are * in sync with FW spare block accounting */ - mutex_lock(&wl->mutex); wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK); - mutex_unlock(&wl->mutex); - wl1271_tx_flush(wl); } diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index d464a8eba7b0..894ddc73a890 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -1287,14 +1287,33 @@ void wlcore_wake_queues(struct wl1271 *wl, bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason) +{ + unsigned long flags; + bool stopped; + + spin_lock_irqsave(&wl->wl_lock, flags); + stopped = wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, queue, + reason); + spin_unlock_irqrestore(&wl->wl_lock, flags); + + return stopped; +} + +bool wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl, + struct wl12xx_vif *wlvif, u8 queue, + enum wlcore_queue_stop_reason reason) { int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); + + WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock)); return test_bit(reason, &wl->queue_stop_reasons[hwq]); } -bool wlcore_is_queue_stopped(struct wl1271 *wl, struct wl12xx_vif *wlvif, - u8 queue) +bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue) { int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue); + + WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock)); return !!wl->queue_stop_reasons[hwq]; } diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 751bb6f46cbf..55aa4acf9105 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -268,8 +268,13 @@ void wlcore_wake_queues(struct wl1271 *wl, bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue, enum wlcore_queue_stop_reason reason); -bool wlcore_is_queue_stopped(struct wl1271 *wl, struct wl12xx_vif *wlvif, - u8 queue); +bool +wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + u8 queue, + enum wlcore_queue_stop_reason reason); +bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 queue); /* from main.c */ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); From 9ae5d8d4b7b845869a04576a7bc5fa6cf9716cd5 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:45 +0200 Subject: [PATCH 081/478] wlcore: consolidate Rx BA bitmap management to links struct Remove the STA specific ba_rx_bitmap field and use the common links structure. This simplifies code setting/checking the BA bitmap. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 1 - drivers/net/wireless/ti/wlcore/event.c | 5 +++-- drivers/net/wireless/ti/wlcore/main.c | 4 ++-- drivers/net/wireless/ti/wlcore/tx.c | 1 - drivers/net/wireless/ti/wlcore/wlcore_i.h | 1 - 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index f115fba41ad7..e70a7c864865 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -560,7 +560,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, if (wlvif->bss_type == BSS_TYPE_STA_BSS || wlvif->bss_type == BSS_TYPE_IBSS) { VIF_STATE_PRINT_INT(sta.hlid); - VIF_STATE_PRINT_INT(sta.ba_rx_bitmap); VIF_STATE_PRINT_INT(sta.basic_rate_idx); VIF_STATE_PRINT_INT(sta.ap_rate_idx); VIF_STATE_PRINT_INT(sta.p2p_rate_idx); diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index cb32c021a9f6..2776d953b6b4 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -58,9 +58,10 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); if (wlvif->bss_type != BSS_TYPE_AP_BSS) { - if (!wlvif->sta.ba_rx_bitmap) + u8 hlid = wlvif->sta.hlid; + if (!wl->links[hlid].ba_bitmap) return; - ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap, + ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap, vif->bss_conf.bssid); } else { u8 hlid; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 56a5308b5b03..227c571a2690 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4764,18 +4764,18 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, if (wlvif->bss_type == BSS_TYPE_STA_BSS) { hlid = wlvif->sta.hlid; - ba_bitmap = &wlvif->sta.ba_rx_bitmap; } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) { struct wl1271_station *wl_sta; wl_sta = (struct wl1271_station *)sta->drv_priv; hlid = wl_sta->hlid; - ba_bitmap = &wl->links[hlid].ba_bitmap; } else { ret = -EINVAL; goto out; } + ba_bitmap = &wl->links[hlid].ba_bitmap; + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 894ddc73a890..ece392c54d9c 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -1066,7 +1066,6 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) wl1271_free_sta(wl, wlvif, i); } else { u8 hlid = i; - wlvif->sta.ba_rx_bitmap = 0; wl12xx_free_link(wl, wlvif, &hlid); } } diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index ee49676de9ec..20316ac328a2 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -330,7 +330,6 @@ struct wl12xx_vif { union { struct { u8 hlid; - u8 ba_rx_bitmap; u8 basic_rate_idx; u8 ap_rate_idx; From c80daad625b382fcd72ad68a5c0a26933263aa85 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:46 +0200 Subject: [PATCH 082/478] wl18xx: support MIMO only if HT mode is not forced to SISO Don't use MIMO rates when HT mode is forced to SISO, even if we have multiple antennas. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 3f6762c9b9a7..0ff9bc7b1f33 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1078,7 +1078,12 @@ static bool wl18xx_is_mimo_supported(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; - return priv->conf.phy.number_of_assembled_ant2_4 >= 2; + /* only support MIMO with multiple antennas, and when SISO + * is not forced through config + */ + return (priv->conf.phy.number_of_assembled_ant2_4 >= 2) && + (priv->conf.ht.mode != HT_MODE_WIDE) && + (priv->conf.ht.mode != HT_MODE_SISO20); } /* From 0b70078c38fd1c8d69cf52b93e8c3bdd719a2394 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Wed, 28 Nov 2012 11:42:47 +0200 Subject: [PATCH 083/478] wlcore: support scan reports during periodic scan FW API changed and now PERIODIC_SCAN_REPORT_EVENT is sent in case results were found at the end of each sched scan cycle. Previous FW was missing that and broke sched scan. This API change is available from 18xx FW 8.5.0.0.27 [Arik - move changes to 18xx specific files, align FW structures to latest for scan command] Signed-off-by: Eyal Shapira Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/event.c | 10 +++++++--- drivers/net/wireless/ti/wl18xx/event.c | 8 ++++++++ drivers/net/wireless/ti/wl18xx/event.h | 1 + drivers/net/wireless/ti/wl18xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/scan.c | 4 ++++ drivers/net/wireless/ti/wl18xx/scan.h | 18 +++++++++++++++--- drivers/net/wireless/ti/wlcore/event.c | 10 ---------- drivers/net/wireless/ti/wlcore/event.h | 2 -- drivers/net/wireless/ti/wlcore/scan.c | 3 ++- drivers/net/wireless/ti/wlcore/scan.h | 2 +- 10 files changed, 39 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/event.c b/drivers/net/wireless/ti/wl12xx/event.c index 6437dac12e70..6ac0ed751da8 100644 --- a/drivers/net/wireless/ti/wl12xx/event.c +++ b/drivers/net/wireless/ti/wl12xx/event.c @@ -64,9 +64,13 @@ int wl12xx_process_mailbox_events(struct wl1271 *wl) wl12xx_scan_completed(wl, wl->scan_wlvif); } - if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) - wlcore_event_sched_scan_report(wl, - mbox->scheduled_scan_status); + if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, + "PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)", + mbox->scheduled_scan_status); + + wlcore_scan_sched_scan_results(wl); + } if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) wlcore_event_sched_scan_completed(wl, diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index 02ef5aa9da6b..c9199d7804c6 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -61,6 +61,14 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) wl18xx_scan_completed(wl, wl->scan_wlvif); } + if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, + "PERIODIC_SCAN_REPORT_EVENT (results %d)", + mbox->number_of_sched_scan_results); + + wlcore_scan_sched_scan_results(wl); + } + if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) wlcore_event_sched_scan_completed(wl, 1); diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index b55fd31e471a..398f3d2c0a6c 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -37,6 +37,7 @@ enum { BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(17), REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18), DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19), + PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20), }; struct wl18xx_event_mailbox { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 0ff9bc7b1f33..ec331e9e0b74 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -890,6 +890,7 @@ static int wl18xx_boot(struct wl1271 *wl) SCAN_COMPLETE_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PERIODIC_SCAN_COMPLETE_EVENT_ID | + PERIODIC_SCAN_REPORT_EVENT_ID | DUMMY_PACKET_EVENT_ID | PEER_REMOVE_COMPLETE_EVENT_ID | BA_SESSION_RX_CONSTRAINT_EVENT_ID | diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c index 1df88d5336f3..09d944505ac0 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.c +++ b/drivers/net/wireless/ti/wl18xx/scan.c @@ -227,6 +227,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl, cmd->tag = WL1271_SCAN_DEFAULT_TAG; + /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */ + cmd->report_threshold = 1; + cmd->terminate_on_report = 0; + if (cmd->active[0]) { u8 band = IEEE80211_BAND_2GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h index 5eb8c7fc1567..eadee42689d1 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.h +++ b/drivers/net/wireless/ti/wl18xx/scan.h @@ -78,8 +78,7 @@ struct wl18xx_cmd_scan_params { __le16 long_cycles_sec; u8 short_cycles_count; u8 total_cycles; /* 0 - infinite */ - u8 rate; - u8 padding[1]; + u8 padding[2]; union { struct { @@ -93,7 +92,20 @@ struct wl18xx_cmd_scan_params { u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ u8 tag; - u8 padding1[2]; + u8 rate; + + /* send SCAN_REPORT_EVENT in periodic scans after each cycle + * if number of results >= report_threshold. Must be 0 for + * non periodic scans + */ + u8 report_threshold; + + /* Should periodic scan stop after a report event was created. + * Must be 0 for non periodic scans. + */ + u8 terminate_on_report; + + u8 padding1[3]; } __packed; struct wl18xx_cmd_scan_stop { diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 2776d953b6b4..70f289aa1bc6 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -94,16 +94,6 @@ void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable) } EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense); -void wlcore_event_sched_scan_report(struct wl1271 *wl, - u8 status) -{ - wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)", - status); - - wl1271_scan_sched_scan_results(wl); -} -EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_report); - void wlcore_event_sched_scan_completed(struct wl1271 *wl, u8 status) { diff --git a/drivers/net/wireless/ti/wlcore/event.h b/drivers/net/wireless/ti/wlcore/event.h index b4353be68ddb..acc7a59d3828 100644 --- a/drivers/net/wireless/ti/wlcore/event.h +++ b/drivers/net/wireless/ti/wlcore/event.h @@ -70,8 +70,6 @@ int wl1271_event_unmask(struct wl1271 *wl); int wl1271_event_handle(struct wl1271 *wl, u8 mbox); void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable); -void wlcore_event_sched_scan_report(struct wl1271 *wl, - u8 status); void wlcore_event_sched_scan_completed(struct wl1271 *wl, u8 status); void wlcore_event_ba_rx_constraint(struct wl1271 *wl, diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index 6cfdeaea12f0..f407101e525b 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -460,9 +460,10 @@ out: } EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list); -void wl1271_scan_sched_scan_results(struct wl1271 *wl) +void wlcore_scan_sched_scan_results(struct wl1271 *wl) { wl1271_debug(DEBUG_SCAN, "got periodic scan results"); ieee80211_sched_scan_results(wl->hw); } +EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_results); diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index 9ae606555aeb..a6ab24b5c0f9 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -39,7 +39,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies); int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif); -void wl1271_scan_sched_scan_results(struct wl1271 *wl); +void wlcore_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_MAX_CHANNELS 24 #define WL1271_SCAN_DEFAULT_TAG 1 From 2fd8a3bb49400a4a0fe9d9cd9d49019e7a46403e Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 28 Nov 2012 11:42:48 +0200 Subject: [PATCH 084/478] wl18xx: count HW block spare based correctly on keys We have no idea how many VIFs there are requiring a special spare, we know just about the number of keys set. Rename the counter appropriately and toggle it whenever a special key is added/removed. Previously this was only changed once, since it was toggled whenever the actual spare was changed. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 58 ++++++++++++++----------- drivers/net/wireless/ti/wl18xx/wl18xx.h | 4 +- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index ec331e9e0b74..6b57686c5b0d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1021,7 +1021,7 @@ static int wl18xx_hw_init(struct wl1271 *wl) /* (re)init private structures. Relevant on recovery as well. */ priv->last_fw_rls_idx = 0; - priv->extra_spare_vif_count = 0; + priv->extra_spare_key_count = 0; /* set the default amount of spare blocks in the bitmap */ ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE); @@ -1285,8 +1285,8 @@ static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem) { struct wl18xx_priv *priv = wl->priv; - /* If we have VIFs requiring extra spare, indulge them */ - if (priv->extra_spare_vif_count) + /* If we have keys requiring extra spare, indulge them */ + if (priv->extra_spare_key_count) return WL18XX_TX_HW_EXTRA_BLOCK_SPARE; return WL18XX_TX_HW_BLOCK_SPARE; @@ -1298,42 +1298,48 @@ static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_key_conf *key_conf) { struct wl18xx_priv *priv = wl->priv; - bool change_spare = false; + bool change_spare = false, special_enc; int ret; - /* - * when adding the first or removing the last GEM/TKIP interface, - * we have to adjust the number of spare blocks. - */ - change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM || - key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) && - ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) || - (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY)); + wl1271_debug(DEBUG_CRYPT, "extra spare keys before: %d", + priv->extra_spare_key_count); - /* no need to change spare - just regular set_key */ - if (!change_spare) - return wlcore_set_key(wl, cmd, vif, sta, key_conf); + special_enc = key_conf->cipher == WL1271_CIPHER_SUITE_GEM || + key_conf->cipher == WLAN_CIPHER_SUITE_TKIP; ret = wlcore_set_key(wl, cmd, vif, sta, key_conf); if (ret < 0) goto out; + /* + * when adding the first or removing the last GEM/TKIP key, + * we have to adjust the number of spare blocks. + */ + if (special_enc) { + if (cmd == SET_KEY) { + /* first key */ + change_spare = (priv->extra_spare_key_count == 0); + priv->extra_spare_key_count++; + } else if (cmd == DISABLE_KEY) { + /* last key */ + change_spare = (priv->extra_spare_key_count == 1); + priv->extra_spare_key_count--; + } + } + + wl1271_debug(DEBUG_CRYPT, "extra spare keys after: %d", + priv->extra_spare_key_count); + + if (!change_spare) + goto out; + /* key is now set, change the spare blocks */ - if (cmd == SET_KEY) { + if (priv->extra_spare_key_count) ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_EXTRA_BLOCK_SPARE); - if (ret < 0) - goto out; - - priv->extra_spare_vif_count++; - } else { + else ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE); - if (ret < 0) - goto out; - - priv->extra_spare_vif_count--; - } out: return ret; diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index e6ea78c7da36..b6739e79efcf 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -49,8 +49,8 @@ struct wl18xx_priv { /* Index of last released Tx desc in FW */ u8 last_fw_rls_idx; - /* number of VIFs requiring extra spare mem-blocks */ - int extra_spare_vif_count; + /* number of keys requiring extra spare mem-blocks */ + int extra_spare_key_count; }; #define WL18XX_FW_MAX_TX_STATUS_DESC 33 From 2e07d02828759a506b5205b2cff40daa58df5bf7 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Wed, 28 Nov 2012 11:42:49 +0200 Subject: [PATCH 085/478] wlcore: Always pass DMA-able buffers to mmc functions Some of the mmc drivers initiate DMA transfers with buffers passed from higher layers. This means that the driver shouldn't ever pass non DMA-able buffers, such as ones that are unaligned, allocated on the stack or static. Fix a couple of calls to the mmc layer in which buffers which weren't necessarily DMA-able were passed. [Use sizeof(*wl->buffer_32) instead of sizeof(u32) -- Luca] Signed-off-by: Ido Yariv Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 31 ++++++++++++++++++++----- drivers/net/wireless/ti/wl12xx/wl12xx.h | 2 ++ drivers/net/wireless/ti/wlcore/io.h | 12 +++++----- drivers/net/wireless/ti/wlcore/main.c | 10 ++++++++ drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 6d4a02171abc..5b023a5bcc09 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -613,7 +613,7 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) if (wl->chip.id != CHIP_ID_128X_PG20) { struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; - struct wl127x_rx_mem_pool_addr rx_mem_addr; + struct wl12xx_priv *priv = wl->priv; /* * Choose the block we want to read @@ -622,13 +622,13 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) */ u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK; - rx_mem_addr.addr = (mem_block << 8) + + priv->rx_mem_addr->addr = (mem_block << 8) + le32_to_cpu(wl_mem_map->packet_memory_pool_start); - rx_mem_addr.addr_extra = rx_mem_addr.addr + 4; + priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4; - ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr, - sizeof(rx_mem_addr), false); + ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr, + sizeof(*priv->rx_mem_addr), false); if (ret < 0) return ret; } @@ -1761,6 +1761,10 @@ static int wl12xx_setup(struct wl1271 *wl) wl1271_error("Invalid tcxo parameter %s", tcxo_param); } + priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL); + if (!priv->rx_mem_addr) + return -ENOMEM; + return 0; } @@ -1794,6 +1798,21 @@ out: return ret; } +static int __devexit wl12xx_remove(struct platform_device *pdev) +{ + struct wl1271 *wl = platform_get_drvdata(pdev); + struct wl12xx_priv *priv; + + if (!wl) + goto out; + priv = wl->priv; + + kfree(priv->rx_mem_addr); + +out: + return wlcore_remove(pdev); +} + static const struct platform_device_id wl12xx_id_table[] __devinitconst = { { "wl12xx", 0 }, { } /* Terminating Entry */ @@ -1802,7 +1821,7 @@ MODULE_DEVICE_TABLE(platform, wl12xx_id_table); static struct platform_driver wl12xx_driver = { .probe = wl12xx_probe, - .remove = __devexit_p(wlcore_remove), + .remove = __devexit_p(wl12xx_remove), .id_table = wl12xx_id_table, .driver = { .name = "wl12xx_driver", diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index a07be5e022fb..d4552857480c 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -73,6 +73,8 @@ struct wl12xx_priv { int ref_clock; int tcxo_clock; + + struct wl127x_rx_mem_pool_addr *rx_mem_addr; }; #endif /* __WL12XX_PRIV_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index f48530fec14f..af7d9f9b3b4d 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -105,13 +105,13 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr, { int ret; - ret = wlcore_raw_read(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + ret = wlcore_raw_read(wl, addr, wl->buffer_32, + sizeof(*wl->buffer_32), false); if (ret < 0) return ret; if (val) - *val = le32_to_cpu(wl->buffer_32); + *val = le32_to_cpu(*wl->buffer_32); return 0; } @@ -119,9 +119,9 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr, static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr, u32 val) { - wl->buffer_32 = cpu_to_le32(val); - return wlcore_raw_write(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + *wl->buffer_32 = cpu_to_le32(val); + return wlcore_raw_write(wl, addr, wl->buffer_32, + sizeof(*wl->buffer_32), false); } static inline int __must_check wlcore_read(struct wl1271 *wl, int addr, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 227c571a2690..0e599c92a071 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5860,8 +5860,17 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, goto err_fwlog; } + wl->buffer_32 = kmalloc(sizeof(*wl->buffer_32), GFP_KERNEL); + if (!wl->buffer_32) { + ret = -ENOMEM; + goto err_mbox; + } + return hw; +err_mbox: + kfree(wl->mbox); + err_fwlog: free_page((unsigned long)wl->fwlog); @@ -5900,6 +5909,7 @@ int wlcore_free_hw(struct wl1271 *wl) device_remove_file(wl->dev, &dev_attr_hw_pg_ver); device_remove_file(wl->dev, &dev_attr_bt_coex_state); + kfree(wl->buffer_32); kfree(wl->mbox); free_page((unsigned long)wl->fwlog); dev_kfree_skb(wl->dummy_packet); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 6c2fd0398e9d..118208a8e997 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -334,7 +334,7 @@ struct wl1271 { struct wl1271_stats stats; - __le32 buffer_32; + __le32 *buffer_32; u32 buffer_cmd; u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; From a73331141354e800e735d0251307dd10e04ac21a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 16 Dec 2012 16:35:35 +0100 Subject: [PATCH 086/478] bcma: BCMA_DRIVER_GPIO should depend on GPIOLIB instead of selecting it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit cf0936b06d8e98a157630e99f647e2ff6d29d7ad ("bcma: add GPIO driver") added BCMA_DRIVER_GPIO, which unconditionally selects GPIOLIB, causing a Kconfig warning: warning: (ARCH_REQUIRE_GPIOLIB && SSB_DRIVER_GPIO && BCMA_DRIVER_GPIO && MFD_TC6393XB && FB_VIA) selects GPIOLIB which has unmet direct dependencies (ARCH_WANT_OPTIONAL_GPIOLIB || ARCH_REQUIRE_GPIOLIB) and build failure for m68k/allmodconfig: In file included from include/linux/bcma/bcma.h:8, from drivers/bcma/bcma_private.h:9, from drivers/bcma/main.c:9: include/linux/bcma/bcma_driver_chipcommon.h:582: error: field ‘gpio’ has incomplete type In file included from include/linux/bcma/bcma.h:12, from drivers/bcma/bcma_private.h:9, from drivers/bcma/main.c:9: include/linux/ssb/ssb.h:440: error: field ‘gpio’ has incomplete type make[4]: *** [drivers/bcma/main.o] Error 1 make[3]: *** [drivers/bcma/] Error 2 Turn the select into a dependency to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: John W. Linville --- drivers/bcma/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index d7b56a88c9f4..8b4221cfd118 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -67,8 +67,7 @@ config BCMA_DRIVER_GMAC_CMN config BCMA_DRIVER_GPIO bool "BCMA GPIO driver" - depends on BCMA - select GPIOLIB + depends on BCMA && GPIOLIB help Driver to provide access to the GPIO pins of the bcma bus. From 3194f2f04547c3e0da488f0478788fbeebd9a914 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 16 Dec 2012 16:35:36 +0100 Subject: [PATCH 087/478] ssb: SSB_DRIVER_GPIO should depend on GPIOLIB instead of selecting it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit ec43b08b5733494ad88aa618ecdf534320dd8207 ("ssb: add GPIO driver") added SSB_DRIVER_GPIO, which unconditionally selects GPIOLIB, causing a Kconfig warning: warning: (ARCH_REQUIRE_GPIOLIB && SSB_DRIVER_GPIO && BCMA_DRIVER_GPIO && MFD_TC6393XB && FB_VIA) selects GPIOLIB which has unmet direct dependencies (ARCH_WANT_OPTIONAL_GPIOLIB || ARCH_REQUIRE_GPIOLIB) and build failure for m68k/allmodconfig: In file included from drivers/ssb/ssb_private.h:5, from drivers/ssb/main.c:12: include/linux/ssb/ssb.h:440: error: field ‘gpio’ has incomplete type make[4]: *** [drivers/ssb/main.o] Error 1 make[3]: *** [drivers/ssb/] Error 2 Turn the select into a dependency to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: John W. Linville --- drivers/ssb/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index ff3c8a21f10d..5d6f2ec1c705 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -162,8 +162,7 @@ config SSB_DRIVER_GIGE config SSB_DRIVER_GPIO bool "SSB GPIO driver" - depends on SSB - select GPIOLIB + depends on SSB && GPIOLIB help Driver to provide access to the GPIO pins on the bus. From 12e9432707dad5a09d8c51857b275c62bb6f91c8 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Thu, 20 Dec 2012 08:38:00 +0530 Subject: [PATCH 088/478] ath9k: Fix compilation breakage Since ath9k makes use of mac80211's debugfs hooks to maintain station statistics, make ATH9K_DEBUGFS select MAC80211_DEBUGFS. This fixes the issue reported by Fengguang Wu: drivers/net/wireless/ath/ath9k/debug.c: In function 'ath9k_sta_add_debugfs': drivers/net/wireless/ath/ath9k/debug.c:1589:4: error: 'struct ath_node' has no member named 'node_stat' drivers/net/wireless/ath/ath9k/debug.c: In function 'ath9k_sta_remove_debugfs': drivers/net/wireless/ath/ath9k/debug.c:1599:19: error: 'struct ath_node' has no member named 'node_stat' Reported-by: Fengguang Wu Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 5fc15bf8be09..581913ee6ef1 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -56,7 +56,8 @@ config ATH9K_AHB config ATH9K_DEBUGFS bool "Atheros ath9k debugging" - depends on ATH9K && DEBUG_FS + depends on ATH9K + select MAC80211_DEBUGFS ---help--- Say Y, if you need access to ath9k's statistics for interrupts, rate control, etc. From 4c3de5920c486b8eefa6187ee6a181864c161100 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Thu, 20 Dec 2012 16:24:43 +0200 Subject: [PATCH 089/478] rtlwifi: fix incorrect use of usb_alloc_coherent with usb_control_msg Incorrect use of usb_alloc_coherent memory as input buffer to usb_control_msg can cause problems in arch DMA code, for example kernel BUG at 'arch/arm/include/asm/dma-mapping.h:321' on ARM (linux-3.4). Change _usb_writeN_sync use kmalloc'd buffer instead. Cc: stable@vger.kernel.org Signed-off-by: Jussi Kivilinna Acked-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/usb.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index 29f0969e4ba0..a3312b7c4907 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -210,17 +210,16 @@ static void _usb_writeN_sync(struct rtl_priv *rtlpriv, u32 addr, void *data, u16 index = REALTEK_USB_VENQT_CMD_IDX; int pipe = usb_sndctrlpipe(udev, 0); /* write_out */ u8 *buffer; - dma_addr_t dma_addr; - wvalue = (u16)(addr&0x0000ffff); - buffer = usb_alloc_coherent(udev, (size_t)len, GFP_ATOMIC, &dma_addr); + wvalue = (u16)(addr & 0x0000ffff); + buffer = kmalloc(len, GFP_ATOMIC); if (!buffer) return; memcpy(buffer, data, len); usb_control_msg(udev, pipe, request, reqtype, wvalue, index, buffer, len, 50); - usb_free_coherent(udev, (size_t)len, buffer, dma_addr); + kfree(buffer); } static void _rtl_usb_io_handler_init(struct device *dev, From 6c653f66772c39c5e25db715bbd4730596fccd9e Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 22 Dec 2012 04:35:24 +0100 Subject: [PATCH 090/478] carl9170: fix -EINVAL bailout during init with !CONFIG_MAC80211_MESH Sean reported that as of 3.7, his AR9170 device no longer works because the driver fails during initialization. He noted this is due to: "In carl9170/fw.c, ar->hw->wiphy is tagged with NL80211_IFTYPE_MESH_POINT support if the firmware has Content after Beacon Queuing. This is both in interface_modes and the only iface_combinations entry. If CONFIG_MAC80211_MESH is not set, ieee80211_register_hw removes NL80211_IFTYPE_MESH_POINT from interface_modes, but not iface_combinations. wiphy_register then checks to see if every interface type in every interface combination is in interface_modes. NL80211_IFTYPE_MESH_POINT was removed, so you get a WARN_ON warning and it returns -EINVAL, giving up." Unfortunately, the iface_combination (types) feature bitmap in ieee80211_iface_limit is part of a const member in the ieee80211_iface_combination struct. Hence, the MESH_POINT feature flag can't be masked by wiphy_register in the same way as interface_modes in ieee80211_register_hw. Cc: Reported-by: Sean Patrick Santos Signed-off-by: Christian Lamparter Tested-by: Sean Patrick Santos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/fw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index aaebecd19e59..63fd9af3fd39 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -336,8 +336,12 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) if (SUPP(CARL9170FW_WLANTX_CAB)) { if_comb_types |= BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_P2P_GO); + +#ifdef CONFIG_MAC80211_MESH + if_comb_types |= + BIT(NL80211_IFTYPE_MESH_POINT); +#endif /* CONFIG_MAC80211_MESH */ } } From 3194b7fcdf6caea338b5d2c72d76fed80437649c Mon Sep 17 00:00:00 2001 From: Tomasz Guszkowski Date: Sat, 22 Dec 2012 18:30:01 +0100 Subject: [PATCH 091/478] p54usb: add USB ID for T-Com Sinus 154 data II Added USB ID for T-Com Sinus 154 data II. Cc: Signed-off-by: Tomasz Guszkowski Acked-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index e71c702e2eb1..64b944b49faa 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -82,6 +82,7 @@ static struct usb_device_id p54u_table[] = { {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */ {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ + {USB_DEVICE(0x083a, 0x4503)}, /* T-Com Sinus 154 data II */ {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */ {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */ From 7ffa592843c77b1f971a6d74d38d0000e6b18284 Mon Sep 17 00:00:00 2001 From: Niels Ole Salscheider Date: Mon, 24 Dec 2012 13:07:36 +0100 Subject: [PATCH 092/478] brcmsmac: Use udelay instead of usleep_range wlc_lcnphy_rx_iq_cal_gain is called during initialization, i. e. when executing brcms_up. But brcms_up is called from brcms_ops_start while the latter holds a spin lock. Thus, we cannot use usleep_range but have to use udelay. This fixes: BUG: scheduling while atomic: NetworkManager/1652/0x00000200 [...] Call Trace: [] __schedule_bug+0x48/0x54 [] __schedule+0x596/0x6d0 [] schedule+0x29/0x70 [] schedule_hrtimeout_range_clock+0xfc/0x140 [] ? update_rmtp+0x70/0x70 [] schedule_hrtimeout_range+0x13/0x20 [] usleep_range+0x40/0x50 [] wlc_lcnphy_rx_iq_cal.constprop.10+0x59b/0xa90 [brcmsmac] [] wlc_lcnphy_periodic_cal+0x20e/0x220 [brcmsmac] [] ? wlc_lcnphy_set_tx_pwr_ctrl+0x21d/0x3c0 [brcmsmac] [] wlc_phy_init_lcnphy+0xacc/0x1100 [brcmsmac] [] ? wlc_phy_txpower_recalc_target_lcnphy+0x90/0x90 [brcmsmac] [] wlc_phy_init+0xcd/0x170 [brcmsmac] [] brcms_b_bsinit.isra.65+0x12e/0x310 [brcmsmac] [] brcms_c_init+0x8fb/0x1170 [brcmsmac] [] brcms_init+0x5a/0x70 [brcmsmac] [] brcms_c_up+0x1ac/0x4a0 [brcmsmac] [] brcms_up+0x25/0x30 [brcmsmac] [] brcms_ops_start+0xd0/0x100 [brcmsmac] [...] Signed-off-by: Niels Ole Salscheider Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c index 606b534347bc..21a824232478 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c @@ -1343,13 +1343,13 @@ static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain, wlc_lcnphy_rx_gain_override_enable(pi, true); wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0); - usleep_range(500, 500); + udelay(500); write_radio_reg(pi, RADIO_2064_REG112, 0); if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l)) return false; wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0); - usleep_range(500, 500); + udelay(500); write_radio_reg(pi, RADIO_2064_REG112, 0); if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h)) return false; From 4dd6ff72c4ffde97f1663848a40f49fdf235c913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 25 Dec 2012 22:34:22 +0100 Subject: [PATCH 093/478] bcma: correct M25P32 serial flash ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Acked-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_sflash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bcma/driver_chipcommon_sflash.c b/drivers/bcma/driver_chipcommon_sflash.c index 63e688393825..1e694db4532d 100644 --- a/drivers/bcma/driver_chipcommon_sflash.c +++ b/drivers/bcma/driver_chipcommon_sflash.c @@ -35,7 +35,7 @@ static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = { { "M25P40", 0x12, 0x10000, 8, }, { "M25P16", 0x14, 0x10000, 32, }, - { "M25P32", 0x14, 0x10000, 64, }, + { "M25P32", 0x15, 0x10000, 64, }, { "M25P64", 0x16, 0x10000, 128, }, { "M25FL128", 0x17, 0x10000, 256, }, { 0 }, From a796a1dd5da9645ad77aa687d1a890ecd63ab5a6 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 26 Dec 2012 12:27:39 +0530 Subject: [PATCH 094/478] ath9k_hw: Fix RX gain initvals for AR9485 Populate iniModesRxGain with the correct initvals array for AR9485 v1.1 Cc: stable@vger.kernel.org Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 74fd3977feeb..59bf5f31e212 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -544,7 +544,7 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) ar9340Common_rx_gain_table_1p0); else if (AR_SREV_9485_11(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9485Common_wo_xlna_rx_gain_1_1); + ar9485_common_rx_gain_1_1); else if (AR_SREV_9550(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, ar955x_1p0_common_rx_gain_table); From 1474a89838c44ef2c9b95401cf3a9ff5ad05d5fd Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Wed, 26 Dec 2012 21:51:12 +0100 Subject: [PATCH 095/478] rtlwifi: Don't leak on error in _rtl_usb_receive() We fail to release 'urb' if '_rtl_prep_rx_urb()' fails in _rtl_usb_receive(). This patch should take care of the leak. Signed-off-by: Jesper Juhl Acked-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index a3312b7c4907..f2ecdeb3a90d 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -639,6 +639,7 @@ static int _rtl_usb_receive(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG, "Failed to prep_rx_urb!!\n"); err = PTR_ERR(skb); + usb_free_urb(urb); goto err_out; } From 4010fe21a315b4223c25376714c6a2b61b722e5c Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 27 Dec 2012 15:18:20 +0100 Subject: [PATCH 096/478] p54usb: add USBIDs for two more p54usb devices This patch adds USBIDs for: - DrayTek Vigor 530 - Zoom 4410a It also adds a note about Gemtek WUBI-100GW and SparkLAN WL-682 USBID conflict [WUBI-100GW is a ISL3886+NET2280 (LM86 firmare) solution, whereas WL-682 is a ISL3887 (LM87 firmware)] device. Source: Cc: Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 64b944b49faa..800a16526c8e 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -47,6 +47,7 @@ static struct usb_device_id p54u_table[] = { {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */ {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */ {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ + {USB_DEVICE(0x0675, 0x0530)}, /* DrayTek Vigor 530 */ {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */ {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */ @@ -82,6 +83,7 @@ static struct usb_device_id p54u_table[] = { {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */ {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ + {USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */ {USB_DEVICE(0x083a, 0x4503)}, /* T-Com Sinus 154 data II */ {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */ @@ -102,6 +104,7 @@ static struct usb_device_id p54u_table[] = { {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */ {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */ {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ + /* {USB_DEVICE(0x15a9, 0x0002)}, * Also SparkLAN WL-682 with 3887 */ {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */ {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */ {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ From 86bfec8d5183be4601db3cc4ffab58fceec33838 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 27 Dec 2012 10:37:28 -0600 Subject: [PATCH 097/478] rtlwifi: Fix warning for unchecked pci_map_single() call Kernel 3.8 implements checking of all DMA mapping calls and issues a WARNING for the first it finds that is not checked. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/pci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 3deacafdcd5e..4261e8ecc4c3 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -743,6 +743,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) done: bufferaddress = (*((dma_addr_t *)skb->cb)); + if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress)) + return; tmp_one = 1; rtlpriv->cfg->ops->set_desc((u8 *) pdesc, false, HW_DESC_RXBUFF_ADDR, @@ -1115,6 +1117,10 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw) PCI_DMA_FROMDEVICE); bufferaddress = (*((dma_addr_t *)skb->cb)); + if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress)) { + dev_kfree_skb_any(skb); + return 1; + } rtlpriv->cfg->ops->set_desc((u8 *)entry, false, HW_DESC_RXBUFF_ADDR, (u8 *)&bufferaddress); From 9145910161ebe227fcdacbc771fadfa55a364763 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 27 Dec 2012 10:37:29 -0600 Subject: [PATCH 098/478] rtlwifi: rtl8192ce: Fix warning for unchecked pci_map_single() call Kernel 3.8 implements checking of all DMA mapping calls and issues a WARNING for the first it finds that is not checked. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192ce/trx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 173424756149..c31795e379f7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -611,8 +611,14 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, dma_addr_t mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + u8 bw_40 = 0; + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } rcu_read_lock(); sta = get_sta(hw, mac->vif, mac->bssid); if (mac->opmode == NL80211_IFTYPE_STATION) { @@ -774,6 +780,11 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); __le16 fc = hdr->frame_control; + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); if (firstseg) From 708b70ff31e0577bff3c48eefa19122f6e346252 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 27 Dec 2012 10:37:30 -0600 Subject: [PATCH 099/478] rtlwifi: rtl8192de: Fix warning for unchecked pci_map_single() call Kernel 3.8 implements checking of all DMA mapping calls and issues a WARNING for the first it finds that is not checked. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192de/trx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index f9f3861046c1..a0fbf284420e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -587,6 +587,11 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, buf_len = skb->len; mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_92d)); if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) { firstseg = true; @@ -740,6 +745,11 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); __le16 fc = hdr->frame_control; + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); if (firstseg) SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); From f6aa93ad039910ea23d65add0a19ce0c896ea9b3 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 27 Dec 2012 10:37:31 -0600 Subject: [PATCH 100/478] rtlwifi: rtl8192se: Fix warning for unchecked pci_map_single() call Kernel 3.8 implements checking of all DMA mapping calls and issues a WARNING for the first it finds that is not checked. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192se/trx.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 0e9f6ebf078a..206561d7282f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -611,6 +611,11 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, PCI_DMA_TODEVICE); u8 bw_40 = 0; + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } if (mac->opmode == NL80211_IFTYPE_STATION) { bw_40 = mac->bw_40; } else if (mac->opmode == NL80211_IFTYPE_AP || @@ -763,6 +768,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb) { + struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct rtl_tcb_desc *tcb_desc = (struct rtl_tcb_desc *)(skb->cb); @@ -770,7 +776,12 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, dma_addr_t mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - /* Clear all status */ + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } + /* Clear all status */ CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_CMDDESC_SIZE_RTL8192S); /* This bit indicate this packet is used for FW download. */ From 3f009c78bb7284eb727dae6e7f74ae493add7b89 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 27 Dec 2012 10:37:32 -0600 Subject: [PATCH 101/478] rtlwifi: rtl8723ae: Fix warning for unchecked pci_map_single() call Kernel 3.8 implements checking of all DMA mapping calls and issues a WARNING for the first it finds that is not checked. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8723ae/trx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index 87331d826d73..a313be8c21d2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -387,6 +387,11 @@ void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw, PCI_DMA_TODEVICE); u8 bw_40 = 0; + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } if (mac->opmode == NL80211_IFTYPE_STATION) { bw_40 = mac->bw_40; } else if (mac->opmode == NL80211_IFTYPE_AP || @@ -542,6 +547,11 @@ void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw, PCI_DMA_TODEVICE); __le16 fc = hdr->frame_control; + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); if (firstseg) From 1b2c2e73b3a7269cff92ac0dab213ab18ac8002d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:12:38 +0100 Subject: [PATCH 102/478] brcmsmac: add copyright information for Canonical Patches from Canonical involved the introduction of new source files debug.[ch]. That coincided with other patches from Broadcom introducing the same files. Cc: Seth Forshee Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Piotr Haber Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/debug.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.h b/drivers/net/wireless/brcm80211/brcmsmac/debug.h index 796836b0f469..822781cf15d4 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/debug.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2012 Broadcom Corporation + * Copyright (c) 2012 Canonical Ltd. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above From 619c5a9ad54e6bbdafd16d1cdc6c049403710540 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 2 Jan 2013 15:12:39 +0100 Subject: [PATCH 103/478] brcmfmac: fix parsing rsn ie for ap mode. RSN IEs got incorrectly parsed and therefore ap mode using WPA2 security was not working. Cc: stable@vger.kernel.org Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 1261a9b84e04..75464ad4fbd1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3091,10 +3091,11 @@ brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie, len = wpa_ie->len + TLV_HDR_LEN; data = (u8 *)wpa_ie; - offset = 0; + offset = TLV_HDR_LEN; if (!is_rsn_ie) offset += VS_IE_FIXED_HDR_LEN; - offset += WPA_IE_VERSION_LEN; + else + offset += WPA_IE_VERSION_LEN; /* check for multicast cipher suite */ if (offset + WPA_IE_MIN_OUI_LEN > len) { From 34bcf71502413f8903ade93746f2d0f04b937a78 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 11 Dec 2012 10:48:23 +0100 Subject: [PATCH 104/478] mac80211: fix ibss scanning Do not scan on no-IBSS and disabled channels in IBSS mode. Doing this can trigger Microcode errors on iwlwifi and iwlegacy drivers. Also rename ieee80211_request_internal_scan() function since it is only used in IBSS mode and simplify calling it from ieee80211_sta_find_ibss(). This patch should address: https://bugzilla.redhat.com/show_bug.cgi?id=883414 https://bugzilla.kernel.org/show_bug.cgi?id=49411 Reported-by: Jesse Kahtava Reported-by: Mikko Rapeli Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Gruszka Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 9 ++++----- net/mac80211/ieee80211_i.h | 6 +++--- net/mac80211/scan.c | 34 ++++++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 8881fc77fb13..6b7644e818d8 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -703,8 +703,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) sdata_info(sdata, "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n"); - ieee80211_request_internal_scan(sdata, - ifibss->ssid, ifibss->ssid_len, NULL); + ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, + NULL); } static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) @@ -802,9 +802,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) IEEE80211_SCAN_INTERVAL)) { sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); - ieee80211_request_internal_scan(sdata, - ifibss->ssid, ifibss->ssid_len, - ifibss->fixed_channel ? ifibss->channel : NULL); + ieee80211_request_ibss_scan(sdata, ifibss->ssid, + ifibss->ssid_len, chan); } else { int interval = IEEE80211_SCAN_INTERVAL; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 42d0d0267730..112bb6192399 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1329,9 +1329,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, /* scan/BSS handling */ void ieee80211_scan_work(struct work_struct *work); -int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan); +int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, + const u8 *ssid, u8 ssid_len, + struct ieee80211_channel *chan); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); void ieee80211_scan_cancel(struct ieee80211_local *local); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 8ed83dcc149f..de3532d18df1 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -832,9 +832,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, return res; } -int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, - const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan) +int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, + const u8 *ssid, u8 ssid_len, + struct ieee80211_channel *chan) { struct ieee80211_local *local = sdata->local; int ret = -EBUSY; @@ -848,22 +848,36 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, /* fill internal scan request */ if (!chan) { - int i, nchan = 0; + int i, max_n; + int n_ch = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!local->hw.wiphy->bands[band]) continue; - for (i = 0; - i < local->hw.wiphy->bands[band]->n_channels; - i++) { - local->int_scan_req->channels[nchan] = + + max_n = local->hw.wiphy->bands[band]->n_channels; + for (i = 0; i < max_n; i++) { + struct ieee80211_channel *tmp_ch = &local->hw.wiphy->bands[band]->channels[i]; - nchan++; + + if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_DISABLED)) + continue; + + local->int_scan_req->channels[n_ch] = tmp_ch; + n_ch++; } } - local->int_scan_req->n_channels = nchan; + if (WARN_ON_ONCE(n_ch == 0)) + goto unlock; + + local->int_scan_req->n_channels = n_ch; } else { + if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_DISABLED))) + goto unlock; + local->int_scan_req->channels[0] = chan; local->int_scan_req->n_channels = 1; } From 2d4072a5470621c552f46d51cd54965aeb033893 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 10 Dec 2012 20:02:34 +0100 Subject: [PATCH 105/478] mac80211: flush AP_VLAN stations when tearing down the BSS AP Signed-off-by: Felix Fietkau [change to flush stations with AP flush in second loop] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5c61677487cf..47e0aca614b7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1009,6 +1009,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) if (old_probe_resp) kfree_rcu(old_probe_resp, rcu_head); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + sta_info_flush(local, vlan); sta_info_flush(local, sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); From 4d76d21bd700fcf72a030ad75c71c816707039b8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Dec 2012 20:38:41 +0100 Subject: [PATCH 106/478] mac80211: assign VLAN channel contexts Make AP_VLAN type interfaces track the AP master channel context so they have one assigned for the various lookups. Don't give them their own refcount etc. since they're just slaves to the AP master. Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 38 ++++++++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/iface.c | 9 ++++++--- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 53f03120db55..80e55527504b 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -4,6 +4,7 @@ #include #include +#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -197,6 +198,15 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) ctx = container_of(conf, struct ieee80211_chanctx, conf); + if (sdata->vif.type == NL80211_IFTYPE_AP) { + struct ieee80211_sub_if_data *vlan; + + /* for the VLAN list */ + ASSERT_RTNL(); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + rcu_assign_pointer(vlan->vif.chanctx_conf, NULL); + } + ieee80211_unassign_vif_chanctx(sdata, ctx); if (ctx->refcount == 0) ieee80211_free_chanctx(local, ctx); @@ -316,6 +326,15 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, goto out; } + if (sdata->vif.type == NL80211_IFTYPE_AP) { + struct ieee80211_sub_if_data *vlan; + + /* for the VLAN list */ + ASSERT_RTNL(); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + rcu_assign_pointer(vlan->vif.chanctx_conf, &ctx->conf); + } + ieee80211_recalc_smps_chanctx(local, ctx); out: mutex_unlock(&local->chanctx_mtx); @@ -331,6 +350,25 @@ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) mutex_unlock(&sdata->local->chanctx_mtx); } +void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *ap; + struct ieee80211_chanctx_conf *conf; + + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) + return; + + ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); + + mutex_lock(&local->chanctx_mtx); + + conf = rcu_dereference_protected(ap->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + rcu_assign_pointer(sdata->vif.chanctx_conf, conf); + mutex_unlock(&local->chanctx_mtx); +} + void ieee80211_iter_chan_contexts_atomic( struct ieee80211_hw *hw, void (*iter)(struct ieee80211_hw *hw, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 112bb6192399..28fc9875537c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1628,6 +1628,7 @@ ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, const struct cfg80211_chan_def *chandef, enum ieee80211_chanctx_mode mode); void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); +void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 09a80b55cf5a..54fb7f9db564 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -586,11 +586,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: - /* no need to tell driver, but set carrier */ - if (rtnl_dereference(sdata->bss->beacon)) + /* no need to tell driver, but set carrier and chanctx */ + if (rtnl_dereference(sdata->bss->beacon)) { + ieee80211_vif_vlan_copy_chanctx(sdata); netif_carrier_on(dev); - else + } else { netif_carrier_off(dev); + } break; case NL80211_IFTYPE_MONITOR: if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { @@ -839,6 +841,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: list_del(&sdata->u.vlan.list); + rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); /* no need to tell driver */ break; case NL80211_IFTYPE_MONITOR: From b7cfcd113ac2a1e6b02afc7d283295729fc178a9 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 17 Dec 2012 18:41:57 -0800 Subject: [PATCH 107/478] mac80211: RMC buckets are just list heads The array of rmc_entrys is redundant since only the list_head is used. Make this an array of list_heads instead and save ~6k per vif at runtime :D Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 8 ++++---- net/mac80211/mesh.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 1bf03f9ff3ba..649ad513547f 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -163,7 +163,7 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) return -ENOMEM; sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1; for (i = 0; i < RMC_BUCKETS; i++) - INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list); + INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]); return 0; } @@ -177,7 +177,7 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata) return; for (i = 0; i < RMC_BUCKETS; i++) - list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) { + list_for_each_entry_safe(p, n, &rmc->bucket[i], list) { list_del(&p->list); kmem_cache_free(rm_cache, p); } @@ -210,7 +210,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, /* Don't care about endianness since only match matters */ memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum)); idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask; - list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) { + list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) { ++entries; if (time_after(jiffies, p->exp_time) || (entries == RMC_QUEUE_MAX_LEN)) { @@ -229,7 +229,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, p->seqnum = seqnum; p->exp_time = jiffies + RMC_TIMEOUT; memcpy(p->sa, sa, ETH_ALEN); - list_add(&p->list, &rmc->bucket[idx].list); + list_add(&p->list, &rmc->bucket[idx]); return 0; } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 7c9215fb2ac8..84c28c6101cd 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -184,7 +184,7 @@ struct rmc_entry { }; struct mesh_rmc { - struct rmc_entry bucket[RMC_BUCKETS]; + struct list_head bucket[RMC_BUCKETS]; u32 idx_mask; }; From 97f97b1f5fe0878b35c8e314f98591771696321b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 22:54:58 +0100 Subject: [PATCH 108/478] mac80211: fix station destruction in AP/mesh modes Unfortunately, commit b22cfcfcae5b, intended to speed up roaming by avoiding the synchronize_rcu() broke AP/mesh modes as it moved some code into that work item that will still call into the driver at a time where it's no longer expected to handle this: after the AP or mesh has been stopped. To fix this problem remove the per-station work struct, maintain a station cleanup list instead and flush this list when stations are flushed. To keep this patch smaller for stable, do this when the stations are flushed (sta_info_flush()). This unfortunately brings back the original roaming delay; I'll fix that again in a separate patch. Also, Ben reported that the original commit could sometimes (with many interfaces) cause long delays when an interface is set down, due to blocking on flush_workqueue(). Since we now maintain the cleanup list, this particular change of the original patch can be reverted. Cc: stable@vger.kernel.org [3.7] Reported-by: Ben Greear Tested-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 4 ++++ net/mac80211/iface.c | 28 +++++++++++++----------- net/mac80211/sta_info.c | 44 ++++++++++++++++++++++++++++++++++---- net/mac80211/sta_info.h | 3 ++- 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 28fc9875537c..2fcd274877d7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -773,6 +773,10 @@ struct ieee80211_sub_if_data { u32 mntr_flags; } u; + spinlock_t cleanup_stations_lock; + struct list_head cleanup_stations; + struct work_struct cleanup_stations_wk; + #ifdef CONFIG_MAC80211_DEBUGFS struct { struct dentry *dir; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 54fb7f9db564..0f2a9f987f79 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -868,20 +868,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->work); /* * When we get here, the interface is marked down. - * Call rcu_barrier() to wait both for the RX path + * Call synchronize_rcu() to wait for the RX path * should it be using the interface and enqueuing - * frames at this very time on another CPU, and - * for the sta free call_rcu callbacks. + * frames at this very time on another CPU. */ - rcu_barrier(); - - /* - * free_sta_rcu() enqueues a work for the actual - * sta cleanup, so we need to flush it while - * sdata is still valid. - */ - flush_workqueue(local->workqueue); - + synchronize_rcu(); skb_queue_purge(&sdata->skb_queue); /* @@ -1501,6 +1492,15 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, mutex_unlock(&local->iflist_mtx); } +static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk) +{ + struct ieee80211_sub_if_data *sdata; + + sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk); + + ieee80211_cleanup_sdata_stas(sdata); +} + int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params) @@ -1576,6 +1576,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, INIT_LIST_HEAD(&sdata->key_list); + spin_lock_init(&sdata->cleanup_stations_lock); + INIT_LIST_HEAD(&sdata->cleanup_stations); + INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk); + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[i]; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f3e502502fee..8bbd3b0fdbcc 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -91,9 +91,8 @@ static int sta_info_hash_del(struct ieee80211_local *local, return -ENOENT; } -static void free_sta_work(struct work_struct *wk) +static void cleanup_single_sta(struct sta_info *sta) { - struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk); int ac, i; struct tid_ampdu_tx *tid_tx; struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -153,11 +152,35 @@ static void free_sta_work(struct work_struct *wk) sta_info_free(local, sta); } +void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata) +{ + struct sta_info *sta; + + spin_lock_bh(&sdata->cleanup_stations_lock); + while (!list_empty(&sdata->cleanup_stations)) { + sta = list_first_entry(&sdata->cleanup_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&sdata->cleanup_stations_lock); + + cleanup_single_sta(sta); + + spin_lock_bh(&sdata->cleanup_stations_lock); + } + + spin_unlock_bh(&sdata->cleanup_stations_lock); +} + static void free_sta_rcu(struct rcu_head *h) { struct sta_info *sta = container_of(h, struct sta_info, rcu_head); + struct ieee80211_sub_if_data *sdata = sta->sdata; - ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk); + spin_lock(&sdata->cleanup_stations_lock); + list_add_tail(&sta->list, &sdata->cleanup_stations); + spin_unlock(&sdata->cleanup_stations_lock); + + ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk); } /* protected by RCU */ @@ -310,7 +333,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, spin_lock_init(&sta->lock); INIT_WORK(&sta->drv_unblock_wk, sta_unblock); - INIT_WORK(&sta->free_sta_wk, free_sta_work); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); mutex_init(&sta->ampdu_mlme.mtx); @@ -891,6 +913,20 @@ int sta_info_flush(struct ieee80211_local *local, } mutex_unlock(&local->sta_mtx); + rcu_barrier(); + + if (sdata) { + ieee80211_cleanup_sdata_stas(sdata); + cancel_work_sync(&sdata->cleanup_stations_wk); + } else { + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + ieee80211_cleanup_sdata_stas(sdata); + cancel_work_sync(&sdata->cleanup_stations_wk); + } + mutex_unlock(&local->iflist_mtx); + } + return ret; } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 1489bca9ea97..37c1889afd3a 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -299,7 +299,6 @@ struct sta_info { spinlock_t lock; struct work_struct drv_unblock_wk; - struct work_struct free_sta_wk; u16 listen_interval; @@ -563,4 +562,6 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); +void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata); + #endif /* STA_INFO_H */ From a56f992cdabc63f56b4b142885deebebf936ff76 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 23:08:52 +0100 Subject: [PATCH 109/478] mac80211: use del_timer_sync for final sta cleanup timer deletion This is a very old bug, but there's nothing that prevents the timer from running while the module is being removed when we only do del_timer() instead of del_timer_sync(). The timer should normally not be running at this point, but it's not clearly impossible (or we could just remove this.) Cc: stable@vger.kernel.org Tested-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8bbd3b0fdbcc..ca9fde198188 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -884,7 +884,7 @@ void sta_info_init(struct ieee80211_local *local) void sta_info_stop(struct ieee80211_local *local) { - del_timer(&local->sta_cleanup); + del_timer_sync(&local->sta_cleanup); sta_info_flush(local, NULL); } From 826262c3d23743cb032a9e1a65a0f9be75091a5e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Dec 2012 16:38:14 +0200 Subject: [PATCH 110/478] mac80211: fix dtim_period in hidden SSID AP association When AP's SSID is hidden the BSS can appear several times in cfg80211's BSS list: once with a zero-length SSID that comes from the beacon, and once for each SSID from probe reponses. Since the mac80211 stores its data in ieee80211_bss which is embedded into cfg80211_bss, mac80211's data will be duplicated too. This becomes a problem when a driver needs the dtim_period since this data exists only in the beacon's instance in cfg80211 bss table which isn't the instance that is used when associating. Remove the DTIM period from the BSS table and track it explicitly to avoid this problem. Cc: stable@vger.kernel.org Tested-by: Efi Tubul Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 5 +-- net/mac80211/mlme.c | 75 +++++++++++++++++++++++++++----------- net/mac80211/scan.c | 12 ------ 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2fcd274877d7..8563b9a5cac3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -92,8 +92,6 @@ struct ieee80211_bss { u32 device_ts; - u8 dtim_period; - bool wmm_used; bool uapsd_supported; @@ -140,7 +138,6 @@ enum ieee80211_bss_corrupt_data_flags { /** * enum ieee80211_valid_data_flags - BSS valid data flags - * @IEEE80211_BSS_VALID_DTIM: DTIM data was gathered from non-corrupt IE * @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE * @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE * @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE @@ -151,7 +148,6 @@ enum ieee80211_bss_corrupt_data_flags { * beacon/probe response. */ enum ieee80211_bss_valid_data_flags { - IEEE80211_BSS_VALID_DTIM = BIT(0), IEEE80211_BSS_VALID_WMM = BIT(1), IEEE80211_BSS_VALID_RATES = BIT(2), IEEE80211_BSS_VALID_ERP = BIT(3) @@ -440,6 +436,7 @@ struct ieee80211_if_managed { unsigned long timers_running; /* used for quiesce/restart */ bool powersave; /* powersave requested for this iface */ bool broken_ap; /* AP is broken -- turn off powersave */ + u8 dtim_period; enum ieee80211_smps_mode req_smps, /* requested smps mode */ driver_smps_mode; /* smps mode request */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7753a9ca98a6..a3552929a21d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1074,12 +1074,8 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) if (beaconint_us > latency) { local->ps_sdata = NULL; } else { - struct ieee80211_bss *bss; int maxslp = 1; - u8 dtimper; - - bss = (void *)found->u.mgd.associated->priv; - dtimper = bss->dtim_period; + u8 dtimper = found->u.mgd.dtim_period; /* If the TIM IE is invalid, pretend the value is 1 */ if (!dtimper) @@ -1410,10 +1406,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_led_assoc(local, 1); - if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) - bss_conf->dtim_period = bss->dtim_period; - else + if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { + /* + * If the AP is buggy we may get here with no DTIM period + * known, so assume it's 1 which is the only safe assumption + * in that case, although if the TIM IE is broken powersave + * probably just won't work at all. + */ + bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1; + } else { bss_conf->dtim_period = 0; + } bss_conf->assoc = 1; @@ -1562,6 +1565,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.timers_running = 0; + sdata->vif.bss_conf.dtim_period = 0; + ifmgd->flags = 0; ieee80211_vif_release_channel(sdata); } @@ -2373,11 +2378,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel; bool need_ps = false; - if (sdata->u.mgd.associated && - ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) { - bss = (void *)sdata->u.mgd.associated->priv; + if ((sdata->u.mgd.associated && + ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) || + (sdata->u.mgd.assoc_data && + ether_addr_equal(mgmt->bssid, + sdata->u.mgd.assoc_data->bss->bssid))) { /* not previously set so we may need to recalc */ - need_ps = !bss->dtim_period; + need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period; + + if (elems->tim && !elems->parse_error) { + struct ieee80211_tim_ie *tim_ie = elems->tim; + sdata->u.mgd.dtim_period = tim_ie->dtim_period; + } } if (elems->ds_params && elems->ds_params_len == 1) @@ -3896,20 +3908,41 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, /* kick off associate process */ ifmgd->assoc_data = assoc_data; + ifmgd->dtim_period = 0; err = ieee80211_prep_connection(sdata, req->bss, true); if (err) goto err_clear; - if (!bss->dtim_period && - sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { - /* - * Wait up to one beacon interval ... - * should this be more if we miss one? - */ - sdata_info(sdata, "waiting for beacon from %pM\n", - ifmgd->bssid); - assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); + if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { + const struct cfg80211_bss_ies *beacon_ies; + + rcu_read_lock(); + beacon_ies = rcu_dereference(req->bss->beacon_ies); + if (!beacon_ies) { + /* + * Wait up to one beacon interval ... + * should this be more if we miss one? + */ + sdata_info(sdata, "waiting for beacon from %pM\n", + ifmgd->bssid); + assoc_data->timeout = + TU_TO_EXP_TIME(req->bss->beacon_interval); + } else { + const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, + beacon_ies->data, + beacon_ies->len); + if (tim_ie && tim_ie[1] >= + sizeof(struct ieee80211_tim_ie)) { + const struct ieee80211_tim_ie *tim; + tim = (void *)(tim_ie + 2); + ifmgd->dtim_period = tim->dtim_period; + } + assoc_data->have_beacon = true; + assoc_data->sent_assoc = false; + assoc_data->timeout = jiffies; + } + rcu_read_unlock(); } else { assoc_data->have_beacon = true; assoc_data->sent_assoc = false; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index de3532d18df1..d59fc6818b1c 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -113,18 +113,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss->valid_data |= IEEE80211_BSS_VALID_ERP; } - if (elems->tim && (!elems->parse_error || - !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) { - struct ieee80211_tim_ie *tim_ie = elems->tim; - bss->dtim_period = tim_ie->dtim_period; - if (!elems->parse_error) - bss->valid_data |= IEEE80211_BSS_VALID_DTIM; - } - - /* If the beacon had no TIM IE, or it was invalid, use 1 */ - if (beacon && !bss->dtim_period) - bss->dtim_period = 1; - /* replace old supported rates if we get new values */ if (!elems->parse_error || !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) { From 09b1426e7fcec510aba55a761da5004e47a11ce3 Mon Sep 17 00:00:00 2001 From: Chaitanya Date: Fri, 21 Dec 2012 17:15:17 +0530 Subject: [PATCH 111/478] mac80211: fix maximum MTU The maximum MTU shouldn't take the headers into account, the maximum MSDU size is exactly the maximum MTU. Signed-off-by: T Krishna Chaitanya Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 0f2a9f987f79..8be854e86cd9 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -207,17 +207,8 @@ void ieee80211_recalc_idle(struct ieee80211_local *local) static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) { - int meshhdrlen; - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - meshhdrlen = (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ? 5 : 0; - - /* FIX: what would be proper limits for MTU? - * This interface uses 802.3 frames. */ - if (new_mtu < 256 || - new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) { + if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) return -EINVAL; - } dev->mtu = new_mtu; return 0; From bc0784d951470ca6597239f1bae6e59caf49fb10 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 12 Dec 2012 16:56:20 -0800 Subject: [PATCH 112/478] mac80211: Allow disabling SGI-20 This allows user-space (wpa_supplicant) to disable short guard interval (SGI) for 20Mhz. The SGI-40 disable option is already handled. Signed-off-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 3 +++ net/mac80211/main.c | 1 + 2 files changed, 4 insertions(+) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index a71d891794a4..8a6d68f1f404 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -62,6 +62,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40); __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40); + /* Allow user to disable SGI-20 (SGI-40 is handled above) */ + __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20); + /* Allow user to disable the max-AMSDU bit. */ __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 1b087fff93e7..0c0d4cabd76c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -537,6 +537,7 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = { .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_MAX_AMSDU | + IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40), .mcs = { .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff, From f4eabc918c3b88763bc20dd9e2b248aa6c757005 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Thu, 13 Dec 2012 18:59:57 +0800 Subject: [PATCH 113/478] mac80211: use short slot time in mesh for 5GHz Use short slot time in 5GHz for mesh. The performance is increased from 16.4Mbps to 23.4Mbps for two directly connected mesh STAs operating in legacy rate using iperf measurement. Almost similar to the results claimed in IBSS mode. Signed-off-by: Chun-Yeow Yeoh [call ieee80211_get_sdata_band() only once] Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 649ad513547f..8565c00cb644 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -607,6 +607,12 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; + u32 changed = BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_HT | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_BEACON_INT; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); local->fif_other_bss++; /* mesh ifaces must set allmulti to forward mcast traffic */ @@ -626,13 +632,14 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.basic_rates = - ieee80211_mandatory_rates(sdata->local, - ieee80211_get_sdata_band(sdata)); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_ENABLED | - BSS_CHANGED_HT | - BSS_CHANGED_BASIC_RATES | - BSS_CHANGED_BEACON_INT); + ieee80211_mandatory_rates(local, band); + + if (band == IEEE80211_BAND_5GHZ) { + sdata->vif.bss_conf.use_short_slot = true; + changed |= BSS_CHANGED_ERP_SLOT; + } + + ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(sdata->dev); } From a357d7f9855e3002d6aaaea5c40dd1ac02b78de7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Dec 2012 11:57:42 +0100 Subject: [PATCH 114/478] mac80211_hwsim: allow testing paged RX Paged RX, i.e. SKBs with (some of) the data in pages instead of the SKB header data (skb->data) can behave differently in the stack and cause other bugs. To make debugging easier add an option to hwsim to test with such SKBs. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index ff9085502bea..145498f8411f 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -48,6 +48,10 @@ static int channels = 1; module_param(channels, int, 0444); MODULE_PARM_DESC(channels, "Number of concurrent channels"); +static bool paged_rx = false; +module_param(paged_rx, bool, 0644); +MODULE_PARM_DESC(paged_rx, "Use paged SKBs for RX instead of linear ones"); + /** * enum hwsim_regtest - the type of regulatory tests we offer * @@ -755,9 +759,25 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, * reserve some space for our vendor and the normal * radiotap header, since we're copying anyway */ - nskb = skb_copy_expand(skb, 64, 0, GFP_ATOMIC); - if (nskb == NULL) - continue; + if (skb->len < PAGE_SIZE && paged_rx) { + struct page *page = alloc_page(GFP_ATOMIC); + + if (!page) + continue; + + nskb = dev_alloc_skb(128); + if (!nskb) { + __free_page(page); + continue; + } + + memcpy(page_address(page), skb->data, skb->len); + skb_add_rx_frag(nskb, 0, page, 0, skb->len, skb->len); + } else { + nskb = skb_copy(skb, GFP_ATOMIC); + if (!nskb) + continue; + } if (mac80211_hwsim_addr_match(data2, hdr->addr1)) ack = true; From 0f6dfcee2e081f47a3e97cb8984fb4d62217e6f7 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Tue, 18 Dec 2012 09:55:33 +0200 Subject: [PATCH 115/478] wireless: more 'capability info' bits define bits for 'capability info', as in recent spec edition IEEE802.11-2012 Also, add mask for 2-bit field 'bss type', as it is in 802.11ad Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index f0859cc73861..09879eb24380 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1311,16 +1311,21 @@ struct ieee80211_vht_operation { #define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) #define WLAN_CAPABILITY_QOS (1<<9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) +#define WLAN_CAPABILITY_APSD (1<<11) +#define WLAN_CAPABILITY_RADIO_MEASURE (1<<12) #define WLAN_CAPABILITY_DSSS_OFDM (1<<13) +#define WLAN_CAPABILITY_DEL_BACK (1<<14) +#define WLAN_CAPABILITY_IMM_BACK (1<<15) /* DMG (60gHz) 802.11ad */ /* type - bits 0..1 */ +#define WLAN_CAPABILITY_DMG_TYPE_MASK (3<<0) #define WLAN_CAPABILITY_DMG_TYPE_IBSS (1<<0) /* Tx by: STA */ #define WLAN_CAPABILITY_DMG_TYPE_PBSS (2<<0) /* Tx by: PCP */ #define WLAN_CAPABILITY_DMG_TYPE_AP (3<<0) /* Tx by: AP */ #define WLAN_CAPABILITY_DMG_CBAP_ONLY (1<<2) -#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3) +#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3) #define WLAN_CAPABILITY_DMG_PRIVACY (1<<4) #define WLAN_CAPABILITY_DMG_ECPAC (1<<5) From b998e8bb3e1c6eeae5eab9d6a434563270286c3b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 23:07:46 +0100 Subject: [PATCH 116/478] mac80211: remove final sta_info_flush() When all interfaces have been removed, there can't be any stations left over, so there's no need to flush again. Remove this, and all code associated with it, which also simplifies the function. Tested-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 7 +++---- net/mac80211/ibss.c | 4 ++-- net/mac80211/iface.c | 5 ++--- net/mac80211/mesh.c | 2 +- net/mac80211/mlme.c | 2 +- net/mac80211/sta_info.c | 23 ++++++----------------- net/mac80211/sta_info.h | 3 +-- 7 files changed, 16 insertions(+), 30 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 47e0aca614b7..be019533b233 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1010,8 +1010,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) kfree_rcu(old_probe_resp, rcu_head); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) - sta_info_flush(local, vlan); - sta_info_flush(local, sdata); + sta_info_flush(vlan); + sta_info_flush(sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_stop_ap(sdata->local, sdata); @@ -1301,7 +1301,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac) { - struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1309,7 +1308,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, if (mac) return sta_info_destroy_addr_bss(sdata, mac); - sta_info_flush(local, sdata); + sta_info_flush(sdata); return 0; } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 6b7644e818d8..854de974dc9a 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -67,7 +67,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, skb_reserve(skb, sdata->local->hw.extra_tx_headroom); if (!ether_addr_equal(ifibss->bssid, bssid)) - sta_info_flush(sdata->local, sdata); + sta_info_flush(sdata); /* if merging, indicate to driver that we leave the old IBSS */ if (sdata->vif.bss_conf.ibss_joined) { @@ -1182,7 +1182,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) memset(ifibss->bssid, 0, ETH_ALEN); ifibss->ssid_len = 0; - sta_info_flush(sdata->local, sdata); + sta_info_flush(sdata); spin_lock_bh(&ifibss->incomplete_lock); while (!list_empty(&ifibss->incomplete_stations)) { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 8be854e86cd9..ecf7d15eaa5e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -776,7 +776,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * all other modes we've already removed all stations when * disconnecting etc. */ - sta_info_flush(local, sdata); + sta_info_flush(sdata); /* * Don't count this interface for promisc/allmulti while it @@ -961,7 +961,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev) */ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_local *local = sdata->local; int flushed; int i; @@ -977,7 +976,7 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_rmc_free(sdata); - flushed = sta_info_flush(local, sdata); + flushed = sta_info_flush(sdata); WARN_ON(flushed); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8565c00cb644..8a9d5d03bbc2 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -656,7 +656,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); /* flush STAs and mpaths on this iface */ - sta_info_flush(sdata->local, sdata); + sta_info_flush(sdata); mesh_path_flush_by_iface(sdata); del_timer_sync(&sdata->u.mesh.housekeeping_timer); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a3552929a21d..6d61d2f88716 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1521,7 +1521,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(ifmgd->bssid, 0, ETH_ALEN); /* remove AP and TDLS peers */ - sta_info_flush(local, sdata); + sta_info_flush(sdata); /* finally reset all BSS / config parameters */ changed |= ieee80211_reset_erp_info(sdata); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ca9fde198188..d743645a17b5 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -885,7 +885,6 @@ void sta_info_init(struct ieee80211_local *local) void sta_info_stop(struct ieee80211_local *local) { del_timer_sync(&local->sta_cleanup); - sta_info_flush(local, NULL); } /** @@ -893,12 +892,11 @@ void sta_info_stop(struct ieee80211_local *local) * * Returns the number of removed STA entries. * - * @local: local interface data - * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs + * @sdata: sdata to remove all stations from */ -int sta_info_flush(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) +int sta_info_flush(struct ieee80211_sub_if_data *sdata) { + struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; int ret = 0; @@ -906,7 +904,7 @@ int sta_info_flush(struct ieee80211_local *local, mutex_lock(&local->sta_mtx); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { - if (!sdata || sdata == sta->sdata) { + if (sdata == sta->sdata) { WARN_ON(__sta_info_destroy(sta)); ret++; } @@ -915,17 +913,8 @@ int sta_info_flush(struct ieee80211_local *local, rcu_barrier(); - if (sdata) { - ieee80211_cleanup_sdata_stas(sdata); - cancel_work_sync(&sdata->cleanup_stations_wk); - } else { - mutex_lock(&local->iflist_mtx); - list_for_each_entry(sdata, &local->interfaces, list) { - ieee80211_cleanup_sdata_stas(sdata); - cancel_work_sync(&sdata->cleanup_stations_wk); - } - mutex_unlock(&local->iflist_mtx); - } + ieee80211_cleanup_sdata_stas(sdata); + cancel_work_sync(&sdata->cleanup_stations_wk); return ret; } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 37c1889afd3a..13698dae6a88 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -548,8 +548,7 @@ void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); -int sta_info_flush(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata); +int sta_info_flush(struct ieee80211_sub_if_data *sdata); void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo); From 09f4114e02aac9cbf40553a17580b07ab29715d8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 23:26:57 +0100 Subject: [PATCH 117/478] mac80211: warn if unexpectedly removing stations When an interface is brought down it must have been disconnected (or similar) in all modes other than WDS, so warn if any stations were removed in other modes. Tested-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index ecf7d15eaa5e..604b4aae3364 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -747,7 +747,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, unsigned long flags; struct sk_buff *skb, *tmp; u32 hw_reconf_flags = 0; - int i; + int i, flushed; clear_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -772,11 +772,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * (because if we remove a STA after ops->remove_interface() * the driver will have removed the vif info already!) * - * This is relevant only in AP, WDS and mesh modes, since in - * all other modes we've already removed all stations when - * disconnecting etc. + * This is relevant only in WDS mode, in all other modes we've + * already removed all stations when disconnecting or similar, + * so warn otherwise. */ - sta_info_flush(sdata); + flushed = sta_info_flush(sdata); + WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || + (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); /* * Don't count this interface for promisc/allmulti while it From 051007d9e281cd8ea603a4cc4c96b0170b26c7e9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 23:49:02 +0100 Subject: [PATCH 118/478] mac80211: optimise roaming time again The last fixes re-added the RCU synchronize penalty on roaming to fix the races. Split up sta_info_flush() now to get rid of that again, and let managed mode (and only it) delay the actual destruction. Tested-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 15 ++++++++++----- net/mac80211/mlme.c | 2 +- net/mac80211/sta_info.c | 27 +++++++++++++++++---------- net/mac80211/sta_info.h | 20 +++++++++++++++++++- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 604b4aae3364..12341efb109e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -775,8 +775,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * This is relevant only in WDS mode, in all other modes we've * already removed all stations when disconnecting or similar, * so warn otherwise. + * + * We call sta_info_flush_cleanup() later, to combine RCU waits. */ - flushed = sta_info_flush(sdata); + flushed = sta_info_flush_defer(sdata); WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); @@ -861,11 +863,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->work); /* * When we get here, the interface is marked down. - * Call synchronize_rcu() to wait for the RX path - * should it be using the interface and enqueuing - * frames at this very time on another CPU. + * sta_info_flush_cleanup() calls rcu_barrier to + * wait for the station call_rcu() calls to complete, + * here we require it to wait for the RX path in case + * it is using the interface and enqueuing frames at + * this very time on another CPU. */ - synchronize_rcu(); + sta_info_flush_cleanup(sdata); + skb_queue_purge(&sdata->skb_queue); /* diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6d61d2f88716..3cf85d8629a7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1521,7 +1521,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(ifmgd->bssid, 0, ETH_ALEN); /* remove AP and TDLS peers */ - sta_info_flush(sdata); + sta_info_flush_defer(sdata); /* finally reset all BSS / config parameters */ changed |= ieee80211_reset_erp_info(sdata); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index d743645a17b5..7199b9d5b2f4 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -104,6 +104,16 @@ static void cleanup_single_sta(struct sta_info *sta) * neither mac80211 nor the driver can reference this * sta struct any more except by still existing timers * associated with this station that we clean up below. + * + * Note though that this still uses the sdata and even + * calls the driver in AP and mesh mode, so interfaces + * of those types mush use call sta_info_flush_cleanup() + * (typically via sta_info_flush()) before deconfiguring + * the driver. + * + * In station mode, nothing happens here so it doesn't + * have to (and doesn't) do that, this is intentional to + * speed up roaming. */ if (test_sta_flag(sta, WLAN_STA_PS_STA)) { @@ -887,14 +897,8 @@ void sta_info_stop(struct ieee80211_local *local) del_timer_sync(&local->sta_cleanup); } -/** - * sta_info_flush - flush matching STA entries from the STA table - * - * Returns the number of removed STA entries. - * - * @sdata: sdata to remove all stations from - */ -int sta_info_flush(struct ieee80211_sub_if_data *sdata) + +int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; @@ -911,12 +915,15 @@ int sta_info_flush(struct ieee80211_sub_if_data *sdata) } mutex_unlock(&local->sta_mtx); + return ret; +} + +void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata) +{ rcu_barrier(); ieee80211_cleanup_sdata_stas(sdata); cancel_work_sync(&sdata->cleanup_stations_wk); - - return ret; } void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 13698dae6a88..c3266aed4a07 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -548,7 +548,25 @@ void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); -int sta_info_flush(struct ieee80211_sub_if_data *sdata); +int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata); +void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata); + +/** + * sta_info_flush - flush matching STA entries from the STA table + * + * Returns the number of removed STA entries. + * + * @sdata: sdata to remove all stations from + */ +static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) +{ + int ret = sta_info_flush_defer(sdata); + + sta_info_flush_cleanup(sdata); + + return ret; +} + void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo); From 10ff57f98d5fccb9bb508ba30230e5df030d67e3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 15 Dec 2012 09:45:50 +0100 Subject: [PATCH 119/478] mac80211: remove a bit of dead mesh code In a file that's only built when CONFIG_MAC80211_MESH is defined, having an #ifdef on the same is entirely pointless, so remove it. Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8a9d5d03bbc2..c0008d5dff0a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -20,16 +20,11 @@ int mesh_allocated; static struct kmem_cache *rm_cache; -#ifdef CONFIG_MAC80211_MESH bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) { return (mgmt->u.action.u.mesh_action.action_code == WLAN_MESH_ACTION_HWMP_PATH_SELECTION); } -#else -bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) -{ return false; } -#endif void ieee80211s_init(void) { From 8a57fff0c178febbe28669a0ef68a8e3460a7589 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 17:03:17 +0100 Subject: [PATCH 120/478] regulatory: don't write past array when intersecting rules When intersecting rules, we count first to know how many rules need to be allocated, and then do the intersection into the allocated array. However, the code doing this writes past the end of the array because it attempts to do all intersections. Make it stop when the right number of rules has been reached. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 6e5308998e30..40646e823d5d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -647,9 +647,9 @@ static struct ieee80211_regdomain *regdom_intersect( if (!rd) return NULL; - for (x = 0; x < rd1->n_reg_rules; x++) { + for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) { rule1 = &rd1->reg_rules[x]; - for (y = 0; y < rd2->n_reg_rules; y++) { + for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) { rule2 = &rd2->reg_rules[y]; /* * This time around instead of using the stack lets From 82f20856304319d5a931846e0269eb911d52a905 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 12:49:16 +0100 Subject: [PATCH 121/478] regulatory: don't allocate too much memory There's no need to allocate one reg rule more than will be used, reduce the allocations. The allocation in nl80211 already doesn't allocate too much space. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 40646e823d5d..62bf212e5648 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -318,8 +318,9 @@ static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, int size_of_regd = 0; unsigned int i; - size_of_regd = sizeof(struct ieee80211_regdomain) + - ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); + size_of_regd = + sizeof(struct ieee80211_regdomain) + + src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule); regd = kzalloc(size_of_regd, GFP_KERNEL); if (!regd) @@ -641,7 +642,7 @@ static struct ieee80211_regdomain *regdom_intersect( return NULL; size_of_regd = sizeof(struct ieee80211_regdomain) + - ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); + num_rules * sizeof(struct ieee80211_reg_rule); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) From 74f53cd8d4474f9ba91c7309feabebae80a60089 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 17:26:17 +0100 Subject: [PATCH 122/478] regulatory: clean up regdom_intersect As the dummy_rule (also renamed from irule) is only used for output by the reg_rules_intersect() function there's no need to clear it at all, remove that. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 62bf212e5648..d52ffa2abc3f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -608,12 +608,7 @@ static struct ieee80211_regdomain *regdom_intersect( struct ieee80211_reg_rule *intersected_rule; struct ieee80211_regdomain *rd; /* This is just a dummy holder to help us count */ - struct ieee80211_reg_rule irule; - - /* Uses the stack temporarily for counter arithmetic */ - intersected_rule = &irule; - - memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); + struct ieee80211_reg_rule dummy_rule; if (!rd1 || !rd2) return NULL; @@ -630,11 +625,8 @@ static struct ieee80211_regdomain *regdom_intersect( rule1 = &rd1->reg_rules[x]; for (y = 0; y < rd2->n_reg_rules; y++) { rule2 = &rd2->reg_rules[y]; - if (!reg_rules_intersect(rule1, rule2, - intersected_rule)) + if (!reg_rules_intersect(rule1, rule2, &dummy_rule)) num_rules++; - memset(intersected_rule, 0, - sizeof(struct ieee80211_reg_rule)); } } From e9763c3c295a8b31316e16558b58b226e005b160 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 16:59:46 +0100 Subject: [PATCH 123/478] regulatory: clean up reg_copy_regd() Use ERR_PTR/IS_ERR to return the result or errors, also do some code cleanups. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 50 ++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index d52ffa2abc3f..52a3598859cd 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -311,11 +311,11 @@ static bool is_user_regdom_saved(void) return true; } -static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, - const struct ieee80211_regdomain *src_regd) +static const struct ieee80211_regdomain * +reg_copy_regd(const struct ieee80211_regdomain *src_regd) { struct ieee80211_regdomain *regd; - int size_of_regd = 0; + int size_of_regd; unsigned int i; size_of_regd = @@ -324,16 +324,15 @@ static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, regd = kzalloc(size_of_regd, GFP_KERNEL); if (!regd) - return -ENOMEM; + return ERR_PTR(-ENOMEM); memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); for (i = 0; i < src_regd->n_reg_rules; i++) memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], - sizeof(struct ieee80211_reg_rule)); + sizeof(struct ieee80211_reg_rule)); - *dst_regd = regd; - return 0; + return regd; } #ifdef CONFIG_CFG80211_INTERNAL_REGDB @@ -348,9 +347,8 @@ static DEFINE_MUTEX(reg_regdb_search_mutex); static void reg_regdb_search(struct work_struct *work) { struct reg_regdb_search_request *request; - const struct ieee80211_regdomain *curdom, *regdom; - int i, r; - bool set_reg = false; + const struct ieee80211_regdomain *curdom, *regdom = NULL; + int i; mutex_lock(&cfg80211_mutex); @@ -365,10 +363,7 @@ static void reg_regdb_search(struct work_struct *work) curdom = reg_regdb[i]; if (!memcmp(request->alpha2, curdom->alpha2, 2)) { - r = reg_copy_regd(®dom, curdom); - if (r) - break; - set_reg = true; + regdom = reg_copy_regd(curdom); break; } } @@ -377,7 +372,7 @@ static void reg_regdb_search(struct work_struct *work) } mutex_unlock(®_regdb_search_mutex); - if (set_reg) + if (!IS_ERR_OR_NULL(regdom)) set_regdom(regdom); mutex_unlock(&cfg80211_mutex); @@ -1509,6 +1504,7 @@ static void reg_set_request_processed(void) static int __regulatory_hint(struct wiphy *wiphy, struct regulatory_request *pending_request) { + const struct ieee80211_regdomain *regd; bool intersect = false; int r = 0; @@ -1519,11 +1515,12 @@ static int __regulatory_hint(struct wiphy *wiphy, if (r == REG_INTERSECT) { if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); - if (r) { + regd = reg_copy_regd(cfg80211_regdomain); + if (IS_ERR(regd)) { kfree(pending_request); - return r; + return PTR_ERR(regd); } + wiphy->regd = regd; } intersect = true; } else if (r) { @@ -1535,12 +1532,13 @@ static int __regulatory_hint(struct wiphy *wiphy, if (r == -EALREADY && pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); - if (r) { + regd = reg_copy_regd(cfg80211_regdomain); + if (IS_ERR(regd)) { kfree(pending_request); - return r; + return PTR_ERR(regd); } r = -EALREADY; + wiphy->regd = regd; goto new_request; } kfree(pending_request); @@ -2200,6 +2198,7 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) /* Takes ownership of rd only if it doesn't fail */ static int __set_regdom(const struct ieee80211_regdomain *rd) { + const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *intersected_rd = NULL; struct wiphy *request_wiphy; /* Some basic sanity checks first */ @@ -2257,8 +2256,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) } if (!last_request->intersect) { - int r; - if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { reset_regdomains(false); cfg80211_regdomain = rd; @@ -2277,10 +2274,11 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (request_wiphy->regd) return -EALREADY; - r = reg_copy_regd(&request_wiphy->regd, rd); - if (r) - return r; + regd = reg_copy_regd(rd); + if (IS_ERR(regd)) + return PTR_ERR(regd); + request_wiphy->regd = regd; reset_regdomains(false); cfg80211_regdomain = rd; return 0; From fea9bcedce2d0c751d4f31155f4634689d46166e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:32:01 +0100 Subject: [PATCH 124/478] regulatory: don't test list before iterating There's no need to test whether a list is empty or not before iterating. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 66 +++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 47 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 52a3598859cd..1496a10da638 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1104,9 +1104,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) assert_cfg80211_lock(); - if (list_empty(®_beacon_list)) - return; - list_for_each_entry(reg_beacon, ®_beacon_list, list) { if (!wiphy->bands[reg_beacon->chan.band]) continue; @@ -1666,11 +1663,6 @@ static void reg_process_pending_beacon_hints(void) /* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); - if (list_empty(®_pending_beacons)) { - spin_unlock_bh(®_pending_beacons_lock); - goto out; - } - list_for_each_entry_safe(pending_beacon, tmp, ®_pending_beacons, list) { @@ -1685,7 +1677,6 @@ static void reg_process_pending_beacon_hints(void) } spin_unlock_bh(®_pending_beacons_lock); -out: mutex_unlock(&cfg80211_mutex); } @@ -1949,34 +1940,24 @@ static void restore_regulatory_settings(bool reset_user) * settings. */ spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) { - list_for_each_entry_safe(reg_request, tmp, - ®_requests_list, list) { - if (reg_request->initiator != - NL80211_REGDOM_SET_BY_USER) - continue; - list_move_tail(®_request->list, &tmp_reg_req_list); - } + list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { + if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER) + continue; + list_move_tail(®_request->list, &tmp_reg_req_list); } spin_unlock(®_requests_lock); /* Clear beacon hints */ spin_lock_bh(®_pending_beacons_lock); - if (!list_empty(®_pending_beacons)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } + list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); } spin_unlock_bh(®_pending_beacons_lock); - if (!list_empty(®_beacon_list)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } + list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); } /* First restore to the basic regulatory settings */ @@ -2490,30 +2471,21 @@ void /* __init_or_exit */ regulatory_exit(void) platform_device_unregister(reg_pdev); spin_lock_bh(®_pending_beacons_lock); - if (!list_empty(®_pending_beacons)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } + list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); } spin_unlock_bh(®_pending_beacons_lock); - if (!list_empty(®_beacon_list)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } + list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); } spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) { - list_for_each_entry_safe(reg_request, tmp, - ®_requests_list, list) { - list_del(®_request->list); - kfree(reg_request); - } + list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { + list_del(®_request->list); + kfree(reg_request); } spin_unlock(®_requests_lock); From 75e2dba866706ab9b133983b7fd9a6297b24c22d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:21:26 +0100 Subject: [PATCH 125/478] regulatory: simplify regulatory_hint_11d There's no need to unlock before calling queue_regulatory_request(), so simplify the function. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1496a10da638..cd99d285e8d8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1825,12 +1825,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; request->country_ie_env = env; - mutex_unlock(®_mutex); - queue_regulatory_request(request); - - return; - out: mutex_unlock(®_mutex); } From 1a9193185f462a51815fe48491f8a6fb6b942551 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:21:11 +0100 Subject: [PATCH 126/478] regulatory: code cleanup Clean up various things like indentation, extra parentheses, too many/few line breaks, etc. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 15 +-- net/wireless/reg.c | 286 +++++++++++++++-------------------------- net/wireless/reg.h | 4 +- net/wireless/sme.c | 6 +- 4 files changed, 116 insertions(+), 195 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f45706adaf34..a19583cf4b6e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4259,7 +4259,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]); nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], - rem_reg_rules) { + rem_reg_rules) { num_rules++; if (num_rules > NL80211_MAX_SUPP_REG_RULES) return -EINVAL; @@ -4273,7 +4273,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) } size_of_regd = sizeof(struct ieee80211_regdomain) + - (num_rules * sizeof(struct ieee80211_reg_rule)); + num_rules * sizeof(struct ieee80211_reg_rule); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) { @@ -4293,10 +4293,10 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) rd->dfs_region = dfs_region; nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], - rem_reg_rules) { + rem_reg_rules) { nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, - nla_data(nl_reg_rule), nla_len(nl_reg_rule), - reg_rule_policy); + nla_data(nl_reg_rule), nla_len(nl_reg_rule), + reg_rule_policy); r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); if (r) goto bad_reg; @@ -4312,10 +4312,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) BUG_ON(rule_idx != num_rules); r = set_regdom(rd); - - mutex_unlock(&cfg80211_mutex); - - return r; + rd = NULL; bad_reg: mutex_unlock(&cfg80211_mutex); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cd99d285e8d8..cf2fb3425a63 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -219,18 +218,14 @@ bool is_world_regdom(const char *alpha2) { if (!alpha2) return false; - if (alpha2[0] == '0' && alpha2[1] == '0') - return true; - return false; + return alpha2[0] == '0' && alpha2[1] == '0'; } static bool is_alpha2_set(const char *alpha2) { if (!alpha2) return false; - if (alpha2[0] != 0 && alpha2[1] != 0) - return true; - return false; + return alpha2[0] && alpha2[1]; } static bool is_unknown_alpha2(const char *alpha2) @@ -241,9 +236,7 @@ static bool is_unknown_alpha2(const char *alpha2) * Special case where regulatory domain was built by driver * but a specific alpha2 cannot be determined */ - if (alpha2[0] == '9' && alpha2[1] == '9') - return true; - return false; + return alpha2[0] == '9' && alpha2[1] == '9'; } static bool is_intersected_alpha2(const char *alpha2) @@ -255,28 +248,21 @@ static bool is_intersected_alpha2(const char *alpha2) * result of an intersection between two regulatory domain * structures */ - if (alpha2[0] == '9' && alpha2[1] == '8') - return true; - return false; + return alpha2[0] == '9' && alpha2[1] == '8'; } static bool is_an_alpha2(const char *alpha2) { if (!alpha2) return false; - if (isalpha(alpha2[0]) && isalpha(alpha2[1])) - return true; - return false; + return isalpha(alpha2[0]) && isalpha(alpha2[1]); } static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) { if (!alpha2_x || !alpha2_y) return false; - if (alpha2_x[0] == alpha2_y[0] && - alpha2_x[1] == alpha2_y[1]) - return true; - return false; + return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]; } static bool regdom_changes(const char *alpha2) @@ -285,9 +271,7 @@ static bool regdom_changes(const char *alpha2) if (!cfg80211_regdomain) return true; - if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) - return false; - return true; + return !alpha2_equal(cfg80211_regdomain->alpha2, alpha2); } /* @@ -301,11 +285,9 @@ static bool is_user_regdom_saved(void) return false; /* This would indicate a mistake on the design */ - if (WARN((!is_world_regdom(user_alpha2) && - !is_an_alpha2(user_alpha2)), + if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2), "Unexpected user alpha2: %c%c\n", - user_alpha2[0], - user_alpha2[1])) + user_alpha2[0], user_alpha2[1])) return false; return true; @@ -359,10 +341,10 @@ static void reg_regdb_search(struct work_struct *work) list); list_del(&request->list); - for (i=0; ialpha2, curdom->alpha2, 2)) { + if (alpha2_equal(request->alpha2, curdom->alpha2)) { regdom = reg_copy_regd(curdom); break; } @@ -456,7 +438,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; if (freq_range->end_freq_khz <= freq_range->start_freq_khz || - freq_range->max_bandwidth_khz > freq_diff) + freq_range->max_bandwidth_khz > freq_diff) return false; return true; @@ -514,7 +496,7 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, * regulatory rule support for other "bands". **/ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, - u32 freq_khz) + u32 freq_khz) { #define ONE_GHZ_IN_KHZ 1000000 /* @@ -536,10 +518,9 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, * Helper for regdom_intersect(), this does the real * mathematical intersection fun */ -static int reg_rules_intersect( - const struct ieee80211_reg_rule *rule1, - const struct ieee80211_reg_rule *rule2, - struct ieee80211_reg_rule *intersected_rule) +static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, + const struct ieee80211_reg_rule *rule2, + struct ieee80211_reg_rule *intersected_rule) { const struct ieee80211_freq_range *freq_range1, *freq_range2; struct ieee80211_freq_range *freq_range; @@ -556,11 +537,11 @@ static int reg_rules_intersect( power_rule = &intersected_rule->power_rule; freq_range->start_freq_khz = max(freq_range1->start_freq_khz, - freq_range2->start_freq_khz); + freq_range2->start_freq_khz); freq_range->end_freq_khz = min(freq_range1->end_freq_khz, - freq_range2->end_freq_khz); + freq_range2->end_freq_khz); freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, - freq_range2->max_bandwidth_khz); + freq_range2->max_bandwidth_khz); freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; if (freq_range->max_bandwidth_khz > freq_diff) @@ -571,7 +552,7 @@ static int reg_rules_intersect( power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, power_rule2->max_antenna_gain); - intersected_rule->flags = (rule1->flags | rule2->flags); + intersected_rule->flags = rule1->flags | rule2->flags; if (!is_valid_reg_rule(intersected_rule)) return -EINVAL; @@ -592,9 +573,9 @@ static int reg_rules_intersect( * resulting intersection of rules between rd1 and rd2. We will * kzalloc() this structure for you. */ -static struct ieee80211_regdomain *regdom_intersect( - const struct ieee80211_regdomain *rd1, - const struct ieee80211_regdomain *rd2) +static struct ieee80211_regdomain * +regdom_intersect(const struct ieee80211_regdomain *rd1, + const struct ieee80211_regdomain *rd2) { int r, size_of_regd; unsigned int x, y; @@ -645,8 +626,7 @@ static struct ieee80211_regdomain *regdom_intersect( * a memcpy() */ intersected_rule = &rd->reg_rules[rule_idx]; - r = reg_rules_intersect(rule1, rule2, - intersected_rule); + r = reg_rules_intersect(rule1, rule2, intersected_rule); /* * No need to memset here the intersected rule here as * we're not using the stack anymore @@ -731,9 +711,7 @@ static int freq_reg_info_regd(struct wiphy *wiphy, if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); - bw_fits = reg_does_bw_fit(fr, - center_freq, - desired_bw_khz); + bw_fits = reg_does_bw_fit(fr, center_freq, desired_bw_khz); if (band_rule_found && bw_fits) { *reg_rule = rr; @@ -747,17 +725,13 @@ static int freq_reg_info_regd(struct wiphy *wiphy, return -EINVAL; } -int freq_reg_info(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, +int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule) { assert_cfg80211_lock(); - return freq_reg_info_regd(wiphy, - center_freq, - desired_bw_khz, - reg_rule, - NULL); + + return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, + reg_rule, NULL); } EXPORT_SYMBOL(freq_reg_info); @@ -795,16 +769,12 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, else snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); - REG_DBG_PRINT("Updating information on frequency %d MHz " - "for a %d MHz width channel with regulatory rule:\n", - chan->center_freq, - KHZ_TO_MHZ(desired_bw_khz)); + REG_DBG_PRINT("Updating information on frequency %d MHz for a %d MHz width channel with regulatory rule:\n", + chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", - freq_range->start_freq_khz, - freq_range->end_freq_khz, - freq_range->max_bandwidth_khz, - max_antenna_gain, + freq_range->start_freq_khz, freq_range->end_freq_khz, + freq_range->max_bandwidth_khz, max_antenna_gain, power_rule->max_eirp); } #else @@ -850,11 +820,8 @@ static void handle_channel(struct wiphy *wiphy, flags = chan->orig_flags; - r = freq_reg_info(wiphy, - MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, - ®_rule); - + r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, ®_rule); if (r) { /* * We will disable all channels that do not match our @@ -902,8 +869,9 @@ static void handle_channel(struct wiphy *wiphy, chan->beacon_found = false; chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); - chan->max_antenna_gain = min(chan->orig_mag, - (int) MBI_TO_DBI(power_rule->max_antenna_gain)); + chan->max_antenna_gain = + min_t(int, chan->orig_mag, + MBI_TO_DBI(power_rule->max_antenna_gain)); chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); if (chan->orig_mpwr) { /* @@ -922,8 +890,7 @@ static void handle_channel(struct wiphy *wiphy, chan->max_power = chan->max_reg_power; } -static void handle_band(struct wiphy *wiphy, - enum ieee80211_band band, +static void handle_band(struct wiphy *wiphy, enum ieee80211_band band, enum nl80211_reg_initiator initiator) { unsigned int i; @@ -940,51 +907,48 @@ static bool reg_request_cell_base(struct regulatory_request *request) { if (request->initiator != NL80211_REGDOM_SET_BY_USER) return false; - if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) - return false; - return true; + return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; } bool reg_last_request_cell_base(void) { bool val; + assert_cfg80211_lock(); mutex_lock(®_mutex); val = reg_request_cell_base(last_request); mutex_unlock(®_mutex); + return val; } #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS - /* Core specific check */ static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { if (!reg_num_devs_support_basehint) return -EOPNOTSUPP; - if (reg_request_cell_base(last_request)) { - if (!regdom_changes(pending_request->alpha2)) - return -EALREADY; - return 0; - } + if (reg_request_cell_base(last_request) && + !regdom_changes(pending_request->alpha2)) + return -EALREADY; + return 0; } /* Device specific check */ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) { - if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) - return true; - return false; + return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS); } #else static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { return -EOPNOTSUPP; } -static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) + +static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) { return true; } @@ -995,17 +959,14 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { if (!last_request) { - REG_DBG_PRINT("Ignoring regulatory request %s since " - "last_request is not set\n", + REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", reg_initiator_name(initiator)); return true; } if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { - REG_DBG_PRINT("Ignoring regulatory request %s " - "since the driver uses its own custom " - "regulatory domain\n", + REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n", reg_initiator_name(initiator)); return true; } @@ -1017,9 +978,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && !is_world_regdom(last_request->alpha2)) { - REG_DBG_PRINT("Ignoring regulatory request %s " - "since the driver requires its own regulatory " - "domain to be set first\n", + REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", reg_initiator_name(initiator)); return true; } @@ -1030,8 +989,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, return false; } -static void handle_reg_beacon(struct wiphy *wiphy, - unsigned int chan_idx, +static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, struct reg_beacon *reg_beacon) { struct ieee80211_supported_band *sband; @@ -1139,16 +1097,14 @@ static void reg_process_beacons(struct wiphy *wiphy) wiphy_update_beacon_reg(wiphy); } -static bool is_ht40_not_allowed(struct ieee80211_channel *chan) +static bool is_ht40_allowed(struct ieee80211_channel *chan) { if (!chan) - return true; + return false; if (chan->flags & IEEE80211_CHAN_DISABLED) - return true; + return false; /* This would happen when regulatory rules disallow HT40 completely */ - if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) - return true; - return false; + return !(chan->flags & IEEE80211_CHAN_NO_HT40); } static void reg_process_ht_flags_channel(struct wiphy *wiphy, @@ -1166,7 +1122,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, BUG_ON(chan_idx >= sband->n_channels); channel = &sband->channels[chan_idx]; - if (is_ht40_not_allowed(channel)) { + if (!is_ht40_allowed(channel)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; } @@ -1177,6 +1133,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, */ for (i = 0; i < sband->n_channels; i++) { struct ieee80211_channel *c = &sband->channels[i]; + if (c->center_freq == (channel->center_freq - 20)) channel_before = c; if (c->center_freq == (channel->center_freq + 20)) @@ -1188,12 +1145,12 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, * if that ever changes we also need to change the below logic * to include that as well. */ - if (is_ht40_not_allowed(channel_before)) + if (!is_ht40_allowed(channel_before)) channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; else channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - if (is_ht40_not_allowed(channel_after)) + if (!is_ht40_allowed(channel_after)) channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; else channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; @@ -1245,6 +1202,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, reg_process_beacons(wiphy); reg_process_ht_flags(wiphy); + if (wiphy->reg_notifier) wiphy->reg_notifier(wiphy, last_request); } @@ -1289,18 +1247,12 @@ static void handle_channel_custom(struct wiphy *wiphy, BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; - r = freq_reg_info_regd(wiphy, - MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, - ®_rule, - regd); + r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, ®_rule, regd); if (r) { - REG_DBG_PRINT("Disabling freq %d MHz as custom " - "regd has no rule that fits a %d MHz " - "wide channel\n", - chan->center_freq, - KHZ_TO_MHZ(desired_bw_khz)); + REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits a %d MHz wide channel\n", + chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); chan->flags = IEEE80211_CHAN_DISABLED; return; } @@ -1350,7 +1302,7 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, /* * no point in calling this if it won't have any effect - * on your device's supportd bands. + * on your device's supported bands. */ WARN_ON(!bands_set); } @@ -1379,7 +1331,6 @@ static int ignore_request(struct wiphy *wiphy, case NL80211_REGDOM_SET_BY_CORE: return 0; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - if (reg_request_cell_base(last_request)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(pending_request->alpha2)) @@ -1444,18 +1395,17 @@ static int ignore_request(struct wiphy *wiphy, * to their country before the IE is picked up */ if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && - last_request->intersect) + last_request->intersect) return -EOPNOTSUPP; /* * Process user requests only after previous user/driver/core * requests have been processed */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || - last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || - last_request->initiator == NL80211_REGDOM_SET_BY_USER) { - if (regdom_changes(last_request->alpha2)) - return -EAGAIN; - } + if ((last_request->initiator == NL80211_REGDOM_SET_BY_CORE || + last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || + last_request->initiator == NL80211_REGDOM_SET_BY_USER) && + regdom_changes(last_request->alpha2)) + return -EAGAIN; if (!regdom_changes(pending_request->alpha2)) return -EALREADY; @@ -1585,8 +1535,7 @@ static void reg_process_hint(struct regulatory_request *reg_request, if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); - if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && - !wiphy) { + if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { kfree(reg_request); return; } @@ -1603,8 +1552,7 @@ static void reg_process_hint(struct regulatory_request *reg_request, * We only time out user hints, given that they should be the only * source of bogus requests. */ - if (r != -EALREADY && - reg_initiator == NL80211_REGDOM_SET_BY_USER) + if (r != -EALREADY && reg_initiator == NL80211_REGDOM_SET_BY_USER) schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); } @@ -1622,8 +1570,7 @@ static void reg_process_pending_hints(void) /* When last_request->processed becomes true this will be rescheduled */ if (last_request && !last_request->processed) { - REG_DBG_PRINT("Pending regulatory request, waiting " - "for it to be processed...\n"); + REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); goto out; } @@ -1665,7 +1612,6 @@ static void reg_process_pending_beacon_hints(void) list_for_each_entry_safe(pending_beacon, tmp, ®_pending_beacons, list) { - list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ @@ -1708,8 +1654,7 @@ static int regulatory_hint_core(const char *alpha2) { struct regulatory_request *request; - request = kzalloc(sizeof(struct regulatory_request), - GFP_KERNEL); + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; @@ -1776,10 +1721,8 @@ EXPORT_SYMBOL(regulatory_hint); * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and * therefore cannot iterate over the rdev list here. */ -void regulatory_hint_11d(struct wiphy *wiphy, - enum ieee80211_band band, - const u8 *country_ie, - u8 country_ie_len) +void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, + const u8 *country_ie, u8 country_ie_len) { char alpha2[2]; enum environment_cap env = ENVIRON_ANY; @@ -1811,8 +1754,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, * cfg80211_mutex. */ if (likely(last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy_idx_valid(last_request->wiphy_idx))) + NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy_idx_valid(last_request->wiphy_idx))) goto out; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); @@ -1840,8 +1783,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) if (is_user_regdom_saved()) { /* Unless we're asked to ignore it and reset it */ if (reset_user) { - REG_DBG_PRINT("Restoring regulatory settings " - "including user preference\n"); + REG_DBG_PRINT("Restoring regulatory settings including user preference\n"); user_alpha2[0] = '9'; user_alpha2[1] = '7'; @@ -1851,26 +1793,20 @@ static void restore_alpha2(char *alpha2, bool reset_user) * back as they were for a full restore. */ if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on " - "module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], - ieee80211_regdom[1]); + REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", + ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } } else { - REG_DBG_PRINT("Restoring regulatory settings " - "while preserving user preference for: %c%c\n", - user_alpha2[0], - user_alpha2[1]); + REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n", + user_alpha2[0], user_alpha2[1]); alpha2[0] = user_alpha2[0]; alpha2[1] = user_alpha2[1]; } } else if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on " - "module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], - ieee80211_regdom[1]); + REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", + ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } else @@ -1986,10 +1922,8 @@ static void restore_regulatory_settings(bool reset_user) spin_lock(®_requests_lock); list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { - REG_DBG_PRINT("Adding request for country %c%c back " - "into the queue\n", - reg_request->alpha2[0], - reg_request->alpha2[1]); + REG_DBG_PRINT("Adding request for country %c%c back into the queue\n", + reg_request->alpha2[0], reg_request->alpha2[1]); list_move_tail(®_request->list, ®_requests_list); } spin_unlock(®_requests_lock); @@ -2004,8 +1938,7 @@ static void restore_regulatory_settings(bool reset_user) void regulatory_hint_disconnect(void) { - REG_DBG_PRINT("All devices are disconnected, going to " - "restore regulatory settings\n"); + REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n"); restore_regulatory_settings(false); } @@ -2024,25 +1957,23 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, { struct reg_beacon *reg_beacon; - if (likely((beacon_chan->beacon_found || - (beacon_chan->flags & IEEE80211_CHAN_RADAR) || + if (beacon_chan->beacon_found || + beacon_chan->flags & IEEE80211_CHAN_RADAR || (beacon_chan->band == IEEE80211_BAND_2GHZ && - !freq_is_chan_12_13_14(beacon_chan->center_freq))))) + !freq_is_chan_12_13_14(beacon_chan->center_freq))) return 0; reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); if (!reg_beacon) return -ENOMEM; - REG_DBG_PRINT("Found new beacon on " - "frequency: %d MHz (Ch %d) on %s\n", + REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n", beacon_chan->center_freq, ieee80211_frequency_to_channel(beacon_chan->center_freq), wiphy_name(wiphy)); memcpy(®_beacon->chan, beacon_chan, - sizeof(struct ieee80211_channel)); - + sizeof(struct ieee80211_channel)); /* * Since we can be called from BH or and non-BH context @@ -2122,7 +2053,7 @@ static void print_dfs_region(u8 dfs_region) pr_info(" DFS Master region JP"); break; default: - pr_info(" DFS Master region Uknown"); + pr_info(" DFS Master region Unknown"); break; } } @@ -2131,7 +2062,6 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) { if (is_intersected_alpha2(rd->alpha2)) { - if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { struct cfg80211_registered_device *rdev; @@ -2145,22 +2075,21 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) pr_info("Current regulatory domain intersected:\n"); } else pr_info("Current regulatory domain intersected:\n"); - } else if (is_world_regdom(rd->alpha2)) + } else if (is_world_regdom(rd->alpha2)) { pr_info("World regulatory domain updated:\n"); - else { + } else { if (is_unknown_alpha2(rd->alpha2)) pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); else { if (reg_request_cell_base(last_request)) - pr_info("Regulatory domain changed " - "to country: %c%c by Cell Station\n", + pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", rd->alpha2[0], rd->alpha2[1]); else - pr_info("Regulatory domain changed " - "to country: %c%c\n", + pr_info("Regulatory domain changed to country: %c%c\n", rd->alpha2[0], rd->alpha2[1]); } } + print_dfs_region(rd->dfs_region); print_rd_rules(rd); } @@ -2187,7 +2116,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) } if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && - !is_unknown_alpha2(rd->alpha2)) + !is_unknown_alpha2(rd->alpha2)) return -EINVAL; if (!last_request) @@ -2263,7 +2192,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Intersection requires a bit more work */ if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - intersected_rd = regdom_intersect(rd, cfg80211_regdomain); if (!intersected_rd) return -EINVAL; @@ -2315,8 +2243,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) } /* This would make this whole thing pointless */ - if (!last_request->intersect) - BUG_ON(rd != cfg80211_regdomain); + BUG_ON(!last_request->intersect && rd != cfg80211_regdomain); /* update all wiphys now with the new established regulatory domain */ update_all_wiphy_regulatory(last_request->initiator); @@ -2393,8 +2320,7 @@ out: static void reg_timeout_work(struct work_struct *work) { - REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " - "restoring regulatory settings\n"); + REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); restore_regulatory_settings(true); } @@ -2448,7 +2374,7 @@ int __init regulatory_init(void) return 0; } -void /* __init_or_exit */ regulatory_exit(void) +void regulatory_exit(void) { struct regulatory_request *reg_request, *tmp; struct reg_beacon *reg_beacon, *btmp; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 4c0a32ffd530..37891e813a74 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -55,8 +55,8 @@ bool reg_last_request_cell_base(void); * set the wiphy->disable_beacon_hints to true. */ int regulatory_hint_found_beacon(struct wiphy *wiphy, - struct ieee80211_channel *beacon_chan, - gfp_t gfp); + struct ieee80211_channel *beacon_chan, + gfp_t gfp); /** * regulatory_hint_11d - hints a country IE as a regulatory domain diff --git a/net/wireless/sme.c b/net/wireless/sme.c index f2431e41a373..d2d26518cdd7 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -519,10 +519,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * - country_ie + 2, the start of the country ie data, and * - and country_ie[1] which is the IE length */ - regulatory_hint_11d(wdev->wiphy, - bss->channel->band, - country_ie + 2, - country_ie[1]); + regulatory_hint_11d(wdev->wiphy, bss->channel->band, + country_ie + 2, country_ie[1]); kfree(country_ie); } From 9027b1493b57a6a8f9f56741e4c4189048ea5633 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:59:24 +0100 Subject: [PATCH 127/478] regulatory: remove useless locking on exit It would be a major problem if anything were to run concurrently while the module is being unloaded so remove the locking that doesn't help anything. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cf2fb3425a63..cf4386fade8a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2382,34 +2382,29 @@ void regulatory_exit(void) cancel_work_sync(®_work); cancel_delayed_work_sync(®_timeout); + /* Lock to suppress warnings */ mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true); + mutex_unlock(&cfg80211_mutex); + mutex_unlock(®_mutex); dev_set_uevent_suppress(®_pdev->dev, true); platform_device_unregister(reg_pdev); - spin_lock_bh(®_pending_beacons_lock); list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { list_del(®_beacon->list); kfree(reg_beacon); } - spin_unlock_bh(®_pending_beacons_lock); list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { list_del(®_beacon->list); kfree(reg_beacon); } - spin_lock(®_requests_lock); list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { list_del(®_request->list); kfree(reg_request); } - spin_unlock(®_requests_lock); - - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); } From 2f92212b71f198d42060f7133ff1dc4edf438f6c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:54:55 +0100 Subject: [PATCH 128/478] regulatory: use proper enum for return values Instead of treating special error codes specially, like -EALREADY, introduce a real enum for all the needed possibilities and use it. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 133 +++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 64 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cf4386fade8a..129586994662 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -65,6 +65,13 @@ #define REG_DBG_PRINT(args...) #endif +enum reg_request_treatment { + REG_REQ_OK, + REG_REQ_IGNORE, + REG_REQ_INTERSECT, + REG_REQ_ALREADY_SET, +}; + static struct regulatory_request core_request_world = { .initiator = NL80211_REGDOM_SET_BY_CORE, .alpha2[0] = '0', @@ -925,16 +932,17 @@ bool reg_last_request_cell_base(void) #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS /* Core specific check */ -static int reg_ignore_cell_hint(struct regulatory_request *pending_request) +static enum reg_request_treatment +reg_ignore_cell_hint(struct regulatory_request *pending_request) { if (!reg_num_devs_support_basehint) - return -EOPNOTSUPP; + return REG_REQ_IGNORE; if (reg_request_cell_base(last_request) && !regdom_changes(pending_request->alpha2)) - return -EALREADY; + return REG_REQ_ALREADY_SET; - return 0; + return REG_REQ_OK; } /* Device specific check */ @@ -945,7 +953,7 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) #else static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { - return -EOPNOTSUPP; + return REG_REQ_IGNORE; } static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) @@ -1308,15 +1316,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, } EXPORT_SYMBOL(wiphy_apply_custom_regulatory); -/* - * Return value which can be used by ignore_request() to indicate - * it has been determined we should intersect two regulatory domains - */ -#define REG_INTERSECT 1 - /* This has the logic which determines when a new request * should be ignored. */ -static int ignore_request(struct wiphy *wiphy, +static enum reg_request_treatment +get_reg_request_treatment(struct wiphy *wiphy, struct regulatory_request *pending_request) { struct wiphy *last_wiphy = NULL; @@ -1325,17 +1328,17 @@ static int ignore_request(struct wiphy *wiphy, /* All initial requests are respected */ if (!last_request) - return 0; + return REG_REQ_OK; switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: - return 0; + return REG_REQ_OK; case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (reg_request_cell_base(last_request)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(pending_request->alpha2)) - return -EOPNOTSUPP; - return -EALREADY; + return REG_REQ_IGNORE; + return REG_REQ_ALREADY_SET; } last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); @@ -1352,23 +1355,23 @@ static int ignore_request(struct wiphy *wiphy, * to be correct. Reject second one for now. */ if (regdom_changes(pending_request->alpha2)) - return -EOPNOTSUPP; - return -EALREADY; + return REG_REQ_IGNORE; + return REG_REQ_ALREADY_SET; } /* * Two consecutive Country IE hints on the same wiphy. * This should be picked up early by the driver/stack */ if (WARN_ON(regdom_changes(pending_request->alpha2))) - return 0; - return -EALREADY; + return REG_REQ_OK; + return REG_REQ_ALREADY_SET; } return 0; case NL80211_REGDOM_SET_BY_DRIVER: if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { if (regdom_changes(pending_request->alpha2)) - return 0; - return -EALREADY; + return REG_REQ_OK; + return REG_REQ_ALREADY_SET; } /* @@ -1378,25 +1381,25 @@ static int ignore_request(struct wiphy *wiphy, */ if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(pending_request->alpha2)) - return -EALREADY; + return REG_REQ_ALREADY_SET; - return REG_INTERSECT; + return REG_REQ_INTERSECT; case NL80211_REGDOM_SET_BY_USER: if (reg_request_cell_base(pending_request)) return reg_ignore_cell_hint(pending_request); if (reg_request_cell_base(last_request)) - return -EOPNOTSUPP; + return REG_REQ_IGNORE; if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) - return REG_INTERSECT; + return REG_REQ_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && last_request->intersect) - return -EOPNOTSUPP; + return REG_REQ_IGNORE; /* * Process user requests only after previous user/driver/core * requests have been processed @@ -1405,15 +1408,15 @@ static int ignore_request(struct wiphy *wiphy, last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || last_request->initiator == NL80211_REGDOM_SET_BY_USER) && regdom_changes(last_request->alpha2)) - return -EAGAIN; + return REG_REQ_IGNORE; if (!regdom_changes(pending_request->alpha2)) - return -EALREADY; + return REG_REQ_ALREADY_SET; - return 0; + return REG_REQ_OK; } - return -EINVAL; + return REG_REQ_IGNORE; } static void reg_set_request_processed(void) @@ -1443,23 +1446,24 @@ static void reg_set_request_processed(void) * The Wireless subsystem can use this function to hint to the wireless core * what it believes should be the current regulatory domain. * - * Returns zero if all went fine, %-EALREADY if a regulatory domain had - * already been set or other standard error codes. + * Returns one of the different reg request treatment values. * * Caller must hold &cfg80211_mutex and ®_mutex */ -static int __regulatory_hint(struct wiphy *wiphy, - struct regulatory_request *pending_request) +static enum reg_request_treatment +__regulatory_hint(struct wiphy *wiphy, + struct regulatory_request *pending_request) { const struct ieee80211_regdomain *regd; bool intersect = false; - int r = 0; + enum reg_request_treatment treatment; assert_cfg80211_lock(); - r = ignore_request(wiphy, pending_request); + treatment = get_reg_request_treatment(wiphy, pending_request); - if (r == REG_INTERSECT) { + switch (treatment) { + case REG_REQ_INTERSECT: if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { regd = reg_copy_regd(cfg80211_regdomain); @@ -1470,26 +1474,28 @@ static int __regulatory_hint(struct wiphy *wiphy, wiphy->regd = regd; } intersect = true; - } else if (r) { + break; + case REG_REQ_OK: + break; + default: /* * If the regulatory domain being requested by the * driver has already been set just copy it to the * wiphy */ - if (r == -EALREADY && - pending_request->initiator == - NL80211_REGDOM_SET_BY_DRIVER) { + if (treatment == REG_REQ_ALREADY_SET && + pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { regd = reg_copy_regd(cfg80211_regdomain); if (IS_ERR(regd)) { kfree(pending_request); - return PTR_ERR(regd); + return REG_REQ_IGNORE; } - r = -EALREADY; + treatment = REG_REQ_ALREADY_SET; wiphy->regd = regd; goto new_request; } kfree(pending_request); - return r; + return treatment; } new_request: @@ -1506,28 +1512,29 @@ new_request: user_alpha2[1] = last_request->alpha2[1]; } - /* When r == REG_INTERSECT we do need to call CRDA */ - if (r < 0) { + /* When r == REG_REQ_INTERSECT we do need to call CRDA */ + if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { /* * Since CRDA will not be called in this case as we already * have applied the requested regulatory domain before we just * inform userspace we have processed the request */ - if (r == -EALREADY) { + if (treatment == REG_REQ_ALREADY_SET) { nl80211_send_reg_change_event(last_request); reg_set_request_processed(); } - return r; + return treatment; } - return call_crda(last_request->alpha2); + if (call_crda(last_request->alpha2)) + return REG_REQ_IGNORE; + return REG_REQ_OK; } /* This processes *all* regulatory hints */ static void reg_process_hint(struct regulatory_request *reg_request, enum nl80211_reg_initiator reg_initiator) { - int r = 0; struct wiphy *wiphy = NULL; BUG_ON(!reg_request->alpha2); @@ -1540,20 +1547,18 @@ static void reg_process_hint(struct regulatory_request *reg_request, return; } - r = __regulatory_hint(wiphy, reg_request); - /* This is required so that the orig_* parameters are saved */ - if (r == -EALREADY && wiphy && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { - wiphy_update_regulatory(wiphy, reg_initiator); - return; + switch (__regulatory_hint(wiphy, reg_request)) { + case REG_REQ_ALREADY_SET: + /* This is required so that the orig_* parameters are saved */ + if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) + wiphy_update_regulatory(wiphy, reg_initiator); + break; + default: + if (reg_initiator == NL80211_REGDOM_SET_BY_USER) + schedule_delayed_work(®_timeout, + msecs_to_jiffies(3142)); + break; } - - /* - * We only time out user hints, given that they should be the only - * source of bogus requests. - */ - if (r != -EALREADY && reg_initiator == NL80211_REGDOM_SET_BY_USER) - schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); } /* From f41737669d2f4c07c09e90767498243e142171f9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 18:23:37 +0100 Subject: [PATCH 129/478] cfg80211: remove wiphy_idx_valid This is pretty much useless since get_wiphy_idx() always returns true since it's always called with a valid wiphy pointer. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/core.c | 14 +++----------- net/wireless/core.h | 16 +++------------- net/wireless/nl80211.c | 2 +- net/wireless/reg.c | 14 +++++--------- 4 files changed, 12 insertions(+), 34 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 14d990400354..747dd9365a44 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -57,9 +57,6 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { struct cfg80211_registered_device *result = NULL, *rdev; - if (!wiphy_idx_valid(wiphy_idx)) - return NULL; - assert_cfg80211_lock(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { @@ -74,10 +71,8 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) int get_wiphy_idx(struct wiphy *wiphy) { - struct cfg80211_registered_device *rdev; - if (!wiphy) - return WIPHY_IDX_STALE; - rdev = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + return rdev->wiphy_idx; } @@ -86,9 +81,6 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) { struct cfg80211_registered_device *rdev; - if (!wiphy_idx_valid(wiphy_idx)) - return NULL; - assert_cfg80211_lock(); rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); @@ -309,7 +301,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy_idx = wiphy_counter++; - if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { + if (unlikely(rdev->wiphy_idx < 0)) { wiphy_counter--; mutex_unlock(&cfg80211_mutex); /* ugh, wrapped! */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 3563097169cb..b8f4630c6ada 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -18,6 +18,9 @@ #include #include "reg.h" + +#define WIPHY_IDX_INVALID -1 + struct cfg80211_registered_device { const struct cfg80211_ops *ops; struct list_head list; @@ -96,13 +99,6 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) return container_of(wiphy, struct cfg80211_registered_device, wiphy); } -/* Note 0 is valid, hence phy0 */ -static inline -bool wiphy_idx_valid(int wiphy_idx) -{ - return wiphy_idx >= 0; -} - static inline void cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) { @@ -126,12 +122,6 @@ static inline void assert_cfg80211_lock(void) lockdep_assert_held(&cfg80211_mutex); } -/* - * You can use this to mark a wiphy_idx as not having an associated wiphy. - * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL - */ -#define WIPHY_IDX_STALE -1 - struct cfg80211_internal_bss { struct list_head list; struct rb_node rbn; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a19583cf4b6e..5fbc8cd9731b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8048,7 +8048,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) goto nla_put_failure; } - if (wiphy_idx_valid(request->wiphy_idx) && + if (request->wiphy_idx != WIPHY_IDX_INVALID && nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) goto nla_put_failure; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 129586994662..b825c82e5a45 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1539,7 +1539,7 @@ static void reg_process_hint(struct regulatory_request *reg_request, BUG_ON(!reg_request->alpha2); - if (wiphy_idx_valid(reg_request->wiphy_idx)) + if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { @@ -1684,7 +1684,7 @@ int regulatory_hint_user(const char *alpha2, if (!request) return -ENOMEM; - request->wiphy_idx = WIPHY_IDX_STALE; + request->wiphy_idx = WIPHY_IDX_INVALID; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_USER; @@ -1709,9 +1709,6 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) request->wiphy_idx = get_wiphy_idx(wiphy); - /* Must have registered wiphy first */ - BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); - request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_DRIVER; @@ -1758,9 +1755,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, * We leave conflict resolution to the workqueue, where can hold * cfg80211_mutex. */ - if (likely(last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy_idx_valid(last_request->wiphy_idx))) + if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && + last_request->wiphy_idx != WIPHY_IDX_INVALID) goto out; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); @@ -2317,7 +2313,7 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) if (!request_wiphy || request_wiphy != wiphy) goto out; - last_request->wiphy_idx = WIPHY_IDX_STALE; + last_request->wiphy_idx = WIPHY_IDX_INVALID; last_request->country_ie_env = ENVIRON_ANY; out: mutex_unlock(®_mutex); From fdc9d7b2863ce67f51ed64f89e5b20f3077be569 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 18:36:09 +0100 Subject: [PATCH 130/478] regulatory: remove BUG_ON This code is a bit too BUG_ON happy, remove all instances and while doing so make some code a bit smarter by passing the right pointer instead of indices into arrays. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 - net/wireless/reg.c | 95 ++++++++++++++++-------------------------- 2 files changed, 37 insertions(+), 60 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5fbc8cd9731b..9c2c91845be7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4309,8 +4309,6 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) } } - BUG_ON(rule_idx != num_rules); - r = set_regdom(rd); rd = NULL; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b825c82e5a45..57812dfc297b 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -213,7 +213,7 @@ static void reset_regdomains(bool full_reset) */ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { - BUG_ON(!last_request); + WARN_ON(!last_request); reset_regdomains(false); @@ -804,8 +804,7 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, */ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, - enum ieee80211_band band, - unsigned int chan_idx) + struct ieee80211_channel *chan) { int r; u32 flags, bw_flags = 0; @@ -813,18 +812,12 @@ static void handle_channel(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; struct wiphy *request_wiphy = NULL; assert_cfg80211_lock(); request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - chan = &sband->channels[chan_idx]; - flags = chan->orig_flags; r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), @@ -897,17 +890,17 @@ static void handle_channel(struct wiphy *wiphy, chan->max_power = chan->max_reg_power; } -static void handle_band(struct wiphy *wiphy, enum ieee80211_band band, - enum nl80211_reg_initiator initiator) +static void handle_band(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator, + struct ieee80211_supported_band *sband) { unsigned int i; - struct ieee80211_supported_band *sband; - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; + if (!sband) + return; for (i = 0; i < sband->n_channels; i++) - handle_channel(wiphy, initiator, band, i); + handle_channel(wiphy, initiator, &sband->channels[i]); } static bool reg_request_cell_base(struct regulatory_request *request) @@ -1116,20 +1109,14 @@ static bool is_ht40_allowed(struct ieee80211_channel *chan) } static void reg_process_ht_flags_channel(struct wiphy *wiphy, - enum ieee80211_band band, - unsigned int chan_idx) + struct ieee80211_channel *channel) { - struct ieee80211_supported_band *sband; - struct ieee80211_channel *channel; + struct ieee80211_supported_band *sband = wiphy->bands[channel->band]; struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; unsigned int i; assert_cfg80211_lock(); - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - channel = &sband->channels[chan_idx]; - if (!is_ht40_allowed(channel)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; @@ -1165,16 +1152,15 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, } static void reg_process_ht_flags_band(struct wiphy *wiphy, - enum ieee80211_band band) + struct ieee80211_supported_band *sband) { unsigned int i; - struct ieee80211_supported_band *sband; - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; + if (!sband) + return; for (i = 0; i < sband->n_channels; i++) - reg_process_ht_flags_channel(wiphy, band, i); + reg_process_ht_flags_channel(wiphy, &sband->channels[i]); } static void reg_process_ht_flags(struct wiphy *wiphy) @@ -1184,11 +1170,8 @@ static void reg_process_ht_flags(struct wiphy *wiphy) if (!wiphy) return; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - reg_process_ht_flags_band(wiphy, band); - } - + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + reg_process_ht_flags_band(wiphy, wiphy->bands[band]); } static void wiphy_update_regulatory(struct wiphy *wiphy, @@ -1203,10 +1186,8 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, last_request->dfs_region = cfg80211_regdomain->dfs_region; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - handle_band(wiphy, band, initiator); - } + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + handle_band(wiphy, initiator, wiphy->bands[band]); reg_process_beacons(wiphy); reg_process_ht_flags(wiphy); @@ -1236,8 +1217,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) } static void handle_channel_custom(struct wiphy *wiphy, - enum ieee80211_band band, - unsigned int chan_idx, + struct ieee80211_channel *chan, const struct ieee80211_regdomain *regd) { int r; @@ -1246,15 +1226,9 @@ static void handle_channel_custom(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; assert_reg_lock(); - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - chan = &sband->channels[chan_idx]; - r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz, ®_rule, regd); @@ -1279,17 +1253,17 @@ static void handle_channel_custom(struct wiphy *wiphy, (int) MBM_TO_DBM(power_rule->max_eirp); } -static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, +static void handle_band_custom(struct wiphy *wiphy, + struct ieee80211_supported_band *sband, const struct ieee80211_regdomain *regd) { unsigned int i; - struct ieee80211_supported_band *sband; - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; + if (!sband) + return; for (i = 0; i < sband->n_channels; i++) - handle_channel_custom(wiphy, band, i, regd); + handle_channel_custom(wiphy, &sband->channels[i], regd); } /* Used by drivers prior to wiphy registration */ @@ -1303,7 +1277,7 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; - handle_band_custom(wiphy, band, regd); + handle_band_custom(wiphy, wiphy->bands[band], regd); bands_set++; } mutex_unlock(®_mutex); @@ -1537,7 +1511,8 @@ static void reg_process_hint(struct regulatory_request *reg_request, { struct wiphy *wiphy = NULL; - BUG_ON(!reg_request->alpha2); + if (WARN_ON(!reg_request->alpha2)) + return; if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); @@ -1678,7 +1653,8 @@ int regulatory_hint_user(const char *alpha2, { struct regulatory_request *request; - BUG_ON(!alpha2); + if (WARN_ON(!alpha2)) + return -EINVAL; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) @@ -1700,8 +1676,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) { struct regulatory_request *request; - BUG_ON(!alpha2); - BUG_ON(!wiphy); + if (WARN_ON(!alpha2 || !wiphy)) + return -EINVAL; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) @@ -2239,12 +2215,14 @@ int set_regdom(const struct ieee80211_regdomain *rd) reg_set_request_processed(); kfree(rd); - mutex_unlock(®_mutex); - return r; + goto out; } /* This would make this whole thing pointless */ - BUG_ON(!last_request->intersect && rd != cfg80211_regdomain); + if (WARN_ON(!last_request->intersect && rd != cfg80211_regdomain)) { + r = -EINVAL; + goto out; + } /* update all wiphys now with the new established regulatory domain */ update_all_wiphy_regulatory(last_request->initiator); @@ -2255,6 +2233,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) reg_set_request_processed(); + out: mutex_unlock(®_mutex); return r; From 11cff96c0660e1e361d60ab90514ffbbfbaf7c03 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 18:56:41 +0100 Subject: [PATCH 131/478] regulatory: simplify restore_regulatory_settings Use list_splice_tail_init() and also simplify the locking. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 57812dfc297b..87daba95639c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1878,9 +1878,6 @@ static void restore_regulatory_settings(bool reset_user) restore_custom_reg_settings(&rdev->wiphy); } - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); - regulatory_hint_core(world_alpha2); /* @@ -1891,18 +1888,8 @@ static void restore_regulatory_settings(bool reset_user) if (is_an_alpha2(alpha2)) regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); - if (list_empty(&tmp_reg_req_list)) - return; - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - spin_lock(®_requests_lock); - list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { - REG_DBG_PRINT("Adding request for country %c%c back into the queue\n", - reg_request->alpha2[0], reg_request->alpha2[1]); - list_move_tail(®_request->list, ®_requests_list); - } + list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); spin_unlock(®_requests_lock); mutex_unlock(®_mutex); From d4f2c8819a51f31ef0f929574ce8d80b323c6a76 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 18:59:58 +0100 Subject: [PATCH 132/478] regulatory: remove redundant isalpha() check toupper() only modifies lower-case letters, so the isalpha() check is redundant; remove it. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 87daba95639c..946cd5320ef9 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1614,10 +1614,8 @@ static void reg_todo(struct work_struct *work) static void queue_regulatory_request(struct regulatory_request *request) { - if (isalpha(request->alpha2[0])) - request->alpha2[0] = toupper(request->alpha2[0]); - if (isalpha(request->alpha2[1])) - request->alpha2[1] = toupper(request->alpha2[1]); + request->alpha2[0] = toupper(request->alpha2[0]); + request->alpha2[1] = toupper(request->alpha2[1]); spin_lock(®_requests_lock); list_add_tail(&request->list, ®_requests_list); From 0ba857ad671aa8aa2a1276aa7a0c79abd0575146 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 19:12:02 +0100 Subject: [PATCH 133/478] regulatory: remove useless warning Even if it never happens and is hidden behind the debug config option, it's completely useless: the calltrace will only show module loading. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 946cd5320ef9..b63bdf899a75 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2322,10 +2322,6 @@ int __init regulatory_init(void) * errors as non-fatal. */ pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); -#ifdef CONFIG_CFG80211_REG_DEBUG - /* We want to find out exactly why when debugging */ - WARN_ON(err); -#endif } /* From 5d885b999c68283c493cf00cc020260d38951ae6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 00:14:17 +0100 Subject: [PATCH 134/478] regulatory: simplify freq_reg_info_regd The function itself has dual-purpose: it can retrieve from a given regdomain or from the globally installed one. Change it to have a single purpose only: to look up from a given regdomain. Pass the correct regdomain in the freq_reg_info() function instead. This also changes the locking rules for it, no locking is required any more. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b63bdf899a75..75239789213a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -678,28 +678,15 @@ static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule, - const struct ieee80211_regdomain *custom_regd) + const struct ieee80211_regdomain *regd) { int i; bool band_rule_found = false; - const struct ieee80211_regdomain *regd; bool bw_fits = false; if (!desired_bw_khz) desired_bw_khz = MHZ_TO_KHZ(20); - regd = custom_regd ? custom_regd : cfg80211_regdomain; - - /* - * Follow the driver's regulatory domain, if present, unless a country - * IE has been processed or a user wants to help complaince further - */ - if (!custom_regd && - last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - last_request->initiator != NL80211_REGDOM_SET_BY_USER && - wiphy->regd) - regd = wiphy->regd; - if (!regd) return -EINVAL; @@ -735,10 +722,24 @@ static int freq_reg_info_regd(struct wiphy *wiphy, int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule) { + const struct ieee80211_regdomain *regd; + + assert_reg_lock(); assert_cfg80211_lock(); + /* + * Follow the driver's regulatory domain, if present, unless a country + * IE has been processed or a user wants to help complaince further + */ + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + last_request->initiator != NL80211_REGDOM_SET_BY_USER && + wiphy->regd) + regd = wiphy->regd; + else + regd = cfg80211_regdomain; + return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, - reg_rule, NULL); + reg_rule, regd); } EXPORT_SYMBOL(freq_reg_info); From e8da2bb4fe9ecb888f44714e2624c7d4268ac09d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 23:00:08 +0100 Subject: [PATCH 135/478] regulatory: clarify locking rules and assertions Many places that currently check that cfg80211_mutex is held don't actually use any data protected by it. The functions that need to hold the cfg80211_mutex are the ones using the cfg80211_regdomain variable, so add the lock assertion to those and clarify this in the comments. The reason for this is that nl80211 uses the regdom without being able to hold reg_mutex. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 75239789213a..603a01911a50 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -94,14 +94,14 @@ static struct device_type reg_device_type = { /* * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no - * information to give us an alpha2 + * information to give us an alpha2. + * Protected by the cfg80211_mutex. */ const struct ieee80211_regdomain *cfg80211_regdomain; /* * Protects static reg.c components: * - cfg80211_world_regdom - * - cfg80211_regdom * - last_request * - reg_num_devs_support_basehint */ @@ -185,6 +185,9 @@ MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); static void reset_regdomains(bool full_reset) { + assert_cfg80211_lock(); + assert_reg_lock(); + /* avoid freeing static information or freeing something twice */ if (cfg80211_regdomain == cfg80211_world_regdom) cfg80211_regdomain = NULL; @@ -215,6 +218,9 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { WARN_ON(!last_request); + assert_cfg80211_lock(); + assert_reg_lock(); + reset_regdomains(false); cfg80211_world_regdom = rd; @@ -422,8 +428,6 @@ static int call_crda(const char *alpha2) /* Used by nl80211 before kmalloc'ing our regulatory domain */ bool reg_is_valid_request(const char *alpha2) { - assert_cfg80211_lock(); - if (!last_request) return false; @@ -915,8 +919,6 @@ bool reg_last_request_cell_base(void) { bool val; - assert_cfg80211_lock(); - mutex_lock(®_mutex); val = reg_request_cell_base(last_request); mutex_unlock(®_mutex); @@ -999,8 +1001,6 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, bool channel_changed = false; struct ieee80211_channel chan_before; - assert_cfg80211_lock(); - sband = wiphy->bands[reg_beacon->chan.band]; chan = &sband->channels[chan_idx]; @@ -1042,8 +1042,6 @@ static void wiphy_update_new_beacon(struct wiphy *wiphy, unsigned int i; struct ieee80211_supported_band *sband; - assert_cfg80211_lock(); - if (!wiphy->bands[reg_beacon->chan.band]) return; @@ -1062,8 +1060,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) struct ieee80211_supported_band *sband; struct reg_beacon *reg_beacon; - assert_cfg80211_lock(); - list_for_each_entry(reg_beacon, ®_beacon_list, list) { if (!wiphy->bands[reg_beacon->chan.band]) continue; @@ -1075,6 +1071,8 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) static bool reg_is_world_roaming(struct wiphy *wiphy) { + assert_cfg80211_lock(); + if (is_world_regdom(cfg80211_regdomain->alpha2) || (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) return true; @@ -1116,8 +1114,6 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; unsigned int i; - assert_cfg80211_lock(); - if (!is_ht40_allowed(channel)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; @@ -1180,6 +1176,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, { enum ieee80211_band band; + assert_cfg80211_lock(); assert_reg_lock(); if (ignore_reg_update(wiphy, initiator)) @@ -1299,8 +1296,6 @@ get_reg_request_treatment(struct wiphy *wiphy, { struct wiphy *last_wiphy = NULL; - assert_cfg80211_lock(); - /* All initial requests are respected */ if (!last_request) return REG_REQ_OK; @@ -2246,8 +2241,6 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) void wiphy_regulatory_register(struct wiphy *wiphy) { - assert_cfg80211_lock(); - mutex_lock(®_mutex); if (!reg_dev_ignore_cell_hint(wiphy)) @@ -2263,8 +2256,6 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) { struct wiphy *request_wiphy = NULL; - assert_cfg80211_lock(); - mutex_lock(®_mutex); if (!reg_dev_ignore_cell_hint(wiphy)) From 540f6f2cc545da9ae2baa9faa3152fc550bedb57 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 00:19:24 +0100 Subject: [PATCH 136/478] regulatory: remove locking from wiphy_apply_custom_regulatory wiphy_apply_custom_regulatory() doesn't have to hold the regulatory mutex as it only modifies the given wiphy with the given regulatory domain, it doesn't access any global regulatory data. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 603a01911a50..752729ecd701 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1225,8 +1225,6 @@ static void handle_channel_custom(struct wiphy *wiphy, const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; - assert_reg_lock(); - r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz, ®_rule, regd); @@ -1271,14 +1269,12 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, enum ieee80211_band band; unsigned int bands_set = 0; - mutex_lock(®_mutex); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; handle_band_custom(wiphy, wiphy->bands[band], regd); bands_set++; } - mutex_unlock(®_mutex); /* * no point in calling this if it won't have any effect From 6913b49a5071064f49f7a74b432286fa735f7612 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 00:48:59 +0100 Subject: [PATCH 137/478] regulatory: fix reg_is_valid_request handling There's a bug with the world regulatory domain, it can be updated any time which is different from all other regdomains that can only be updated once after a request for them. Fix this by adding a check for "processed" to the reg_is_valid_request() function and clear that when doing a request. While looking at this I also found another locking bug, last_request is protected by the reg_mutex not the cfg80211_mutex so the code in nl80211 is racy. Remove that code as it only tries to prevent an allocation in an error case, which isn't necessary. Then the function can also become static and locking in nl80211 can have a smaller scope. Also change __set_regdom() to do the checks earlier and not different for world/other regdomains. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 18 ++++++------------ net/wireless/reg.c | 21 +++++++++++---------- net/wireless/reg.h | 1 - 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9c2c91845be7..b387deaf1132 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4265,21 +4265,12 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - mutex_lock(&cfg80211_mutex); - - if (!reg_is_valid_request(alpha2)) { - r = -EINVAL; - goto bad_reg; - } - size_of_regd = sizeof(struct ieee80211_regdomain) + num_rules * sizeof(struct ieee80211_reg_rule); rd = kzalloc(size_of_regd, GFP_KERNEL); - if (!rd) { - r = -ENOMEM; - goto bad_reg; - } + if (!rd) + return -ENOMEM; rd->n_reg_rules = num_rules; rd->alpha2[0] = alpha2[0]; @@ -4309,11 +4300,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) } } + mutex_lock(&cfg80211_mutex); + r = set_regdom(rd); + /* set_regdom took ownership */ rd = NULL; + mutex_unlock(&cfg80211_mutex); bad_reg: - mutex_unlock(&cfg80211_mutex); kfree(rd); return r; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 752729ecd701..b3f94c957d1d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -425,12 +425,16 @@ static int call_crda(const char *alpha2) return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); } -/* Used by nl80211 before kmalloc'ing our regulatory domain */ -bool reg_is_valid_request(const char *alpha2) +static bool reg_is_valid_request(const char *alpha2) { + assert_reg_lock(); + if (!last_request) return false; + if (last_request->processed) + return false; + return alpha2_equal(last_request->alpha2, alpha2); } @@ -1470,6 +1474,7 @@ new_request: last_request = pending_request; last_request->intersect = intersect; + last_request->processed = false; pending_request = NULL; @@ -2060,11 +2065,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *intersected_rd = NULL; struct wiphy *request_wiphy; + /* Some basic sanity checks first */ + if (!reg_is_valid_request(rd->alpha2)) + return -EINVAL; + if (is_world_regdom(rd->alpha2)) { - if (WARN_ON(!reg_is_valid_request(rd->alpha2))) - return -EINVAL; update_world_regdomain(rd); return 0; } @@ -2073,9 +2080,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) !is_unknown_alpha2(rd->alpha2)) return -EINVAL; - if (!last_request) - return -EINVAL; - /* * Lets only bother proceeding on the same alpha2 if the current * rd is non static (it means CRDA was present and was used last) @@ -2097,9 +2101,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * internal EEPROM data */ - if (WARN_ON(!reg_is_valid_request(rd->alpha2))) - return -EINVAL; - if (!is_valid_rd(rd)) { pr_err("Invalid regulatory domain detected:\n"); print_regdomain_info(rd); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 37891e813a74..d391b50d2829 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -19,7 +19,6 @@ extern const struct ieee80211_regdomain *cfg80211_regdomain; bool is_world_regdom(const char *alpha2); -bool reg_is_valid_request(const char *alpha2); bool reg_supported_dfs_region(u8 dfs_region); int regulatory_hint_user(const char *alpha2, From fe7ef5e9ba0c60bab01390493a4c6790f7b093af Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 15:07:34 +0100 Subject: [PATCH 138/478] regulatory: remove handling of channel bandwidth The channel bandwidth handling isn't really quite right, it assumes that a 40 MHz channel is really two 20 MHz channels, which isn't strictly true. This is the way the regulatory database handling is defined right now though so remove the logic to handle other channel widths. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/regd.c | 7 +-- .../net/wireless/brcm80211/brcmsmac/channel.c | 2 +- drivers/net/wireless/rtlwifi/regd.c | 9 ++-- include/net/cfg80211.h | 8 +--- net/wireless/reg.c | 44 ++++++------------- 5 files changed, 21 insertions(+), 49 deletions(-) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index d81698015bf7..8ae58c404c67 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -195,7 +195,6 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; - u32 bandwidth = 0; int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { @@ -216,7 +215,6 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { r = freq_reg_info(wiphy, ch->center_freq, - bandwidth, ®_rule); if (r) continue; @@ -254,7 +252,6 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; - u32 bandwidth = 0; int r; sband = wiphy->bands[IEEE80211_BAND_2GHZ]; @@ -283,7 +280,7 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) @@ -291,7 +288,7 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, } ch = &sband->channels[12]; /* CH 13 */ - r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index a90b72202ec5..30272519d795 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -686,7 +686,7 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { ret = freq_reg_info(wiphy, ch->center_freq, - 0, &rule); + &rule); if (ret) continue; diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c index c1608cddc529..be55dc9167f0 100644 --- a/drivers/net/wireless/rtlwifi/regd.c +++ b/drivers/net/wireless/rtlwifi/regd.c @@ -158,7 +158,6 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; - u32 bandwidth = 0; int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { @@ -174,8 +173,7 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, (ch->flags & IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - r = freq_reg_info(wiphy, ch->center_freq, - bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (r) continue; @@ -211,7 +209,6 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; - u32 bandwidth = 0; int r; if (!wiphy->bands[IEEE80211_BAND_2GHZ]) @@ -240,7 +237,7 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) @@ -248,7 +245,7 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, } ch = &sband->channels[12]; /* CH 13 */ - r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8e6a6b73b9c9..c222e5fbf53a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2938,10 +2938,6 @@ extern void wiphy_apply_custom_regulatory( * freq_reg_info - get regulatory information for the given frequency * @wiphy: the wiphy for which we want to process this rule for * @center_freq: Frequency in KHz for which we want regulatory information for - * @desired_bw_khz: the desired max bandwidth you want to use per - * channel. Note that this is still 20 MHz if you want to use HT40 - * as HT40 makes use of two channels for its 40 MHz width bandwidth. - * If set to 0 we'll assume you want the standard 20 MHz. * @reg_rule: the regulatory rule which we have for this frequency * * Use this function to get the regulatory rule for a specific frequency on @@ -2956,9 +2952,7 @@ extern void wiphy_apply_custom_regulatory( * freq_in_rule_band() for our current definition of a band -- this is purely * subjective and right now its 802.11 specific. */ -extern int freq_reg_info(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, +extern int freq_reg_info(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule **reg_rule); /* diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b3f94c957d1d..2e38b47939a3 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -480,8 +480,7 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) } static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, - u32 center_freq_khz, - u32 bw_khz) + u32 center_freq_khz, u32 bw_khz) { u32 start_freq_khz, end_freq_khz; @@ -682,9 +681,7 @@ static u32 map_regdom_flags(u32 rd_flags) return channel_flags; } -static int freq_reg_info_regd(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, +static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule **reg_rule, const struct ieee80211_regdomain *regd) { @@ -692,9 +689,6 @@ static int freq_reg_info_regd(struct wiphy *wiphy, bool band_rule_found = false; bool bw_fits = false; - if (!desired_bw_khz) - desired_bw_khz = MHZ_TO_KHZ(20); - if (!regd) return -EINVAL; @@ -713,7 +707,7 @@ static int freq_reg_info_regd(struct wiphy *wiphy, if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); - bw_fits = reg_does_bw_fit(fr, center_freq, desired_bw_khz); + bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); if (band_rule_found && bw_fits) { *reg_rule = rr; @@ -727,7 +721,7 @@ static int freq_reg_info_regd(struct wiphy *wiphy, return -EINVAL; } -int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, +int freq_reg_info(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule **reg_rule) { const struct ieee80211_regdomain *regd; @@ -746,8 +740,7 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, else regd = cfg80211_regdomain; - return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, - reg_rule, regd); + return freq_reg_info_regd(wiphy, center_freq, reg_rule, regd); } EXPORT_SYMBOL(freq_reg_info); @@ -770,7 +763,6 @@ static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) } static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, - u32 desired_bw_khz, const struct ieee80211_reg_rule *reg_rule) { const struct ieee80211_power_rule *power_rule; @@ -785,8 +777,8 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, else snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); - REG_DBG_PRINT("Updating information on frequency %d MHz for a %d MHz width channel with regulatory rule:\n", - chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); + REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n", + chan->center_freq); REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, @@ -795,7 +787,6 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, } #else static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, - u32 desired_bw_khz, const struct ieee80211_reg_rule *reg_rule) { return; @@ -805,11 +796,7 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, /* * Note that right now we assume the desired channel bandwidth * is always 20 MHz for each individual channel (HT40 uses 20 MHz - * per channel, the primary and the extension channel). To support - * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a - * new ieee80211_channel.target_bw and re run the regulatory check - * on the wiphy with the target_bw specified. Then we can simply use - * that below for the desired_bw_khz below. + * per channel, the primary and the extension channel). */ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, @@ -817,7 +804,6 @@ static void handle_channel(struct wiphy *wiphy, { int r; u32 flags, bw_flags = 0; - u32 desired_bw_khz = MHZ_TO_KHZ(20); const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; @@ -829,8 +815,7 @@ static void handle_channel(struct wiphy *wiphy, flags = chan->orig_flags; - r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, ®_rule); + r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), ®_rule); if (r) { /* * We will disable all channels that do not match our @@ -851,7 +836,7 @@ static void handle_channel(struct wiphy *wiphy, return; } - chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); + chan_reg_rule_print_dbg(chan, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; @@ -1223,23 +1208,22 @@ static void handle_channel_custom(struct wiphy *wiphy, const struct ieee80211_regdomain *regd) { int r; - u32 desired_bw_khz = MHZ_TO_KHZ(20); u32 bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, ®_rule, regd); + ®_rule, regd); if (r) { - REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits a %d MHz wide channel\n", - chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); + REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", + chan->center_freq); chan->flags = IEEE80211_CHAN_DISABLED; return; } - chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); + chan_reg_rule_print_dbg(chan, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; From 379b82f4c9dc6e67bf61aa61b096c06a2f320f60 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 15:44:07 +0100 Subject: [PATCH 139/478] regulatory: pass new regdomain to reset function Instead of assigning after calling the function do it inside the function. This will later avoid a period of time where the pointer is NULL. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2e38b47939a3..35541d6d4145 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -183,7 +183,8 @@ static char user_alpha2[2]; module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); -static void reset_regdomains(bool full_reset) +static void reset_regdomains(bool full_reset, + const struct ieee80211_regdomain *new_regdom) { assert_cfg80211_lock(); assert_reg_lock(); @@ -200,7 +201,7 @@ static void reset_regdomains(bool full_reset) kfree(cfg80211_world_regdom); cfg80211_world_regdom = &world_regdom; - cfg80211_regdomain = NULL; + cfg80211_regdomain = new_regdom; if (!full_reset) return; @@ -221,10 +222,9 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) assert_cfg80211_lock(); assert_reg_lock(); - reset_regdomains(false); + reset_regdomains(false, rd); cfg80211_world_regdom = rd; - cfg80211_regdomain = rd; } bool is_world_regdom(const char *alpha2) @@ -1817,7 +1817,7 @@ static void restore_regulatory_settings(bool reset_user) mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true); + reset_regdomains(true, cfg80211_world_regdom); restore_alpha2(alpha2, reset_user); /* @@ -1848,9 +1848,8 @@ static void restore_regulatory_settings(bool reset_user) } /* First restore to the basic regulatory settings */ - cfg80211_regdomain = cfg80211_world_regdom; - world_alpha2[0] = cfg80211_regdomain->alpha2[0]; - world_alpha2[1] = cfg80211_regdomain->alpha2[1]; + world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; + world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) @@ -2101,8 +2100,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!last_request->intersect) { if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { - reset_regdomains(false); - cfg80211_regdomain = rd; + reset_regdomains(false, rd); return 0; } @@ -2123,8 +2121,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return PTR_ERR(regd); request_wiphy->regd = regd; - reset_regdomains(false); - cfg80211_regdomain = rd; + reset_regdomains(false, rd); return 0; } @@ -2147,8 +2144,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) rd = NULL; - reset_regdomains(false); - cfg80211_regdomain = intersected_rd; + reset_regdomains(false, intersected_rd); return 0; } @@ -2319,7 +2315,7 @@ void regulatory_exit(void) /* Lock to suppress warnings */ mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true); + reset_regdomains(true, NULL); mutex_unlock(&cfg80211_mutex); mutex_unlock(®_mutex); From 458f4f9e960b9a3b674c4b87d996eef186b1fe83 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 15:47:38 +0100 Subject: [PATCH 140/478] regulatory: use RCU to protect global and wiphy regdomains To simplify the locking and not require cfg80211_mutex (which nl80211 uses to access the global regdomain) and also to make it possible for drivers to access their wiphy->regd safely, use RCU to protect these pointers. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- include/net/regulatory.h | 2 + net/wireless/nl80211.c | 35 ++++++------ net/wireless/reg.c | 113 +++++++++++++++++++++++---------------- net/wireless/reg.h | 2 +- 5 files changed, 88 insertions(+), 66 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c222e5fbf53a..f3be58a29642 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2369,7 +2369,7 @@ struct wiphy { /* fields below are read-only, assigned by cfg80211 */ - const struct ieee80211_regdomain *regd; + const struct ieee80211_regdomain __rcu *regd; /* the item in /sys/class/ieee80211/ points to this, * you need use set_wiphy_dev() (see below) */ diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 7dcaa2794fde..96b0f07cb85b 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -18,6 +18,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include /** * enum environment_cap - Environment parsed from country IE @@ -101,6 +102,7 @@ struct ieee80211_reg_rule { }; struct ieee80211_regdomain { + struct rcu_head rcu_head; u32 n_reg_rules; char alpha2[2]; u8 dfs_region; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b387deaf1132..b3cf7cc0d4a1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3787,12 +3787,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) * window between nl80211_init() and regulatory_init(), if that is * even possible. */ - mutex_lock(&cfg80211_mutex); - if (unlikely(!cfg80211_regdomain)) { - mutex_unlock(&cfg80211_mutex); + if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) return -EINPROGRESS; - } - mutex_unlock(&cfg80211_mutex); if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) return -EINVAL; @@ -4152,6 +4148,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) { + const struct ieee80211_regdomain *regdom; struct sk_buff *msg; void *hdr = NULL; struct nlattr *nl_reg_rules; @@ -4174,35 +4171,36 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) if (!hdr) goto put_failure; - if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, - cfg80211_regdomain->alpha2) || - (cfg80211_regdomain->dfs_region && - nla_put_u8(msg, NL80211_ATTR_DFS_REGION, - cfg80211_regdomain->dfs_region))) - goto nla_put_failure; - if (reg_last_request_cell_base() && nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, NL80211_USER_REG_HINT_CELL_BASE)) goto nla_put_failure; + rcu_read_lock(); + regdom = rcu_dereference(cfg80211_regdomain); + + if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) || + (regdom->dfs_region && + nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) + goto nla_put_failure_rcu; + nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); if (!nl_reg_rules) - goto nla_put_failure; + goto nla_put_failure_rcu; - for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { + for (i = 0; i < regdom->n_reg_rules; i++) { struct nlattr *nl_reg_rule; const struct ieee80211_reg_rule *reg_rule; const struct ieee80211_freq_range *freq_range; const struct ieee80211_power_rule *power_rule; - reg_rule = &cfg80211_regdomain->reg_rules[i]; + reg_rule = ®dom->reg_rules[i]; freq_range = ®_rule->freq_range; power_rule = ®_rule->power_rule; nl_reg_rule = nla_nest_start(msg, i); if (!nl_reg_rule) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS, reg_rule->flags) || @@ -4216,10 +4214,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) power_rule->max_antenna_gain) || nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, power_rule->max_eirp)) - goto nla_put_failure; + goto nla_put_failure_rcu; nla_nest_end(msg, nl_reg_rule); } + rcu_read_unlock(); nla_nest_end(msg, nl_reg_rules); @@ -4227,6 +4226,8 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) err = genlmsg_reply(msg, info); goto out; +nla_put_failure_rcu: + rcu_read_unlock(); nla_put_failure: genlmsg_cancel(msg, hdr); put_failure: diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 35541d6d4145..9b64b201cdf1 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -95,15 +95,15 @@ static struct device_type reg_device_type = { * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no * information to give us an alpha2. - * Protected by the cfg80211_mutex. */ -const struct ieee80211_regdomain *cfg80211_regdomain; +const struct ieee80211_regdomain __rcu *cfg80211_regdomain; /* * Protects static reg.c components: - * - cfg80211_world_regdom - * - last_request - * - reg_num_devs_support_basehint + * - cfg80211_regdomain (if not used with RCU) + * - cfg80211_world_regdom + * - last_request + * - reg_num_devs_support_basehint */ static DEFINE_MUTEX(reg_mutex); @@ -118,6 +118,25 @@ static inline void assert_reg_lock(void) lockdep_assert_held(®_mutex); } +static const struct ieee80211_regdomain *get_cfg80211_regdom(void) +{ + return rcu_dereference_protected(cfg80211_regdomain, + lockdep_is_held(®_mutex)); +} + +static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) +{ + return rcu_dereference_protected(wiphy->regd, + lockdep_is_held(®_mutex)); +} + +static void rcu_free_regdom(const struct ieee80211_regdomain *r) +{ + if (!r) + return; + kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); +} + /* Used to queue up regulatory hints */ static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; @@ -186,22 +205,25 @@ MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); static void reset_regdomains(bool full_reset, const struct ieee80211_regdomain *new_regdom) { - assert_cfg80211_lock(); + const struct ieee80211_regdomain *r; + assert_reg_lock(); + r = get_cfg80211_regdom(); + /* avoid freeing static information or freeing something twice */ - if (cfg80211_regdomain == cfg80211_world_regdom) - cfg80211_regdomain = NULL; + if (r == cfg80211_world_regdom) + r = NULL; if (cfg80211_world_regdom == &world_regdom) cfg80211_world_regdom = NULL; - if (cfg80211_regdomain == &world_regdom) - cfg80211_regdomain = NULL; + if (r == &world_regdom) + r = NULL; - kfree(cfg80211_regdomain); - kfree(cfg80211_world_regdom); + rcu_free_regdom(r); + rcu_free_regdom(cfg80211_world_regdom); cfg80211_world_regdom = &world_regdom; - cfg80211_regdomain = new_regdom; + rcu_assign_pointer(cfg80211_regdomain, new_regdom); if (!full_reset) return; @@ -219,7 +241,6 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { WARN_ON(!last_request); - assert_cfg80211_lock(); assert_reg_lock(); reset_regdomains(false, rd); @@ -280,11 +301,11 @@ static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) static bool regdom_changes(const char *alpha2) { - assert_cfg80211_lock(); + const struct ieee80211_regdomain *r = get_cfg80211_regdom(); - if (!cfg80211_regdomain) + if (!r) return true; - return !alpha2_equal(cfg80211_regdomain->alpha2, alpha2); + return !alpha2_equal(r->alpha2, alpha2); } /* @@ -727,7 +748,6 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_regdomain *regd; assert_reg_lock(); - assert_cfg80211_lock(); /* * Follow the driver's regulatory domain, if present, unless a country @@ -736,9 +756,9 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && last_request->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) - regd = wiphy->regd; + regd = get_wiphy_regdom(wiphy); else - regd = cfg80211_regdomain; + regd = get_cfg80211_regdom(); return freq_reg_info_regd(wiphy, center_freq, reg_rule, regd); } @@ -809,8 +829,6 @@ static void handle_channel(struct wiphy *wiphy, const struct ieee80211_freq_range *freq_range = NULL; struct wiphy *request_wiphy = NULL; - assert_cfg80211_lock(); - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); flags = chan->orig_flags; @@ -1060,15 +1078,17 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) static bool reg_is_world_roaming(struct wiphy *wiphy) { - assert_cfg80211_lock(); + const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); + const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); - if (is_world_regdom(cfg80211_regdomain->alpha2) || - (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) + if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) return true; + if (last_request && last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) return true; + return false; } @@ -1165,13 +1185,12 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, { enum ieee80211_band band; - assert_cfg80211_lock(); assert_reg_lock(); if (ignore_reg_update(wiphy, initiator)) return; - last_request->dfs_region = cfg80211_regdomain->dfs_region; + last_request->dfs_region = get_cfg80211_regdom()->dfs_region; for (band = 0; band < IEEE80211_NUM_BANDS; band++) handle_band(wiphy, initiator, wiphy->bands[band]); @@ -1188,6 +1207,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) struct cfg80211_registered_device *rdev; struct wiphy *wiphy; + assert_cfg80211_lock(); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { wiphy = &rdev->wiphy; wiphy_update_regulatory(wiphy, initiator); @@ -1402,7 +1423,7 @@ static void reg_set_request_processed(void) * * Returns one of the different reg request treatment values. * - * Caller must hold &cfg80211_mutex and ®_mutex + * Caller must hold ®_mutex */ static enum reg_request_treatment __regulatory_hint(struct wiphy *wiphy, @@ -1412,20 +1433,18 @@ __regulatory_hint(struct wiphy *wiphy, bool intersect = false; enum reg_request_treatment treatment; - assert_cfg80211_lock(); - treatment = get_reg_request_treatment(wiphy, pending_request); switch (treatment) { case REG_REQ_INTERSECT: if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - regd = reg_copy_regd(cfg80211_regdomain); + regd = reg_copy_regd(get_cfg80211_regdom()); if (IS_ERR(regd)) { kfree(pending_request); return PTR_ERR(regd); } - wiphy->regd = regd; + rcu_assign_pointer(wiphy->regd, regd); } intersect = true; break; @@ -1439,13 +1458,13 @@ __regulatory_hint(struct wiphy *wiphy, */ if (treatment == REG_REQ_ALREADY_SET && pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - regd = reg_copy_regd(cfg80211_regdomain); + regd = reg_copy_regd(get_cfg80211_regdom()); if (IS_ERR(regd)) { kfree(pending_request); return REG_REQ_IGNORE; } treatment = REG_REQ_ALREADY_SET; - wiphy->regd = regd; + rcu_assign_pointer(wiphy->regd, regd); goto new_request; } kfree(pending_request); @@ -2051,6 +2070,8 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Some basic sanity checks first */ + assert_reg_lock(); + if (!reg_is_valid_request(rd->alpha2)) return -EINVAL; @@ -2120,7 +2141,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (IS_ERR(regd)) return PTR_ERR(regd); - request_wiphy->regd = regd; + rcu_assign_pointer(request_wiphy->regd, regd); reset_regdomains(false, rd); return 0; } @@ -2128,7 +2149,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Intersection requires a bit more work */ if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - intersected_rd = regdom_intersect(rd, cfg80211_regdomain); + intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); if (!intersected_rd) return -EINVAL; @@ -2138,7 +2159,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * domain we keep it for its private use */ if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) - request_wiphy->regd = rd; + rcu_assign_pointer(request_wiphy->regd, rd); else kfree(rd); @@ -2156,14 +2177,12 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* * Use this call to set the current regulatory domain. Conflicts with * multiple drivers can be ironed out later. Caller must've already - * kmalloc'd the rd structure. Caller must hold cfg80211_mutex + * kmalloc'd the rd structure. */ int set_regdom(const struct ieee80211_regdomain *rd) { int r; - assert_cfg80211_lock(); - mutex_lock(®_mutex); /* Note that this doesn't update the wiphys, this is done below */ @@ -2177,7 +2196,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) } /* This would make this whole thing pointless */ - if (WARN_ON(!last_request->intersect && rd != cfg80211_regdomain)) { + if (WARN_ON(!last_request->intersect && + rd != get_cfg80211_regdom())) { r = -EINVAL; goto out; } @@ -2185,7 +2205,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) /* update all wiphys now with the new established regulatory domain */ update_all_wiphy_regulatory(last_request->initiator); - print_regdomain(cfg80211_regdomain); + print_regdomain(get_cfg80211_regdom()); nl80211_send_reg_change_event(last_request); @@ -2238,7 +2258,8 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint--; - kfree(wiphy->regd); + rcu_free_regdom(get_wiphy_regdom(wiphy)); + rcu_assign_pointer(wiphy->regd, NULL); if (last_request) request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); @@ -2273,13 +2294,13 @@ int __init regulatory_init(void) reg_regdb_size_check(); - cfg80211_regdomain = cfg80211_world_regdom; + rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom); user_alpha2[0] = '9'; user_alpha2[1] = '7'; /* We always try to get an update for the static regdomain */ - err = regulatory_hint_core(cfg80211_regdomain->alpha2); + err = regulatory_hint_core(cfg80211_world_regdom->alpha2); if (err) { if (err == -ENOMEM) return err; @@ -2313,10 +2334,8 @@ void regulatory_exit(void) cancel_delayed_work_sync(®_timeout); /* Lock to suppress warnings */ - mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); reset_regdomains(true, NULL); - mutex_unlock(&cfg80211_mutex); mutex_unlock(®_mutex); dev_set_uevent_suppress(®_pdev->dev, true); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index d391b50d2829..af2d5f8a5d82 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -16,7 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -extern const struct ieee80211_regdomain *cfg80211_regdomain; +extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; bool is_world_regdom(const char *alpha2); bool reg_supported_dfs_region(u8 dfs_region); From c492db370c17c428a0a58d3673294d4e99634b7d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 16:29:25 +0100 Subject: [PATCH 141/478] regulatory: use RCU to protect last_request This will allow making freq_reg_info() lock-free. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/regulatory.h | 2 + net/wireless/reg.c | 199 +++++++++++++++++++++------------------ 2 files changed, 111 insertions(+), 90 deletions(-) diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 96b0f07cb85b..f17ed590d64a 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -36,6 +36,7 @@ enum environment_cap { /** * struct regulatory_request - used to keep track of regulatory requests * + * @rcu_head: RCU head struct used to free the request * @wiphy_idx: this is set if this request's initiator is * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This * can be used by the wireless core to deal with conflicts @@ -73,6 +74,7 @@ enum environment_cap { * @list: used to insert into the reg_requests_list linked list */ struct regulatory_request { + struct rcu_head rcu_head; int wiphy_idx; enum nl80211_reg_initiator initiator; enum nl80211_user_reg_hint_type user_reg_hint_type; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9b64b201cdf1..2a7c3adf902f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -82,7 +82,8 @@ static struct regulatory_request core_request_world = { }; /* Receipt of information from last regulatory request */ -static struct regulatory_request *last_request = &core_request_world; +static struct regulatory_request __rcu *last_request = + (void __rcu *)&core_request_world; /* To trigger userspace events */ static struct platform_device *reg_pdev; @@ -102,7 +103,7 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain; * Protects static reg.c components: * - cfg80211_regdomain (if not used with RCU) * - cfg80211_world_regdom - * - last_request + * - last_request (if not used with RCU) * - reg_num_devs_support_basehint */ static DEFINE_MUTEX(reg_mutex); @@ -137,6 +138,12 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r) kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); } +static struct regulatory_request *get_last_request(void) +{ + return rcu_dereference_protected(last_request, + lockdep_is_held(®_mutex)); +} + /* Used to queue up regulatory hints */ static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; @@ -206,6 +213,7 @@ static void reset_regdomains(bool full_reset, const struct ieee80211_regdomain *new_regdom) { const struct ieee80211_regdomain *r; + struct regulatory_request *lr; assert_reg_lock(); @@ -228,9 +236,10 @@ static void reset_regdomains(bool full_reset, if (!full_reset) return; - if (last_request != &core_request_world) - kfree(last_request); - last_request = &core_request_world; + lr = get_last_request(); + if (lr != &core_request_world && lr) + kfree_rcu(lr, rcu_head); + rcu_assign_pointer(last_request, &core_request_world); } /* @@ -239,9 +248,11 @@ static void reset_regdomains(bool full_reset, */ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { - WARN_ON(!last_request); + struct regulatory_request *lr; - assert_reg_lock(); + lr = get_last_request(); + + WARN_ON(!lr); reset_regdomains(false, rd); @@ -448,15 +459,12 @@ static int call_crda(const char *alpha2) static bool reg_is_valid_request(const char *alpha2) { - assert_reg_lock(); + struct regulatory_request *lr = get_last_request(); - if (!last_request) + if (!lr || lr->processed) return false; - if (last_request->processed) - return false; - - return alpha2_equal(last_request->alpha2, alpha2); + return alpha2_equal(lr->alpha2, alpha2); } /* Sanity check on a regulatory rule */ @@ -746,15 +754,14 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule **reg_rule) { const struct ieee80211_regdomain *regd; - - assert_reg_lock(); + struct regulatory_request *lr = get_last_request(); /* * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help complaince further */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - last_request->initiator != NL80211_REGDOM_SET_BY_USER && + if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + lr->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) regd = get_wiphy_regdom(wiphy); else @@ -828,8 +835,9 @@ static void handle_channel(struct wiphy *wiphy, const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; struct wiphy *request_wiphy = NULL; + struct regulatory_request *lr = get_last_request(); - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); flags = chan->orig_flags; @@ -862,7 +870,7 @@ static void handle_channel(struct wiphy *wiphy, if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags = IEEE80211_CHAN_NO_HT40; - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && + if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { /* @@ -927,7 +935,7 @@ bool reg_last_request_cell_base(void) bool val; mutex_lock(®_mutex); - val = reg_request_cell_base(last_request); + val = reg_request_cell_base(get_last_request()); mutex_unlock(®_mutex); return val; @@ -938,10 +946,12 @@ bool reg_last_request_cell_base(void) static enum reg_request_treatment reg_ignore_cell_hint(struct regulatory_request *pending_request) { + struct regulatory_request *lr = get_last_request(); + if (!reg_num_devs_support_basehint) return REG_REQ_IGNORE; - if (reg_request_cell_base(last_request) && + if (reg_request_cell_base(lr) && !regdom_changes(pending_request->alpha2)) return REG_REQ_ALREADY_SET; @@ -969,7 +979,9 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { - if (!last_request) { + struct regulatory_request *lr = get_last_request(); + + if (!lr) { REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", reg_initiator_name(initiator)); return true; @@ -988,13 +1000,13 @@ static bool ignore_reg_update(struct wiphy *wiphy, */ if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - !is_world_regdom(last_request->alpha2)) { + !is_world_regdom(lr->alpha2)) { REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", reg_initiator_name(initiator)); return true; } - if (reg_request_cell_base(last_request)) + if (reg_request_cell_base(lr)) return reg_dev_ignore_cell_hint(wiphy); return false; @@ -1080,12 +1092,12 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) { const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); + struct regulatory_request *lr = get_last_request(); if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) return true; - if (last_request && - last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) return true; @@ -1184,13 +1196,12 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { enum ieee80211_band band; - - assert_reg_lock(); + struct regulatory_request *lr = get_last_request(); if (ignore_reg_update(wiphy, initiator)) return; - last_request->dfs_region = get_cfg80211_regdom()->dfs_region; + lr->dfs_region = get_cfg80211_regdom()->dfs_region; for (band = 0; band < IEEE80211_NUM_BANDS; band++) handle_band(wiphy, initiator, wiphy->bands[band]); @@ -1199,7 +1210,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, reg_process_ht_flags(wiphy); if (wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, last_request); + wiphy->reg_notifier(wiphy, lr); } static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) @@ -1220,7 +1231,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, last_request); + wiphy->reg_notifier(wiphy, get_last_request()); } } @@ -1300,28 +1311,28 @@ get_reg_request_treatment(struct wiphy *wiphy, struct regulatory_request *pending_request) { struct wiphy *last_wiphy = NULL; + struct regulatory_request *lr = get_last_request(); /* All initial requests are respected */ - if (!last_request) + if (!lr) return REG_REQ_OK; switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: return REG_REQ_OK; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - if (reg_request_cell_base(last_request)) { + if (reg_request_cell_base(lr)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(pending_request->alpha2)) return REG_REQ_IGNORE; return REG_REQ_ALREADY_SET; } - last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); if (unlikely(!is_an_alpha2(pending_request->alpha2))) return -EINVAL; - if (last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { if (last_wiphy != wiphy) { /* * Two cards with two APs claiming different @@ -1343,7 +1354,7 @@ get_reg_request_treatment(struct wiphy *wiphy, } return 0; case NL80211_REGDOM_SET_BY_DRIVER: - if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { + if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { if (regdom_changes(pending_request->alpha2)) return REG_REQ_OK; return REG_REQ_ALREADY_SET; @@ -1354,7 +1365,7 @@ get_reg_request_treatment(struct wiphy *wiphy, * back in or if you add a new device for which the previously * loaded card also agrees on the regulatory domain. */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && + if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(pending_request->alpha2)) return REG_REQ_ALREADY_SET; @@ -1363,26 +1374,26 @@ get_reg_request_treatment(struct wiphy *wiphy, if (reg_request_cell_base(pending_request)) return reg_ignore_cell_hint(pending_request); - if (reg_request_cell_base(last_request)) + if (reg_request_cell_base(lr)) return REG_REQ_IGNORE; - if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) + if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) return REG_REQ_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && - last_request->intersect) + if (lr->initiator == NL80211_REGDOM_SET_BY_USER && + lr->intersect) return REG_REQ_IGNORE; /* * Process user requests only after previous user/driver/core * requests have been processed */ - if ((last_request->initiator == NL80211_REGDOM_SET_BY_CORE || - last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || - last_request->initiator == NL80211_REGDOM_SET_BY_USER) && - regdom_changes(last_request->alpha2)) + if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || + lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || + lr->initiator == NL80211_REGDOM_SET_BY_USER) && + regdom_changes(lr->alpha2)) return REG_REQ_IGNORE; if (!regdom_changes(pending_request->alpha2)) @@ -1397,15 +1408,16 @@ get_reg_request_treatment(struct wiphy *wiphy, static void reg_set_request_processed(void) { bool need_more_processing = false; + struct regulatory_request *lr = get_last_request(); - last_request->processed = true; + lr->processed = true; spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) need_more_processing = true; spin_unlock(®_requests_lock); - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) + if (lr->initiator == NL80211_REGDOM_SET_BY_USER) cancel_delayed_work(®_timeout); if (need_more_processing) @@ -1432,6 +1444,7 @@ __regulatory_hint(struct wiphy *wiphy, const struct ieee80211_regdomain *regd; bool intersect = false; enum reg_request_treatment treatment; + struct regulatory_request *lr; treatment = get_reg_request_treatment(wiphy, pending_request); @@ -1472,18 +1485,20 @@ __regulatory_hint(struct wiphy *wiphy, } new_request: - if (last_request != &core_request_world) - kfree(last_request); + lr = get_last_request(); + if (lr != &core_request_world && lr) + kfree_rcu(lr, rcu_head); - last_request = pending_request; - last_request->intersect = intersect; - last_request->processed = false; + pending_request->intersect = intersect; + pending_request->processed = false; + rcu_assign_pointer(last_request, pending_request); + lr = pending_request; pending_request = NULL; - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { - user_alpha2[0] = last_request->alpha2[0]; - user_alpha2[1] = last_request->alpha2[1]; + if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { + user_alpha2[0] = lr->alpha2[0]; + user_alpha2[1] = lr->alpha2[1]; } /* When r == REG_REQ_INTERSECT we do need to call CRDA */ @@ -1494,13 +1509,13 @@ new_request: * inform userspace we have processed the request */ if (treatment == REG_REQ_ALREADY_SET) { - nl80211_send_reg_change_event(last_request); + nl80211_send_reg_change_event(lr); reg_set_request_processed(); } return treatment; } - if (call_crda(last_request->alpha2)) + if (call_crda(lr->alpha2)) return REG_REQ_IGNORE; return REG_REQ_OK; } @@ -1543,13 +1558,14 @@ static void reg_process_hint(struct regulatory_request *reg_request, */ static void reg_process_pending_hints(void) { - struct regulatory_request *reg_request; + struct regulatory_request *reg_request, *lr; mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); + lr = get_last_request(); /* When last_request->processed becomes true this will be rescheduled */ - if (last_request && !last_request->processed) { + if (lr && !lr->processed) { REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); goto out; } @@ -1702,11 +1718,12 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, { char alpha2[2]; enum environment_cap env = ENVIRON_ANY; - struct regulatory_request *request; + struct regulatory_request *request, *lr; mutex_lock(®_mutex); + lr = get_last_request(); - if (unlikely(!last_request)) + if (unlikely(!lr)) goto out; /* IE len must be evenly divisible by 2 */ @@ -1729,8 +1746,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, * We leave conflict resolution to the workqueue, where can hold * cfg80211_mutex. */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - last_request->wiphy_idx != WIPHY_IDX_INVALID) + if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && + lr->wiphy_idx != WIPHY_IDX_INVALID) goto out; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); @@ -2021,13 +2038,12 @@ static void print_dfs_region(u8 dfs_region) static void print_regdomain(const struct ieee80211_regdomain *rd) { + struct regulatory_request *lr = get_last_request(); if (is_intersected_alpha2(rd->alpha2)) { - if (last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { struct cfg80211_registered_device *rdev; - rdev = cfg80211_rdev_by_wiphy_idx( - last_request->wiphy_idx); + rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx); if (rdev) { pr_info("Current regulatory domain updated by AP to: %c%c\n", rdev->country_ie_alpha2[0], @@ -2042,7 +2058,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) if (is_unknown_alpha2(rd->alpha2)) pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); else { - if (reg_request_cell_base(last_request)) + if (reg_request_cell_base(lr)) pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", rd->alpha2[0], rd->alpha2[1]); else @@ -2067,11 +2083,10 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *intersected_rd = NULL; struct wiphy *request_wiphy; + struct regulatory_request *lr = get_last_request(); /* Some basic sanity checks first */ - assert_reg_lock(); - if (!reg_is_valid_request(rd->alpha2)) return -EINVAL; @@ -2089,7 +2104,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * rd is non static (it means CRDA was present and was used last) * and the pending request came in from a country IE */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { /* * If someone else asked us to change the rd lets only bother * checking if the alpha2 changes if CRDA was already called @@ -2111,16 +2126,16 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return -EINVAL; } - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); if (!request_wiphy && - (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || - last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { + (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || + lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { schedule_delayed_work(®_timeout, 0); return -ENODEV; } - if (!last_request->intersect) { - if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { + if (!lr->intersect) { + if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { reset_regdomains(false, rd); return 0; } @@ -2148,7 +2163,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Intersection requires a bit more work */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); if (!intersected_rd) return -EINVAL; @@ -2158,7 +2173,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * However if a driver requested this specific regulatory * domain we keep it for its private use */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) + if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) rcu_assign_pointer(request_wiphy->regd, rd); else kfree(rd); @@ -2181,9 +2196,11 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) */ int set_regdom(const struct ieee80211_regdomain *rd) { + struct regulatory_request *lr; int r; mutex_lock(®_mutex); + lr = get_last_request(); /* Note that this doesn't update the wiphys, this is done below */ r = __set_regdom(rd); @@ -2196,18 +2213,17 @@ int set_regdom(const struct ieee80211_regdomain *rd) } /* This would make this whole thing pointless */ - if (WARN_ON(!last_request->intersect && - rd != get_cfg80211_regdom())) { + if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) { r = -EINVAL; goto out; } /* update all wiphys now with the new established regulatory domain */ - update_all_wiphy_regulatory(last_request->initiator); + update_all_wiphy_regulatory(lr->initiator); print_regdomain(get_cfg80211_regdom()); - nl80211_send_reg_change_event(last_request); + nl80211_send_reg_change_event(lr); reg_set_request_processed(); @@ -2220,10 +2236,11 @@ int set_regdom(const struct ieee80211_regdomain *rd) #ifdef CONFIG_HOTPLUG int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - if (last_request && !last_request->processed) { + struct regulatory_request *lr = get_last_request(); + + if (lr && !lr->processed) { if (add_uevent_var(env, "COUNTRY=%c%c", - last_request->alpha2[0], - last_request->alpha2[1])) + lr->alpha2[0], lr->alpha2[1])) return -ENOMEM; } @@ -2252,8 +2269,10 @@ void wiphy_regulatory_register(struct wiphy *wiphy) void wiphy_regulatory_deregister(struct wiphy *wiphy) { struct wiphy *request_wiphy = NULL; + struct regulatory_request *lr; mutex_lock(®_mutex); + lr = get_last_request(); if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint--; @@ -2261,14 +2280,14 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) rcu_free_regdom(get_wiphy_regdom(wiphy)); rcu_assign_pointer(wiphy->regd, NULL); - if (last_request) - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + if (lr) + request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); if (!request_wiphy || request_wiphy != wiphy) goto out; - last_request->wiphy_idx = WIPHY_IDX_INVALID; - last_request->country_ie_env = ENVIRON_ANY; + lr->wiphy_idx = WIPHY_IDX_INVALID; + lr->country_ie_env = ENVIRON_ANY; out: mutex_unlock(®_mutex); } From 361c9c8b0eeeec7d881e018d5143bf883558c566 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 15:57:14 +0100 Subject: [PATCH 142/478] regulatory: use IS_ERR macro family for freq_reg_info Instead of returning an error and filling a pointer return the pointer and an ERR_PTR value in error cases. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/regd.c | 16 +++----- .../net/wireless/brcm80211/brcmsmac/channel.c | 7 ++-- drivers/net/wireless/rtlwifi/regd.c | 14 +++---- include/net/cfg80211.h | 18 ++++----- net/wireless/reg.c | 38 +++++++++---------- 5 files changed, 41 insertions(+), 52 deletions(-) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 8ae58c404c67..7a6c79e1f819 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -195,7 +195,6 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; - int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { @@ -213,10 +212,8 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - r = freq_reg_info(wiphy, - ch->center_freq, - ®_rule); - if (r) + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (IS_ERR(reg_rule)) continue; /* * If 11d had a rule for this channel ensure @@ -252,7 +249,6 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; - int r; sband = wiphy->bands[IEEE80211_BAND_2GHZ]; if (!sband) @@ -280,16 +276,16 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (!r) { + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (!IS_ERR(reg_rule)) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } ch = &sband->channels[12]; /* CH 13 */ - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (!r) { + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (!IS_ERR(reg_rule)) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 30272519d795..4eb3f0d52105 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -670,7 +670,7 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *rule; - int band, i, ret; + int band, i; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; @@ -685,9 +685,8 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - ret = freq_reg_info(wiphy, ch->center_freq, - &rule); - if (ret) + rule = freq_reg_info(wiphy, ch->center_freq); + if (IS_ERR(rule)) continue; if (!(rule->flags & NL80211_RRF_NO_IBSS)) diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c index be55dc9167f0..7e3ead774fb9 100644 --- a/drivers/net/wireless/rtlwifi/regd.c +++ b/drivers/net/wireless/rtlwifi/regd.c @@ -158,7 +158,6 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; - int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { @@ -173,8 +172,8 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, (ch->flags & IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (r) + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (IS_ERR(reg_rule)) continue; /* @@ -209,7 +208,6 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; - int r; if (!wiphy->bands[IEEE80211_BAND_2GHZ]) return; @@ -237,16 +235,16 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (!r) { + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (!IS_ERR(reg_rule)) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } ch = &sband->channels[12]; /* CH 13 */ - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (!r) { + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (!IS_ERR(reg_rule)) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f3be58a29642..1f74360b527c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2938,22 +2938,22 @@ extern void wiphy_apply_custom_regulatory( * freq_reg_info - get regulatory information for the given frequency * @wiphy: the wiphy for which we want to process this rule for * @center_freq: Frequency in KHz for which we want regulatory information for - * @reg_rule: the regulatory rule which we have for this frequency * * Use this function to get the regulatory rule for a specific frequency on * a given wireless device. If the device has a specific regulatory domain * it wants to follow we respect that unless a country IE has been received * and processed already. * - * Returns 0 if it was able to find a valid regulatory rule which does - * apply to the given center_freq otherwise it returns non-zero. It will - * also return -ERANGE if we determine the given center_freq does not even have - * a regulatory rule for a frequency range in the center_freq's band. See - * freq_in_rule_band() for our current definition of a band -- this is purely - * subjective and right now its 802.11 specific. + * When an error occurs, for example if no rule can be found, the return value + * is encoded using ERR_PTR(). Use IS_ERR() to check and PTR_ERR() to obtain + * the numeric return value. The numeric return value will be -ERANGE if we + * determine the given center_freq does not even have a regulatory rule for a + * frequency range in the center_freq's band. See freq_in_rule_band() for our + * current definition of a band -- this is purely subjective and right now it's + * 802.11 specific. */ -extern int freq_reg_info(struct wiphy *wiphy, u32 center_freq, - const struct ieee80211_reg_rule **reg_rule); +const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, + u32 center_freq); /* * callbacks for asynchronous cfg80211 methods, notification diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2a7c3adf902f..fd53d975c0bc 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -710,16 +710,16 @@ static u32 map_regdom_flags(u32 rd_flags) return channel_flags; } -static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, - const struct ieee80211_reg_rule **reg_rule, - const struct ieee80211_regdomain *regd) +static const struct ieee80211_reg_rule * +freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, + const struct ieee80211_regdomain *regd) { int i; bool band_rule_found = false; bool bw_fits = false; if (!regd) - return -EINVAL; + return ERR_PTR(-EINVAL); for (i = 0; i < regd->n_reg_rules; i++) { const struct ieee80211_reg_rule *rr; @@ -738,20 +738,18 @@ static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); - if (band_rule_found && bw_fits) { - *reg_rule = rr; - return 0; - } + if (band_rule_found && bw_fits) + return rr; } if (!band_rule_found) - return -ERANGE; + return ERR_PTR(-ERANGE); - return -EINVAL; + return ERR_PTR(-EINVAL); } -int freq_reg_info(struct wiphy *wiphy, u32 center_freq, - const struct ieee80211_reg_rule **reg_rule) +const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, + u32 center_freq) { const struct ieee80211_regdomain *regd; struct regulatory_request *lr = get_last_request(); @@ -767,7 +765,7 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, else regd = get_cfg80211_regdom(); - return freq_reg_info_regd(wiphy, center_freq, reg_rule, regd); + return freq_reg_info_regd(wiphy, center_freq, regd); } EXPORT_SYMBOL(freq_reg_info); @@ -829,7 +827,6 @@ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, struct ieee80211_channel *chan) { - int r; u32 flags, bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; @@ -841,8 +838,8 @@ static void handle_channel(struct wiphy *wiphy, flags = chan->orig_flags; - r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), ®_rule); - if (r) { + reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); + if (IS_ERR(reg_rule)) { /* * We will disable all channels that do not match our * received regulatory rule unless the hint is coming @@ -854,7 +851,7 @@ static void handle_channel(struct wiphy *wiphy, * while 5 GHz is still supported. */ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - r == -ERANGE) + PTR_ERR(reg_rule) == -ERANGE) return; REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); @@ -1239,16 +1236,15 @@ static void handle_channel_custom(struct wiphy *wiphy, struct ieee80211_channel *chan, const struct ieee80211_regdomain *regd) { - int r; u32 bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; - r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), - ®_rule, regd); + reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), + regd); - if (r) { + if (IS_ERR(reg_rule)) { REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", chan->center_freq); chan->flags = IEEE80211_CHAN_DISABLED; From 75de9113bb9dc4939a7cd54e4bdfad555b35f5b1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:56:03 +0100 Subject: [PATCH 143/478] mac80211: optimise AP stop RCU handling If there are VLANs, stopping an AP is inefficient as it calls rcu_barrier() once for each interface (the VLANs and the AP itself). Optimise this by moving rcu_barrier() out of the station cleanups and calling it only once for all interfaces combined. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 9 +++++++-- net/mac80211/iface.c | 13 ++++++++----- net/mac80211/sta_info.c | 2 -- net/mac80211/sta_info.h | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index be019533b233..908f1153942d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1010,8 +1010,13 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) kfree_rcu(old_probe_resp, rcu_head); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) - sta_info_flush(vlan); - sta_info_flush(sdata); + sta_info_flush_defer(vlan); + sta_info_flush_defer(sdata); + rcu_barrier(); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + sta_info_flush_cleanup(vlan); + sta_info_flush_cleanup(sdata); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_stop_ap(sdata->local, sdata); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 12341efb109e..1ab7d8e57d39 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -863,12 +863,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->work); /* * When we get here, the interface is marked down. - * sta_info_flush_cleanup() calls rcu_barrier to - * wait for the station call_rcu() calls to complete, - * here we require it to wait for the RX path in case - * it is using the interface and enqueuing frames at - * this very time on another CPU. + * + * sta_info_flush_cleanup() requires rcu_barrier() + * first to wait for the station call_rcu() calls + * to complete, here we need at least sychronize_rcu() + * it to wait for the RX path in case it is using the + * interface and enqueuing frames at this very time on + * another CPU. */ + rcu_barrier(); sta_info_flush_cleanup(sdata); skb_queue_purge(&sdata->skb_queue); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 7199b9d5b2f4..738f9349c0a2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -920,8 +920,6 @@ int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata) { - rcu_barrier(); - ieee80211_cleanup_sdata_stas(sdata); cancel_work_sync(&sdata->cleanup_stations_wk); } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c3266aed4a07..031e4a5bbeca 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -549,6 +549,19 @@ void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata); + +/** + * sta_info_flush_cleanup - flush the sta_info cleanup queue + * @sdata: the interface + * + * Flushes the sta_info cleanup queue for a given interface; + * this is necessary before the interface is removed or, for + * AP/mesh interfaces, before it is deconfigured. + * + * Note an rcu_barrier() must precede the function, after all + * stations have been flushed/removed to ensure the call_rcu() + * calls that add stations to the cleanup queue have completed. + */ void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata); /** @@ -562,6 +575,7 @@ static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) { int ret = sta_info_flush_defer(sdata); + rcu_barrier(); sta_info_flush_cleanup(sdata); return ret; From 9cab315190cddbe3aaa31b64841354dd719afdfd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 00:19:08 +0100 Subject: [PATCH 144/478] cfg80211: adjacent 80+80 MHz channel segments are invalid In that case, it's really a 160 MHz channel, so disallow this configuration. Signed-off-by: Johannes Berg --- net/wireless/chan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/wireless/chan.c b/net/wireless/chan.c index a7990bb16529..396373f3ec26 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -76,6 +76,10 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) return false; if (!chandef->center_freq2) return false; + /* adjacent is not allowed -- that's a 160 MHz channel */ + if (chandef->center_freq1 - chandef->center_freq2 == 80 || + chandef->center_freq2 - chandef->center_freq1 == 80) + return false; break; case NL80211_CHAN_WIDTH_80: if (chandef->center_freq1 != control_freq + 30 && From cc3983d8ab174ca43d5817f16c489b3cce3a795a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Dec 2012 12:45:06 +0100 Subject: [PATCH 145/478] mac80211: fix ieee80211_ie_build_vht_cap indentation Signed-off-by: Johannes Berg --- net/mac80211/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f11e8c540db4..051313f0d1c2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1864,7 +1864,7 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, } u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, - u32 cap) + u32 cap) { __le32 tmp; From d45c41722a3eca639ced2c4c93665ced229b1da9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 10 Dec 2012 16:19:13 +0200 Subject: [PATCH 146/478] mac82011: use frame control to differentiate probe resp/beacon The probe response/beacon management frame RX code passes a bool parameter to differentiate beacons and probe responses. This is useless since we have the frame and can thus use its frame control field. Moreover it is buggy since there is one call to ieee80211_rx_bss_info with a beacon frame that is indicated as a probe response, which is also fixed by using the frame control field, so do that. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 50 +++++++++++++------------------------- net/mac80211/ieee80211_i.h | 3 +-- net/mac80211/mlme.c | 13 ++++------ net/mac80211/scan.c | 9 +++---- 4 files changed, 27 insertions(+), 48 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 854de974dc9a..272274931982 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -425,11 +425,9 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, } static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, + struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status, - struct ieee802_11_elems *elems, - bool beacon) + struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; int freq; @@ -530,7 +528,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, - channel, beacon); + channel); if (!bss) return; @@ -877,14 +875,21 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, ieee80211_tx_skb(sdata, skb); } -static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) +static +void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status) { size_t baselen; struct ieee802_11_elems elems; + BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) != + offsetof(typeof(mgmt->u.beacon), variable)); + + /* + * either beacon or probe_resp but the variable field is at the + * same offset + */ baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; if (baselen > len) return; @@ -892,25 +897,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); -} - -static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - size_t baselen; - struct ieee802_11_elems elems; - - /* Process beacon from the current BSS */ - baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; - if (baselen > len) - return; - - ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); - - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); } void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, @@ -934,12 +921,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee80211_rx_mgmt_probe_req(sdata, skb); break; case IEEE80211_STYPE_PROBE_RESP: - ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, - rx_status); - break; case IEEE80211_STYPE_BEACON: - ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, - rx_status); + ieee80211_rx_mgmt_probe_beacon(sdata, mgmt, skb->len, + rx_status); break; case IEEE80211_STYPE_AUTH: ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8563b9a5cac3..fa6d43e6197f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1346,8 +1346,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - struct ieee80211_channel *channel, - bool beacon); + struct ieee80211_channel *channel); void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 3cf85d8629a7..7d253a022173 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2369,8 +2369,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status, - struct ieee802_11_elems *elems, - bool beacon) + struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; int freq; @@ -2404,7 +2403,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, return; bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, - channel, beacon); + channel); if (bss) ieee80211_rx_bss_put(local, bss); @@ -2447,7 +2446,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); if (ifmgd->associated && ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) @@ -2528,8 +2527,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, - false); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ifmgd->assoc_data->have_beacon = true; ifmgd->assoc_data->sent_assoc = false; /* continue assoc process */ @@ -2682,8 +2680,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ifmgd->beacon_crc = ncrc; ifmgd->beacon_crc_valid = true; - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, - true); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len)) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d59fc6818b1c..06cbe26892a8 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -65,12 +65,11 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems) struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, - struct ieee80211_mgmt *mgmt, - size_t len, + struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - struct ieee80211_channel *channel, - bool beacon) + struct ieee80211_channel *channel) { + bool beacon = ieee80211_is_beacon(mgmt->frame_control); struct cfg80211_bss *cbss; struct ieee80211_bss *bss; int clen, srlen; @@ -203,7 +202,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) bss = ieee80211_bss_info_update(local, rx_status, mgmt, skb->len, &elems, - channel, beacon); + channel); if (bss) ieee80211_rx_bss_put(local, bss); } From 61e8a48cc1283c8e4358b3ce06305d886c55f7ce Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 16:47:42 +0100 Subject: [PATCH 147/478] mac80211: clean up ieee80211_quiesce It's a bit odd that there's a return value that only depends on the iftype, move that logic out of the function into the only caller that needs it. Also, since the quiescing could stop timers that trigger the sdata work, move the sdata work cancel into the function and after the actual quiesce. Finally, there's no need to call it on interfaces that are down, so don't. Change-Id: I1632d46d21ba3558ea713d035184f1939905f2f1 Signed-off-by: Johannes Berg --- net/mac80211/pm.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 79a48f37d409..f3217831d236 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -7,25 +7,23 @@ #include "led.h" /* return value indicates whether the driver should be further notified */ -static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) +static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) { switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_quiesce(sdata); - return true; + break; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_quiesce(sdata); - return true; + break; case NL80211_IFTYPE_MESH_POINT: ieee80211_mesh_quiesce(sdata); - return true; - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_MONITOR: - /* don't tell driver about this */ - return false; + break; default: - return true; + break; } + + cancel_work_sync(&sdata->work); } int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) @@ -94,10 +92,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) WARN_ON(err != 1); local->wowlan = false; } else { - list_for_each_entry(sdata, &local->interfaces, list) { - cancel_work_sync(&sdata->work); - ieee80211_quiesce(sdata); - } + list_for_each_entry(sdata, &local->interfaces, list) + if (ieee80211_sdata_running(sdata)) + ieee80211_quiesce(sdata); goto suspend; } } @@ -124,14 +121,19 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - cancel_work_sync(&sdata->work); - - if (!ieee80211_quiesce(sdata)) - continue; - if (!ieee80211_sdata_running(sdata)) continue; + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* skip these */ + continue; + default: + ieee80211_quiesce(sdata); + break; + } + /* disable beaconing */ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); From 529ba6e9313dbe60dab7e72c6fdf647a012e9f5b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 17:16:45 +0100 Subject: [PATCH 148/478] mac80211: clean up association better in suspend When suspending, bss_info_changed() is called to disable beacons, but managed mode interfaces are simply removed (bss_info_changed() is called with "no change" only). This can lead to problems. To fix this and copy the BSS configuration, clear it during suspend and restore it on resume. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 5 +++++ net/mac80211/pm.c | 19 ++++++++++++++++--- net/mac80211/util.c | 5 +++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fa6d43e6197f..38e7883cff23 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -783,6 +783,11 @@ struct ieee80211_sub_if_data { struct dentry *default_mgmt_key; } debugfs; #endif + +#ifdef CONFIG_PM + struct ieee80211_bss_conf suspend_bss_conf; +#endif + /* must be last, dynamically sized area in this! */ struct ieee80211_vif vif; }; diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index f3217831d236..712c17ff6e22 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -121,6 +121,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { + u32 changed = BSS_CHANGED_BEACON_ENABLED; + if (!ieee80211_sdata_running(sdata)) continue; @@ -129,14 +131,25 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) case NL80211_IFTYPE_MONITOR: /* skip these */ continue; + case NL80211_IFTYPE_STATION: + if (sdata->vif.bss_conf.assoc) + changed = BSS_CHANGED_ASSOC | + BSS_CHANGED_BSSID | + BSS_CHANGED_IDLE; + else + changed = 0; + /* fall through */ default: ieee80211_quiesce(sdata); break; } - /* disable beaconing */ - ieee80211_bss_info_change_notify(sdata, - BSS_CHANGED_BEACON_ENABLED); + sdata->suspend_bss_conf = sdata->vif.bss_conf; + memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf)); + sdata->vif.bss_conf.idle = true; + + /* disable beaconing or remove association */ + ieee80211_bss_info_change_notify(sdata, changed); if (sdata->vif.type == NL80211_IFTYPE_AP && rcu_access_pointer(sdata->u.ap.beacon)) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 051313f0d1c2..2e2e13bf96b2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1526,6 +1526,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_IDLE | BSS_CHANGED_TXPOWER; +#ifdef CONFIG_PM + if (local->resuming) + sdata->vif.bss_conf = sdata->suspend_bss_conf; +#endif + switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: changed |= BSS_CHANGED_ASSOC | From 8a61af65c6d03781015315dbc43d0942a5b31db9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 17:42:30 +0100 Subject: [PATCH 149/478] mac80211: fix channel context iteration During suspend/resume channel contexts might be iterated even if they haven't been re-added to the driver, keep track of this and skip them in iteration. Also use the new status for sanity checks. Also clarify the fact that during HW restart all contexts are iterated over (thanks Eliad.) Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++++ net/mac80211/chan.c | 3 ++- net/mac80211/driver-ops.h | 15 ++++++++++++--- net/mac80211/ieee80211_i.h | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ee50c5eba50c..0978b0faa880 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3754,6 +3754,11 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw, * The iterator will not find a context that's being added (during * the driver callback to add it) but will find it while it's being * removed. + * + * Note that during hardware restart, all contexts that existed + * before the restart are considered already present so will be + * found while iterating, whether they've been re-added already + * or not. */ void ieee80211_iter_chan_contexts_atomic( struct ieee80211_hw *hw, diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 80e55527504b..1bfe0a8b19d2 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -381,7 +381,8 @@ void ieee80211_iter_chan_contexts_atomic( rcu_read_lock(); list_for_each_entry_rcu(ctx, &local->chanctx_list, list) - iter(hw, &ctx->conf, iter_data); + if (ctx->driver_present) + iter(hw, &ctx->conf, iter_data); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 698dc7e6f309..608ced41548d 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -913,6 +913,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local, if (local->ops->add_chanctx) ret = local->ops->add_chanctx(&local->hw, &ctx->conf); trace_drv_return_int(local, ret); + if (!ret) + ctx->driver_present = true; return ret; } @@ -924,6 +926,7 @@ static inline void drv_remove_chanctx(struct ieee80211_local *local, if (local->ops->remove_chanctx) local->ops->remove_chanctx(&local->hw, &ctx->conf); trace_drv_return_void(local); + ctx->driver_present = false; } static inline void drv_change_chanctx(struct ieee80211_local *local, @@ -931,8 +934,10 @@ static inline void drv_change_chanctx(struct ieee80211_local *local, u32 changed) { trace_drv_change_chanctx(local, ctx, changed); - if (local->ops->change_chanctx) + if (local->ops->change_chanctx) { + WARN_ON_ONCE(!ctx->driver_present); local->ops->change_chanctx(&local->hw, &ctx->conf, changed); + } trace_drv_return_void(local); } @@ -945,10 +950,12 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local, check_sdata_in_driver(sdata); trace_drv_assign_vif_chanctx(local, sdata, ctx); - if (local->ops->assign_vif_chanctx) + if (local->ops->assign_vif_chanctx) { + WARN_ON_ONCE(!ctx->driver_present); ret = local->ops->assign_vif_chanctx(&local->hw, &sdata->vif, &ctx->conf); + } trace_drv_return_int(local, ret); return ret; @@ -961,10 +968,12 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, check_sdata_in_driver(sdata); trace_drv_unassign_vif_chanctx(local, sdata, ctx); - if (local->ops->unassign_vif_chanctx) + if (local->ops->unassign_vif_chanctx) { + WARN_ON_ONCE(!ctx->driver_present); local->ops->unassign_vif_chanctx(&local->hw, &sdata->vif, &ctx->conf); + } trace_drv_return_void(local); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 38e7883cff23..23161189b173 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -685,6 +685,7 @@ struct ieee80211_chanctx { enum ieee80211_chanctx_mode mode; int refcount; + bool driver_present; struct ieee80211_chanctx_conf conf; }; From d6a83228823fc0cc8d79d95c9f0bf568b7317862 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:06:28 +0100 Subject: [PATCH 150/478] mac80211: track enable_beacon explicitly Instead of calculating in ieee80211_bss_info_change_notify() whether beaconing should be enabled or not, set it in the correct places in the callers. This simplifies the logic in this function at the expense of offchannel, but is also more robust. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 +++ net/mac80211/ibss.c | 3 +++ net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/main.c | 32 -------------------------------- net/mac80211/mesh.c | 3 +++ net/mac80211/offchannel.c | 15 +++++++++------ 6 files changed, 21 insertions(+), 38 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 908f1153942d..3e7d557fd481 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -930,6 +930,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->vif.bss_conf.dtim_period = params->dtim_period; + sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.ssid_len = params->ssid_len; if (params->ssid_len) @@ -1017,6 +1018,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) sta_info_flush_cleanup(vlan); sta_info_flush_cleanup(sdata); + sdata->vif.bss_conf.enable_beacon = false; + clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_stop_ap(sdata->local, sdata); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 272274931982..b4b866f41919 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -191,6 +191,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, rcu_assign_pointer(ifibss->presp, skb); + sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.beacon_int = beacon_int; sdata->vif.bss_conf.basic_rates = basic_rates; bss_change = BSS_CHANGED_BEACON_INT; @@ -1189,6 +1190,8 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); sdata->vif.bss_conf.ibss_joined = false; sdata->vif.bss_conf.ibss_creator = false; + sdata->vif.bss_conf.enable_beacon = false; + clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_IBSS); synchronize_rcu(); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 23161189b173..c084c1503c04 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -659,10 +659,13 @@ enum ieee80211_sub_if_data_flags { * change handling while the interface is up * @SDATA_STATE_OFFCHANNEL: This interface is currently in offchannel * mode, so queues are stopped + * @SDATA_STATE_OFFCHANNEL_BEACON_STOPPED: Beaconing was stopped due + * to offchannel, reset when offchannel returns */ enum ieee80211_sdata_state_bits { SDATA_STATE_RUNNING, SDATA_STATE_OFFCHANNEL, + SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, }; /** diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0c0d4cabd76c..71a64f49fe61 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -245,38 +245,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, break; } - if (changed & BSS_CHANGED_BEACON_ENABLED) { - if (local->quiescing || !ieee80211_sdata_running(sdata) || - test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) { - sdata->vif.bss_conf.enable_beacon = false; - } else { - /* - * Beacon should be enabled, but AP mode must - * check whether there is a beacon configured. - */ - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP: - sdata->vif.bss_conf.enable_beacon = - !!sdata->u.ap.beacon; - break; - case NL80211_IFTYPE_ADHOC: - sdata->vif.bss_conf.enable_beacon = - !!sdata->u.ibss.presp; - break; -#ifdef CONFIG_MAC80211_MESH - case NL80211_IFTYPE_MESH_POINT: - sdata->vif.bss_conf.enable_beacon = - !!sdata->u.mesh.mesh_id_len; - break; -#endif - default: - /* not reached */ - WARN_ON(1); - break; - } - } - } - drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index c0008d5dff0a..fcfa7ef9af98 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -626,6 +626,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.ht_operation_mode = ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; + sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(local, band); @@ -648,6 +649,8 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) /* stop the beacon */ ifmsh->mesh_id_len = 0; + sdata->vif.bss_conf.enable_beacon = false; + clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); /* flush STAs and mpaths on this iface */ diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index a5379aea7d09..1430b48600fc 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -126,11 +126,13 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); /* Check to see if we should disable beaconing. */ - if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_ADHOC || - sdata->vif.type == NL80211_IFTYPE_MESH_POINT) + if (sdata->vif.bss_conf.enable_beacon) { + set_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, + &sdata->state); + sdata->vif.bss_conf.enable_beacon = false; ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); + } if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { netif_tx_stop_all_queues(sdata->dev); @@ -183,11 +185,12 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, netif_tx_wake_all_queues(sdata->dev); } - if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_ADHOC || - sdata->vif.type == NL80211_IFTYPE_MESH_POINT) + if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, + &sdata->state)) { + sdata->vif.bss_conf.enable_beacon = true; ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); + } } mutex_unlock(&local->iflist_mtx); } From 8da349329ae52462253052f804ef30d086c9911a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:17:26 +0100 Subject: [PATCH 151/478] mac80211: reconfig bss_info_changed only if beaconing For AP/IBSS/mesh interfaces, call the driver to reconfigure bss_info_changed only if the interface was beaconing before suspend, otherwise we call the driver and it might interpret the change as going from enabled to disabled. Signed-off-by: Johannes Berg --- net/mac80211/util.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2e2e13bf96b2..e27c89c27bf4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1555,9 +1555,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* fall through */ case NL80211_IFTYPE_MESH_POINT: - changed |= BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_ENABLED; - ieee80211_bss_info_change_notify(sdata, changed); + if (sdata->vif.bss_conf.enable_beacon) { + changed |= BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED; + ieee80211_bss_info_change_notify(sdata, changed); + } break; case NL80211_IFTYPE_WDS: break; From b8dc1a35c88dddcf62ce7bc59a7ed4c38c2f7597 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:22:10 +0100 Subject: [PATCH 152/478] mac80211: further simplify ieee80211_bss_info_change_notify The special case in the function isn't really needed, instead make the suspend code a bit better and also easier to understand and move the warning into the driver op wrapper inline. Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.h | 6 ++++++ net/mac80211/main.c | 14 -------------- net/mac80211/pm.c | 15 ++++++++++----- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 608ced41548d..cccc486ae3d0 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -207,6 +207,12 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, { might_sleep(); + WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED) && + sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT); + check_sdata_in_driver(sdata); trace_drv_bss_info_changed(local, sdata, info, changed); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 71a64f49fe61..b719a16eabde 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -231,20 +231,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, return; } - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MESH_POINT: - break; - default: - /* do not warn to simplify caller in scan.c */ - changed &= ~BSS_CHANGED_BEACON_ENABLED; - if (WARN_ON(changed & BSS_CHANGED_BEACON)) - return; - break; - } - drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 712c17ff6e22..8a67d7e982f7 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -121,7 +121,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - u32 changed = BSS_CHANGED_BEACON_ENABLED; + u32 changed = 0; if (!ieee80211_sdata_running(sdata)) continue; @@ -136,14 +136,19 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) changed = BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID | BSS_CHANGED_IDLE; - else - changed = 0; - /* fall through */ + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + if (sdata->vif.bss_conf.enable_beacon) + changed = BSS_CHANGED_BEACON_ENABLED; + break; default: - ieee80211_quiesce(sdata); break; } + ieee80211_quiesce(sdata); + sdata->suspend_bss_conf = sdata->vif.bss_conf; memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf)); sdata->vif.bss_conf.idle = true; From ad2d223aa900179031feb40273881e212941573d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:34:25 +0100 Subject: [PATCH 153/478] mac80211: assign bss_conf.bssid only once Instead of checking every time bss_info_changed is called, assign the pointer once depending on the interface type and then leave it untouched until the interface type is changed. This makes the ieee80211_bss_info_change_notify() now a simple wrapper to call the driver only. Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.h | 2 ++ net/mac80211/iface.c | 7 +++++++ net/mac80211/main.c | 20 -------------------- net/mac80211/mesh.c | 3 +++ net/mac80211/pm.c | 3 +++ 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index cccc486ae3d0..0c07f94c5378 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -212,6 +212,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_ADHOC && sdata->vif.type != NL80211_IFTYPE_MESH_POINT); + WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE && + changed & ~BSS_CHANGED_IDLE); check_sdata_in_driver(sdata); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 1ab7d8e57d39..06fac2991d40 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1227,6 +1227,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: skb_queue_head_init(&sdata->u.ap.ps.bc_buf); INIT_LIST_HEAD(&sdata->u.ap.vlans); + sdata->vif.bss_conf.bssid = sdata->vif.addr; break; case NL80211_IFTYPE_P2P_CLIENT: type = NL80211_IFTYPE_STATION; @@ -1234,9 +1235,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->vif.p2p = true; /* fall through */ case NL80211_IFTYPE_STATION: + sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; ieee80211_sta_setup_sdata(sdata); break; case NL80211_IFTYPE_ADHOC: + sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; ieee80211_ibss_setup_sdata(sdata); break; case NL80211_IFTYPE_MESH_POINT: @@ -1250,8 +1253,12 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, MONITOR_FLAG_OTHER_BSS; break; case NL80211_IFTYPE_WDS: + sdata->vif.bss_conf.bssid = NULL; + break; case NL80211_IFTYPE_AP_VLAN: + break; case NL80211_IFTYPE_P2P_DEVICE: + sdata->vif.bss_conf.bssid = sdata->vif.addr; break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b719a16eabde..e6514f240fce 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -207,30 +207,10 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, u32 changed) { struct ieee80211_local *local = sdata->local; - static const u8 zero[ETH_ALEN] = { 0 }; if (!changed) return; - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; - } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; - else if (sdata->vif.type == NL80211_IFTYPE_AP) - sdata->vif.bss_conf.bssid = sdata->vif.addr; - else if (sdata->vif.type == NL80211_IFTYPE_WDS) - sdata->vif.bss_conf.bssid = NULL; - else if (ieee80211_vif_is_mesh(&sdata->vif)) { - sdata->vif.bss_conf.bssid = zero; - } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { - sdata->vif.bss_conf.bssid = sdata->vif.addr; - WARN_ONCE(changed & ~(BSS_CHANGED_IDLE), - "P2P Device BSS changed %#x", changed); - } else { - WARN_ON(1); - return; - } - drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index fcfa7ef9af98..245885841c8d 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -810,6 +810,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + static u8 zero_addr[ETH_ALEN] = {}; setup_timer(&ifmsh->housekeeping_timer, ieee80211_mesh_housekeeping_timer, @@ -835,4 +836,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) INIT_LIST_HEAD(&ifmsh->preq_queue.list); spin_lock_init(&ifmsh->mesh_preq_queue_lock); spin_lock_init(&ifmsh->sync_offset_lock); + + sdata->vif.bss_conf.bssid = zero_addr; } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 8a67d7e982f7..f81fd30581d9 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -121,6 +121,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { + static u8 zero_addr[ETH_ALEN] = {}; u32 changed = 0; if (!ieee80211_sdata_running(sdata)) @@ -152,6 +153,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) sdata->suspend_bss_conf = sdata->vif.bss_conf; memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf)); sdata->vif.bss_conf.idle = true; + if (sdata->suspend_bss_conf.bssid) + sdata->vif.bss_conf.bssid = zero_addr; /* disable beaconing or remove association */ ieee80211_bss_info_change_notify(sdata, changed); From 3ebfa6e76beab01e644e1facd5fdb84d4274043e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 19 Dec 2012 10:53:02 -0800 Subject: [PATCH 154/478] cfg80211: do not process beacon hints if one is already queued Regulatory beacon hints are used to help with world roaming and as it is right now we learn from a beacon hint processed on one wiphy to all other wiphys. The processing of beacon hints however is scheduled and if we have a lot of interfaces we may hit the case that we'll queue a the same beacon hint many times until its processed. To avoid this do a lookup on the queued up beacon hints prior to adding a new beacon hint. If the beacon hint is removed from the pending reg beacon hint list then it would be processed and we'd ensure all wiphys would have learned from it, if its on the pending reg beacon list we'd now find it prior to it being processed. Tested-by: Ben Greear Reported-by: Ben Greear Reported-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index fd53d975c0bc..2a3ae4d1001d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1925,11 +1925,23 @@ static bool freq_is_chan_12_13_14(u16 freq) return false; } +static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan) +{ + struct reg_beacon *pending_beacon; + + list_for_each_entry(pending_beacon, ®_pending_beacons, list) + if (beacon_chan->center_freq == + pending_beacon->chan.center_freq) + return true; + return false; +} + int regulatory_hint_found_beacon(struct wiphy *wiphy, struct ieee80211_channel *beacon_chan, gfp_t gfp) { struct reg_beacon *reg_beacon; + bool processing; if (beacon_chan->beacon_found || beacon_chan->flags & IEEE80211_CHAN_RADAR || @@ -1937,6 +1949,13 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, !freq_is_chan_12_13_14(beacon_chan->center_freq))) return 0; + spin_lock_bh(®_pending_beacons_lock); + processing = pending_reg_beacon(beacon_chan); + spin_unlock_bh(®_pending_beacons_lock); + + if (processing) + return 0; + reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); if (!reg_beacon) return -ENOMEM; From 3195e489a878aaa3b735d4a1b7c5c79b3786efed Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 19 Dec 2012 10:53:03 -0800 Subject: [PATCH 155/478] cfg80211: move reg_is_world_roaming() This will be used later by other code. This has no functional change. Tested-by: Ben Greear Reported-by: Ben Greear Reported-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2a3ae4d1001d..b68eeef45e6c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1009,6 +1009,22 @@ static bool ignore_reg_update(struct wiphy *wiphy, return false; } +static bool reg_is_world_roaming(struct wiphy *wiphy) +{ + const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); + const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); + struct regulatory_request *lr = get_last_request(); + + if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) + return true; + + if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) + return true; + + return false; +} + static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, struct reg_beacon *reg_beacon) { @@ -1085,22 +1101,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) } } -static bool reg_is_world_roaming(struct wiphy *wiphy) -{ - const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); - const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); - struct regulatory_request *lr = get_last_request(); - - if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) - return true; - - if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) - return true; - - return false; -} - /* Reap the advantages of previously found beacons */ static void reg_process_beacons(struct wiphy *wiphy) { From 0f500a5f6cadac888d5d3fce326452372d1a8343 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 19 Dec 2012 10:53:04 -0800 Subject: [PATCH 156/478] cfg80211: move world roaming check for beacon hints We should not add new beacon hints even if the wiphy is not world roaming. Without this we were always adding a beacon hint if not world roaming for every non world roaming wiphy interface. Tested-by: Ben Greear Reported-by: Ben Greear Reported-by: Johannes Berg Signed-off-by: Luis R. Rodriguez [fix locking] Signed-off-by: Johannes Berg --- net/wireless/reg.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b68eeef45e6c..0b35de001937 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1044,6 +1044,9 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, chan->beacon_found = true; + if (!reg_is_world_roaming(wiphy)) + return; + if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) return; @@ -1110,8 +1113,6 @@ static void reg_process_beacons(struct wiphy *wiphy) */ if (!last_request) return; - if (!reg_is_world_roaming(wiphy)) - return; wiphy_update_beacon_reg(wiphy); } @@ -1593,11 +1594,8 @@ static void reg_process_pending_beacon_hints(void) struct cfg80211_registered_device *rdev; struct reg_beacon *pending_beacon, *tmp; - /* - * No need to hold the reg_mutex here as we just touch wiphys - * and do not read or access regulatory variables. - */ mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); /* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); @@ -1615,6 +1613,7 @@ static void reg_process_pending_beacon_hints(void) } spin_unlock_bh(®_pending_beacons_lock); + mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); } From b08fbbd8ade1fe3eea821a65b98d9f6a828329da Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Dec 2012 13:06:48 +0100 Subject: [PATCH 157/478] mac80211: restrict assoc request VHT capabilities In interoperability testing some APs showed bad behaviour if some of the VHT capabilities of the station are better than their own. Restrict the assoc request parameters - beamformee capabable, - RX STBC and - RX MCS set to the subset that the AP can support. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 51 +++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c084c1503c04..7182907e282a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -405,6 +405,8 @@ struct ieee80211_mgd_assoc_data { u8 ap_ht_param; + struct ieee80211_vht_cap ap_vht_cap; + size_t ie_len; u8 ie[]; }; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7d253a022173..6ffbf701de95 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -341,11 +341,13 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_supported_band *sband) + struct ieee80211_supported_band *sband, + struct ieee80211_vht_cap *ap_vht_cap) { u8 *pos; u32 cap; struct ieee80211_sta_vht_cap vht_cap; + int i; BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); @@ -364,6 +366,42 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; } + /* + * Some APs apparently get confused if our capabilities are better + * than theirs, so restrict what we advertise in the assoc request. + */ + if (!(ap_vht_cap->vht_cap_info & + cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) + cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + + if (!(ap_vht_cap->vht_cap_info & + cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC))) + cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_RXSTBC_3 | + IEEE80211_VHT_CAP_RXSTBC_4); + + for (i = 0; i < 8; i++) { + int shift = i * 2; + u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift; + u16 ap_mcs, our_mcs; + + ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) & + mask) >> shift; + our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) & + mask) >> shift; + + switch (ap_mcs) { + default: + if (our_mcs <= ap_mcs) + break; + /* fall through */ + case IEEE80211_VHT_MCS_NOT_SUPPORTED: + vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask); + vht_cap.vht_mcs.rx_mcs_map |= + cpu_to_le16(ap_mcs << shift); + } + } + /* reserve and fill IE */ pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); @@ -562,7 +600,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) sband, chan, sdata->smps_mode); if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) - ieee80211_add_vht_ie(sdata, skb, sband); + ieee80211_add_vht_ie(sdata, skb, sband, + &assoc_data->ap_vht_cap); /* if present, add any custom non-vendor IEs that go after HT */ if (assoc_data->ie_len && assoc_data->ie) { @@ -3753,7 +3792,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)req->bss->priv; struct ieee80211_mgd_assoc_data *assoc_data; struct ieee80211_supported_band *sband; - const u8 *ssidie, *ht_ie; + const u8 *ssidie, *ht_ie, *vht_ie; int i, err; assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); @@ -3872,6 +3911,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; else ifmgd->flags |= IEEE80211_STA_DISABLE_HT; + vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY); + if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap)) + memcpy(&assoc_data->ap_vht_cap, vht_ie + 2, + sizeof(struct ieee80211_vht_cap)); + else + ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; rcu_read_unlock(); if (bss->wmm_used && bss->uapsd_supported && From dfa674da1807bc8fb782fc38a7d48c8ea3168eb7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 20 Dec 2012 18:23:01 +0100 Subject: [PATCH 158/478] cfg80211: move some AP code to right file Some AP code ended up in mlme.c as ap.c didn't exist when it was written, move it now. Signed-off-by: Johannes Berg --- net/wireless/ap.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ net/wireless/mlme.c | 62 --------------------------------------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 324e8d851dc4..a4a14e8f55cc 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -46,3 +46,65 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, return err; } + +void cfg80211_ch_switch_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + trace_cfg80211_ch_switch_notify(dev, chandef); + + wdev_lock(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) + goto out; + + wdev->channel = chandef->chan; + nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); +out: + wdev_unlock(wdev); + return; +} +EXPORT_SYMBOL(cfg80211_ch_switch_notify); + +bool cfg80211_rx_spurious_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + bool ret; + + trace_cfg80211_rx_spurious_frame(dev, addr); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) { + trace_cfg80211_return_bool(false); + return false; + } + ret = nl80211_unexpected_frame(dev, addr, gfp); + trace_cfg80211_return_bool(ret); + return ret; +} +EXPORT_SYMBOL(cfg80211_rx_spurious_frame); + +bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + bool ret; + + trace_cfg80211_rx_unexpected_4addr_frame(dev, addr); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { + trace_cfg80211_return_bool(false); + return false; + } + ret = nl80211_unexpected_4addr_frame(dev, addr, gfp); + trace_cfg80211_return_bool(ret); + return ret; +} +EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 5e8123ee63fd..461e692cdfec 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -987,65 +987,3 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); - -void cfg80211_ch_switch_notify(struct net_device *dev, - struct cfg80211_chan_def *chandef) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - trace_cfg80211_ch_switch_notify(dev, chandef); - - wdev_lock(wdev); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) - goto out; - - wdev->channel = chandef->chan; - nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); -out: - wdev_unlock(wdev); - return; -} -EXPORT_SYMBOL(cfg80211_ch_switch_notify); - -bool cfg80211_rx_spurious_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - bool ret; - - trace_cfg80211_rx_spurious_frame(dev, addr); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) { - trace_cfg80211_return_bool(false); - return false; - } - ret = nl80211_unexpected_frame(dev, addr, gfp); - trace_cfg80211_return_bool(ret); - return ret; -} -EXPORT_SYMBOL(cfg80211_rx_spurious_frame); - -bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - bool ret; - - trace_cfg80211_rx_unexpected_4addr_frame(dev, addr); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO && - wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { - trace_cfg80211_return_bool(false); - return false; - } - ret = nl80211_unexpected_4addr_frame(dev, addr, gfp); - trace_cfg80211_return_bool(ret); - return ret; -} -EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); From d582cffbcd04eae0bd8a83b05648bfd54bfd21c9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 26 Oct 2012 17:53:44 +0200 Subject: [PATCH 159/478] nl80211/mac80211: support full station state in AP mode Today, stations are added already associated. That is inefficient if, for example, the driver has no room for stations any more because then the station will go through the entire auth/assoc handshake, only to be kicked out afterwards. To address this a bit better, at least with drivers using the new station state callback, allow hostapd to add stations in unauthenticated mode, just after receiving the AUTH frame, before even replying. Thus if there's no more space at that point, it can send a negative auth frame back. It still needs to handle later state transition errors though, of course. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 16 +++++ net/mac80211/cfg.c | 115 +++++++++++++++++++++-------------- net/mac80211/main.c | 3 +- net/wireless/nl80211.c | 24 ++++++++ 4 files changed, 113 insertions(+), 45 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e3e19f8b16f2..547017100a30 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1697,6 +1697,9 @@ enum nl80211_iftype { * flag can't be changed, it is only valid while adding a station, and * attempts to change it will silently be ignored (rather than rejected * as errors.) + * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers + * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a + * previously added station into associated state * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -1708,6 +1711,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_MFP, NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, + NL80211_STA_FLAG_ASSOCIATED, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, @@ -3140,6 +3144,17 @@ enum nl80211_ap_sme_features { * setting * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic * powersave + * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state + * transitions for AP clients. Without this flag (and if the driver + * doesn't have the AP SME in the device) the driver supports adding + * stations only when they're associated and adds them in associated + * state (to later be transitioned into authorized), with this flag + * they should be added before even sending the authentication reply + * and then transitioned into authenticated, associated and authorized + * states using station flags. + * Note that even for drivers that support this, the default is to add + * stations in authenticated/associated state, so to add unauthenticated + * stations the authenticated/associated bits have to be set in the mask. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3155,6 +3170,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, + NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 13, }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3e7d557fd481..f4d12c71928d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -510,6 +510,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | BIT(NL80211_STA_FLAG_TDLS_PEER); if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); @@ -521,6 +522,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); if (test_sta_flag(sta, WLAN_STA_AUTH)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (test_sta_flag(sta, WLAN_STA_ASSOC)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); } @@ -1077,6 +1080,58 @@ static void ieee80211_send_layer2_update(struct sta_info *sta) netif_rx_ni(skb); } +static int sta_apply_auth_flags(struct ieee80211_local *local, + struct sta_info *sta, + u32 mask, u32 set) +{ + int ret; + + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) && + set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && + !test_sta_flag(sta, WLAN_STA_AUTH)) { + ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && + set & BIT(NL80211_STA_FLAG_ASSOCIATED) && + !test_sta_flag(sta, WLAN_STA_ASSOC)) { + ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { + if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) + ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); + else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); + else + ret = 0; + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && + !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) && + test_sta_flag(sta, WLAN_STA_ASSOC)) { + ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) && + !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && + test_sta_flag(sta, WLAN_STA_AUTH)) { + ret = sta_info_move_state(sta, IEEE80211_STA_NONE); + if (ret) + return ret; + } + + return 0; +} + static int sta_apply_parameters(struct ieee80211_local *local, struct sta_info *sta, struct station_parameters *params) @@ -1094,52 +1149,20 @@ static int sta_apply_parameters(struct ieee80211_local *local, mask = params->sta_flags_mask; set = params->sta_flags_set; - /* - * In mesh mode, we can clear AUTHENTICATED flag but must - * also make ASSOCIATED follow appropriately for the driver - * API. See also below, after AUTHORIZED changes. - */ - if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { - /* cfg80211 should not allow this in non-mesh modes */ - if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) - return -EINVAL; - - if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && - !test_sta_flag(sta, WLAN_STA_AUTH)) { - ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); - if (ret) - return ret; - ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); - if (ret) - return ret; - } - } - - if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { - if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) - ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); - else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) - ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); - if (ret) - return ret; - } - - if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { - /* cfg80211 should not allow this in non-mesh modes */ - if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) - return -EINVAL; - - if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && - test_sta_flag(sta, WLAN_STA_AUTH)) { - ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); - if (ret) - return ret; - ret = sta_info_move_state(sta, IEEE80211_STA_NONE); - if (ret) - return ret; - } + if (ieee80211_vif_is_mesh(&sdata->vif)) { + /* + * In mesh mode, ASSOCIATED isn't part of the nl80211 + * API but must follow AUTHENTICATED for driver state. + */ + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) + mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); + if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) + set |= BIT(NL80211_STA_FLAG_ASSOCIATED); } + ret = sta_apply_auth_flags(local, sta, mask, set); + if (ret) + return ret; if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) @@ -1273,6 +1296,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, if (!sta) return -ENOMEM; + /* + * defaults -- if userspace wants something else we'll + * change it accordingly in sta_apply_parameters() + */ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index e6514f240fce..39cfe8f10ad2 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -541,7 +541,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | NL80211_FEATURE_SAE | NL80211_FEATURE_HT_IBSS | - NL80211_FEATURE_VIF_TXPOWER; + NL80211_FEATURE_VIF_TXPOWER | + NL80211_FEATURE_FULL_AP_CLIENT_STATE; if (!ops->hw_scan) wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b3cf7cc0d4a1..087f68ba6d7a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3231,11 +3231,21 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* accept only the listed bits */ if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP))) return -EINVAL; + /* but authenticated/associated only if driver handles it */ + if (!(rdev->wiphy.features & + NL80211_FEATURE_FULL_AP_CLIENT_STATE) && + params.sta_flags_mask & + (BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED))) + return -EINVAL; + /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); if (IS_ERR(params.vlan)) @@ -3393,17 +3403,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) /* but don't bother the driver with it */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); + /* allow authenticated/associated only if driver handles it */ + if (!(rdev->wiphy.features & + NL80211_FEATURE_FULL_AP_CLIENT_STATE) && + params.sta_flags_mask & + (BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED))) + return -EINVAL; + /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); if (IS_ERR(params.vlan)) return PTR_ERR(params.vlan); break; case NL80211_IFTYPE_MESH_POINT: + /* associated is disallowed */ + if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) + return -EINVAL; /* TDLS peers cannot be added */ if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) return -EINVAL; break; case NL80211_IFTYPE_STATION: + /* associated is disallowed */ + if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) + return -EINVAL; /* Only TDLS peers can be added */ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) return -EINVAL; From c82c4a80bbb9c8ffa1d783070a8caa37f2db45b6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jul 2012 13:31:31 +0200 Subject: [PATCH 160/478] mac80211: split aggregation stop by reason The initiator/tx doesn't really identify why an aggregation session is stopped, give a reason for stopping that more clearly identifies what's going on. This will help tell the driver clearly what is expected of it. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 17 ++++++++--------- net/mac80211/ht.c | 15 ++++++++------- net/mac80211/ieee80211_i.h | 9 ++++----- net/mac80211/mlme.c | 2 +- net/mac80211/pm.c | 3 ++- net/mac80211/sta_info.c | 2 +- net/mac80211/sta_info.h | 7 +++++++ net/mac80211/util.c | 3 ++- 8 files changed, 33 insertions(+), 25 deletions(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index eb9df22418f0..e0656d77b313 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -150,8 +150,7 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, } int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_back_parties initiator, - bool tx) + enum ieee80211_agg_stop_reason reason) { struct ieee80211_local *local = sta->local; struct tid_ampdu_tx *tid_tx; @@ -212,8 +211,10 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, */ synchronize_net(); - tid_tx->stop_initiator = initiator; - tid_tx->tx_stop = tx; + tid_tx->stop_initiator = reason == AGG_STOP_PEER_REQUEST ? + WLAN_BACK_RECIPIENT : + WLAN_BACK_INITIATOR; + tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_STOP, @@ -660,14 +661,13 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe); int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_back_parties initiator, - bool tx) + enum ieee80211_agg_stop_reason reason) { int ret; mutex_lock(&sta->ampdu_mlme.mtx); - ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator, tx); + ret = ___ieee80211_stop_tx_ba_session(sta, tid, reason); mutex_unlock(&sta->ampdu_mlme.mtx); @@ -868,8 +868,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, } } else { - ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, - false); + ___ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED); } out: diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 8a6d68f1f404..df46cd8e55f5 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -182,16 +182,19 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, ieee80211_apply_htcap_overrides(sdata, ht_cap); } -void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx) +void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, + enum ieee80211_agg_stop_reason reason) { int i; cancel_work_sync(&sta->ampdu_mlme.work); for (i = 0; i < IEEE80211_NUM_TIDS; i++) { - __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx); + __ieee80211_stop_tx_ba_session(sta, i, reason); __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, - WLAN_REASON_QSTA_LEAVE_QBSS, tx); + WLAN_REASON_QSTA_LEAVE_QBSS, + reason != AGG_STOP_DESTROY_STA && + reason != AGG_STOP_PEER_REQUEST); } } @@ -248,8 +251,7 @@ void ieee80211_ba_session_work(struct work_struct *work) if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) ___ieee80211_stop_tx_ba_session(sta, tid, - WLAN_BACK_INITIATOR, - true); + AGG_STOP_LOCAL_REQUEST); } mutex_unlock(&sta->ampdu_mlme.mtx); } @@ -317,8 +319,7 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0, true); else - __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, - true); + __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST); } int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 7182907e282a..0fa44a965ad9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1432,7 +1432,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); -void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx); +void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, + enum ieee80211_agg_stop_reason reason); void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len); @@ -1446,11 +1447,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, size_t len); int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_back_parties initiator, - bool tx); + enum ieee80211_agg_stop_reason reason); int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_back_parties initiator, - bool tx); + enum ieee80211_agg_stop_reason reason); void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid); void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid); void ieee80211_ba_session_work(struct work_struct *work); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6ffbf701de95..d90c07b1795f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1525,7 +1525,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sta = sta_info_get(sdata, ifmgd->bssid); if (sta) { set_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, false); + ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); } mutex_unlock(&local->sta_mtx); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index f81fd30581d9..e45b83610e85 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -42,7 +42,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { set_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, true); + ieee80211_sta_tear_down_BA_sessions( + sta, AGG_STOP_LOCAL_REQUEST); } mutex_unlock(&local->sta_mtx); } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 738f9349c0a2..9d864ed5f3da 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -784,7 +784,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta) * will be sufficient. */ set_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, false); + ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); ret = sta_info_hash_del(local, sta); if (ret) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 031e4a5bbeca..af7d78aa5523 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -92,6 +92,13 @@ enum ieee80211_sta_info_flags { #define HT_AGG_STATE_WANT_START 4 #define HT_AGG_STATE_WANT_STOP 5 +enum ieee80211_agg_stop_reason { + AGG_STOP_DECLINED, + AGG_STOP_LOCAL_REQUEST, + AGG_STOP_PEER_REQUEST, + AGG_STOP_DESTROY_STA, +}; + /** * struct tid_ampdu_tx - TID aggregation information (Tx). * diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e27c89c27bf4..1b9420730d8c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1639,7 +1639,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - ieee80211_sta_tear_down_BA_sessions(sta, true); + ieee80211_sta_tear_down_BA_sessions( + sta, AGG_STOP_LOCAL_REQUEST); clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } From faec12ee2dd92edc09f75aab3d8c5085102052c5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jul 2012 14:12:44 +0200 Subject: [PATCH 161/478] mac80211: split out aggregation TX removal Create the function ieee80211_remove_tid_tx to call it from ___ieee80211_stop_tx_ba_session later. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 48 +++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index e0656d77b313..101bbfba9c8d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -713,6 +713,35 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) } EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); +static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) +{ + struct tid_ampdu_tx *tid_tx; + + lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_held(&sta->lock); + + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + + /* + * When we get here, the TX path will not be lockless any more wrt. + * aggregation, since the OPERATIONAL bit has long been cleared. + * Thus it will block on getting the lock, if it occurs. So if we + * stop the queue now, we will not get any more packets, and any + * that might be being processed will wait for us here, thereby + * guaranteeing that no packets go to the tid_tx pending queue any + * more. + */ + + ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); + + /* future packets must not find the tid_tx struct any more */ + ieee80211_assign_tid_tx(sta, tid, NULL); + + ieee80211_agg_splice_finish(sta->sdata, tid); + + kfree_rcu(tid_tx, rcu_head); +} + void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); @@ -751,24 +780,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) ieee80211_send_delba(sta->sdata, ra, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); - /* - * When we get here, the TX path will not be lockless any more wrt. - * aggregation, since the OPERATIONAL bit has long been cleared. - * Thus it will block on getting the lock, if it occurs. So if we - * stop the queue now, we will not get any more packets, and any - * that might be being processed will wait for us here, thereby - * guaranteeing that no packets go to the tid_tx pending queue any - * more. - */ - - ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); - - /* future packets must not find the tid_tx struct any more */ - ieee80211_assign_tid_tx(sta, tid, NULL); - - ieee80211_agg_splice_finish(sta->sdata, tid); - - kfree_rcu(tid_tx, rcu_head); + ieee80211_remove_tid_tx(sta, tid); unlock_sta: spin_unlock_bh(&sta->lock); From 30bf5f1f433c7612857ed13c50525945c483dfe0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 09:43:03 +0100 Subject: [PATCH 162/478] mac80211: move ieee80211_remove_tid_tx function To call it from ___ieee80211_stop_tx_ba_session, move the function and dependencies up. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 206 +++++++++++++++++++++--------------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 101bbfba9c8d..dda8d7df4b54 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -149,6 +149,109 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); } +static inline int ieee80211_ac_from_tid(int tid) +{ + return ieee802_1d_to_ac[tid & 7]; +} + +/* + * When multiple aggregation sessions on multiple stations + * are being created/destroyed simultaneously, we need to + * refcount the global queue stop caused by that in order + * to not get into a situation where one of the aggregation + * setup or teardown re-enables queues before the other is + * ready to handle that. + * + * These two functions take care of this issue by keeping + * a global "agg_queue_stop" refcount. + */ +static void __acquires(agg_queue) +ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) +{ + int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + + if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1) + ieee80211_stop_queue_by_reason( + &sdata->local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + __acquire(agg_queue); +} + +static void __releases(agg_queue) +ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) +{ + int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + + if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0) + ieee80211_wake_queue_by_reason( + &sdata->local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + __release(agg_queue); +} + +/* + * splice packets from the STA's pending to the local pending, + * requires a call to ieee80211_agg_splice_finish later + */ +static void __acquires(agg_queue) +ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata, + struct tid_ampdu_tx *tid_tx, u16 tid) +{ + struct ieee80211_local *local = sdata->local; + int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + unsigned long flags; + + ieee80211_stop_queue_agg(sdata, tid); + + if (WARN(!tid_tx, + "TID %d gone but expected when splicing aggregates from the pending queue\n", + tid)) + return; + + if (!skb_queue_empty(&tid_tx->pending)) { + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + /* copy over remaining packets */ + skb_queue_splice_tail_init(&tid_tx->pending, + &local->pending[queue]); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + } +} + +static void __releases(agg_queue) +ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid) +{ + ieee80211_wake_queue_agg(sdata, tid); +} + +static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) +{ + struct tid_ampdu_tx *tid_tx; + + lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_held(&sta->lock); + + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + + /* + * When we get here, the TX path will not be lockless any more wrt. + * aggregation, since the OPERATIONAL bit has long been cleared. + * Thus it will block on getting the lock, if it occurs. So if we + * stop the queue now, we will not get any more packets, and any + * that might be being processed will wait for us here, thereby + * guaranteeing that no packets go to the tid_tx pending queue any + * more. + */ + + ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); + + /* future packets must not find the tid_tx struct any more */ + ieee80211_assign_tid_tx(sta, tid, NULL); + + ieee80211_agg_splice_finish(sta->sdata, tid); + + kfree_rcu(tid_tx, rcu_head); +} + int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, enum ieee80211_agg_stop_reason reason) { @@ -265,80 +368,6 @@ static void sta_addba_resp_timer_expired(unsigned long data) rcu_read_unlock(); } -static inline int ieee80211_ac_from_tid(int tid) -{ - return ieee802_1d_to_ac[tid & 7]; -} - -/* - * When multiple aggregation sessions on multiple stations - * are being created/destroyed simultaneously, we need to - * refcount the global queue stop caused by that in order - * to not get into a situation where one of the aggregation - * setup or teardown re-enables queues before the other is - * ready to handle that. - * - * These two functions take care of this issue by keeping - * a global "agg_queue_stop" refcount. - */ -static void __acquires(agg_queue) -ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) -{ - int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; - - if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1) - ieee80211_stop_queue_by_reason( - &sdata->local->hw, queue, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - __acquire(agg_queue); -} - -static void __releases(agg_queue) -ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) -{ - int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; - - if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0) - ieee80211_wake_queue_by_reason( - &sdata->local->hw, queue, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - __release(agg_queue); -} - -/* - * splice packets from the STA's pending to the local pending, - * requires a call to ieee80211_agg_splice_finish later - */ -static void __acquires(agg_queue) -ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata, - struct tid_ampdu_tx *tid_tx, u16 tid) -{ - struct ieee80211_local *local = sdata->local; - int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; - unsigned long flags; - - ieee80211_stop_queue_agg(sdata, tid); - - if (WARN(!tid_tx, - "TID %d gone but expected when splicing aggregates from the pending queue\n", - tid)) - return; - - if (!skb_queue_empty(&tid_tx->pending)) { - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - /* copy over remaining packets */ - skb_queue_splice_tail_init(&tid_tx->pending, - &local->pending[queue]); - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - } -} - -static void __releases(agg_queue) -ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid) -{ - ieee80211_wake_queue_agg(sdata, tid); -} - void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) { struct tid_ampdu_tx *tid_tx; @@ -713,35 +742,6 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) } EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); -static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) -{ - struct tid_ampdu_tx *tid_tx; - - lockdep_assert_held(&sta->ampdu_mlme.mtx); - lockdep_assert_held(&sta->lock); - - tid_tx = rcu_dereference_protected_tid_tx(sta, tid); - - /* - * When we get here, the TX path will not be lockless any more wrt. - * aggregation, since the OPERATIONAL bit has long been cleared. - * Thus it will block on getting the lock, if it occurs. So if we - * stop the queue now, we will not get any more packets, and any - * that might be being processed will wait for us here, thereby - * guaranteeing that no packets go to the tid_tx pending queue any - * more. - */ - - ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); - - /* future packets must not find the tid_tx struct any more */ - ieee80211_assign_tid_tx(sta, tid, NULL); - - ieee80211_agg_splice_finish(sta->sdata, tid); - - kfree_rcu(tid_tx, rcu_head); -} - void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); From 18b559d5db47c86b10c14590aa2d26c0243c39e4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jul 2012 13:51:25 +0200 Subject: [PATCH 163/478] mac80211: split TX aggregation stop action When TX aggregation is stopped, there are a few different cases: - connection with the peer was dropped - session stop was requested locally - session stop was requested by the peer - connection was dropped while a session is stopping The behaviour in these cases should be different, if the connection is dropped then the driver should drop all frames, otherwise the frames may continue to be transmitted, aggregated in the case of a locally requested session stop or unaggregated in the case of the peer requesting session stop. Split these different cases so that the driver can act accordingly; however, treat local and remote stop the same way and ask the driver to not send frames as aggregated packets any more. In the case of connection drop, the stop callback the driver is otherwise supposed to call is no longer required. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 4 +- drivers/net/wireless/ath/ath9k/main.c | 4 +- drivers/net/wireless/ath/carl9170/main.c | 4 +- .../wireless/brcm80211/brcmsmac/mac80211_if.c | 4 +- drivers/net/wireless/iwlegacy/4965-mac.c | 4 +- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 4 +- drivers/net/wireless/mac80211_hwsim.c | 4 +- drivers/net/wireless/mwl8k.c | 4 +- drivers/net/wireless/rt2x00/rt2800lib.c | 4 +- drivers/net/wireless/rtlwifi/core.c | 4 +- drivers/net/wireless/ti/wlcore/main.c | 4 +- include/net/mac80211.h | 22 +++++++--- net/mac80211/agg-tx.c | 40 ++++++++++++++++--- 13 files changed, 85 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 9c07a8fa5134..a8016d70088a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1628,7 +1628,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, if (!ret) ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index be30a9af1528..e1fa70596e61 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1610,7 +1610,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ath9k_ps_restore(sc); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ath9k_ps_wakeup(sc); ath_tx_aggr_stop(sc, sta, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 25a1e2f4f738..9d2051aeb782 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1394,7 +1394,9 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: rcu_read_lock(); tid_info = rcu_dereference(sta_info->agg[tid]); if (tid_info) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 1fbd8ecbe2ea..f0fc8cd4d5df 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -668,7 +668,9 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw, ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: spin_lock_bh(&wl->lock); brcms_c_ampdu_flush(wl->wlc, sta, tid); spin_unlock_bh(&wl->lock); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index c3fbf6717564..6a86ed45835d 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5968,7 +5968,9 @@ il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, D_HT("start Tx\n"); ret = il4965_tx_agg_start(il, vif, sta, tid, ssn); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: D_HT("stop Tx\n"); ret = il4965_tx_agg_stop(il, vif, sta, tid); if (test_bit(S_EXIT_PENDING, &il->status)) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 3163e0f38c25..02fdcea76b21 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -679,7 +679,9 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, IWL_DEBUG_HT(priv, "start Tx\n"); ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: IWL_DEBUG_HT(priv, "stop Tx\n"); ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); if ((ret == 0) && (priv->agg_tids_count > 0)) { diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 145498f8411f..d248a4cc6656 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1312,7 +1312,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_START: ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f221b95b90b3..19b46fdf9f0f 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5170,7 +5170,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: if (stream) { if (stream->state == AMPDU_STREAM_ACTIVE) { spin_unlock(&priv->stream_lock); diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 197b4466a5d2..12f93e42e160 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -5484,7 +5484,9 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_TX_START: ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index be33aa14c8af..d3ce9fbef00e 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -879,7 +879,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw, "IEEE80211_AMPDU_TX_START: TID:%d\n", tid); return rtl_tx_agg_start(hw, sta, tid, ssn); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid); return rtl_tx_agg_stop(hw, sta, tid); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ea9d8e011bc9..d7de06359ae1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4575,7 +4575,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, * Falling break here on purpose for all TX APDU commands. */ case IEEE80211_AMPDU_TX_START: - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_OPERATIONAL: ret = -EINVAL; break; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0978b0faa880..a464f4fb36a0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2033,17 +2033,29 @@ enum ieee80211_filter_flags { * calling ieee80211_start_tx_ba_cb_irqsafe, because the peer * might receive the addBA frame and send a delBA right away! * - * @IEEE80211_AMPDU_RX_START: start Rx aggregation - * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation - * @IEEE80211_AMPDU_TX_START: start Tx aggregation - * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation + * @IEEE80211_AMPDU_RX_START: start RX aggregation + * @IEEE80211_AMPDU_RX_STOP: stop RX aggregation + * @IEEE80211_AMPDU_TX_START: start TX aggregation * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational + * @IEEE80211_AMPDU_TX_STOP_CONT: stop TX aggregation but continue transmitting + * queued packets, now unaggregated. After all packets are transmitted the + * driver has to call ieee80211_stop_tx_ba_cb_irqsafe(). + * @IEEE80211_AMPDU_TX_STOP_FLUSH: stop TX aggregation and flush all packets, + * called when the station is removed. There's no need or reason to call + * ieee80211_stop_tx_ba_cb_irqsafe() in this case as mac80211 assumes the + * session is gone and removes the station. + * @IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: called when TX aggregation is stopped + * but the driver hasn't called ieee80211_stop_tx_ba_cb_irqsafe() yet and + * now the connection is dropped and the station will be removed. Drivers + * should clean up and drop remaining packets when this is called. */ enum ieee80211_ampdu_mlme_action { IEEE80211_AMPDU_RX_START, IEEE80211_AMPDU_RX_STOP, IEEE80211_AMPDU_TX_START, - IEEE80211_AMPDU_TX_STOP, + IEEE80211_AMPDU_TX_STOP_CONT, + IEEE80211_AMPDU_TX_STOP_FLUSH, + IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, IEEE80211_AMPDU_TX_OPERATIONAL, }; diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index dda8d7df4b54..2f0ccbc5f13e 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -257,10 +257,25 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, { struct ieee80211_local *local = sta->local; struct tid_ampdu_tx *tid_tx; + enum ieee80211_ampdu_mlme_action action; int ret; lockdep_assert_held(&sta->ampdu_mlme.mtx); + switch (reason) { + case AGG_STOP_DECLINED: + case AGG_STOP_LOCAL_REQUEST: + case AGG_STOP_PEER_REQUEST: + action = IEEE80211_AMPDU_TX_STOP_CONT; + break; + case AGG_STOP_DESTROY_STA: + action = IEEE80211_AMPDU_TX_STOP_FLUSH; + break; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } + spin_lock_bh(&sta->lock); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); @@ -269,10 +284,19 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, return -ENOENT; } - /* if we're already stopping ignore any new requests to stop */ + /* + * if we're already stopping ignore any new requests to stop + * unless we're destroying it in which case notify the driver + */ if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { spin_unlock_bh(&sta->lock); - return -EALREADY; + if (reason != AGG_STOP_DESTROY_STA) + return -EALREADY; + ret = drv_ampdu_action(local, sta->sdata, + IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, + &sta->sta, tid, NULL, 0); + WARN_ON_ONCE(ret); + goto remove_tid_tx; } if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { @@ -319,8 +343,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, WLAN_BACK_INITIATOR; tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; - ret = drv_ampdu_action(local, sta->sdata, - IEEE80211_AMPDU_TX_STOP, + ret = drv_ampdu_action(local, sta->sdata, action, &sta->sta, tid, NULL, 0); /* HW shall not deny going back to legacy */ @@ -331,7 +354,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, */ } - return ret; + if (reason == AGG_STOP_DESTROY_STA) { + remove_tid_tx: + spin_lock_bh(&sta->lock); + ieee80211_remove_tid_tx(sta, tid); + spin_unlock_bh(&sta->lock); + } + + return 0; } /* From 598a5938e04ce30d837dca4c3c3326c69435342a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 12:00:40 +0100 Subject: [PATCH 164/478] wireless: use __packed in ieee80211.h Use __packed instead of __attribute__((packed)). Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 84 +++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 09879eb24380..5db76ebe8810 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -180,7 +180,7 @@ struct ieee80211_hdr { u8 addr3[6]; __le16 seq_ctrl; u8 addr4[6]; -} __attribute__ ((packed)); +} __packed; struct ieee80211_hdr_3addr { __le16 frame_control; @@ -189,7 +189,7 @@ struct ieee80211_hdr_3addr { u8 addr2[6]; u8 addr3[6]; __le16 seq_ctrl; -} __attribute__ ((packed)); +} __packed; struct ieee80211_qos_hdr { __le16 frame_control; @@ -199,7 +199,7 @@ struct ieee80211_qos_hdr { u8 addr3[6]; __le16 seq_ctrl; __le16 qos_ctrl; -} __attribute__ ((packed)); +} __packed; /** * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set @@ -576,7 +576,7 @@ struct ieee80211s_hdr { __le32 seqnum; u8 eaddr1[6]; u8 eaddr2[6]; -} __attribute__ ((packed)); +} __packed; /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 @@ -614,7 +614,7 @@ struct ieee80211_quiet_ie { u8 period; __le16 duration; __le16 offset; -} __attribute__ ((packed)); +} __packed; /** * struct ieee80211_msrment_ie @@ -626,7 +626,7 @@ struct ieee80211_msrment_ie { u8 mode; u8 type; u8 request[0]; -} __attribute__ ((packed)); +} __packed; /** * struct ieee80211_channel_sw_ie @@ -637,7 +637,7 @@ struct ieee80211_channel_sw_ie { u8 mode; u8 new_ch_num; u8 count; -} __attribute__ ((packed)); +} __packed; /** * struct ieee80211_tim @@ -650,7 +650,7 @@ struct ieee80211_tim_ie { u8 bitmap_ctrl; /* variable size: 1 - 251 bytes */ u8 virtual_map[1]; -} __attribute__ ((packed)); +} __packed; /** * struct ieee80211_meshconf_ie @@ -665,7 +665,7 @@ struct ieee80211_meshconf_ie { u8 meshconf_auth; u8 meshconf_form; u8 meshconf_cap; -} __attribute__ ((packed)); +} __packed; /** * enum mesh_config_capab_flags - Mesh Configuration IE capability field flags @@ -695,7 +695,7 @@ struct ieee80211_rann_ie { __le32 rann_seq; __le32 rann_interval; __le32 rann_metric; -} __attribute__ ((packed)); +} __packed; enum ieee80211_rann_flags { RANN_FLAG_IS_GATE = 1 << 0, @@ -717,33 +717,33 @@ struct ieee80211_mgmt { __le16 status_code; /* possibly followed by Challenge text */ u8 variable[0]; - } __attribute__ ((packed)) auth; + } __packed auth; struct { __le16 reason_code; - } __attribute__ ((packed)) deauth; + } __packed deauth; struct { __le16 capab_info; __le16 listen_interval; /* followed by SSID and Supported rates */ u8 variable[0]; - } __attribute__ ((packed)) assoc_req; + } __packed assoc_req; struct { __le16 capab_info; __le16 status_code; __le16 aid; /* followed by Supported rates */ u8 variable[0]; - } __attribute__ ((packed)) assoc_resp, reassoc_resp; + } __packed assoc_resp, reassoc_resp; struct { __le16 capab_info; __le16 listen_interval; u8 current_ap[6]; /* followed by SSID and Supported rates */ u8 variable[0]; - } __attribute__ ((packed)) reassoc_req; + } __packed reassoc_req; struct { __le16 reason_code; - } __attribute__ ((packed)) disassoc; + } __packed disassoc; struct { __le64 timestamp; __le16 beacon_int; @@ -751,11 +751,11 @@ struct ieee80211_mgmt { /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params, TIM */ u8 variable[0]; - } __attribute__ ((packed)) beacon; + } __packed beacon; struct { /* only variable items: SSID, Supported rates */ u8 variable[0]; - } __attribute__ ((packed)) probe_req; + } __packed probe_req; struct { __le64 timestamp; __le16 beacon_int; @@ -763,7 +763,7 @@ struct ieee80211_mgmt { /* followed by some of SSID, Supported rates, * FH Params, DS Params, CF Params, IBSS Params */ u8 variable[0]; - } __attribute__ ((packed)) probe_resp; + } __packed probe_resp; struct { u8 category; union { @@ -772,55 +772,55 @@ struct ieee80211_mgmt { u8 dialog_token; u8 status_code; u8 variable[0]; - } __attribute__ ((packed)) wme_action; + } __packed wme_action; struct{ u8 action_code; u8 element_id; u8 length; struct ieee80211_channel_sw_ie sw_elem; - } __attribute__((packed)) chan_switch; + } __packed chan_switch; struct{ u8 action_code; u8 dialog_token; u8 element_id; u8 length; struct ieee80211_msrment_ie msr_elem; - } __attribute__((packed)) measurement; + } __packed measurement; struct{ u8 action_code; u8 dialog_token; __le16 capab; __le16 timeout; __le16 start_seq_num; - } __attribute__((packed)) addba_req; + } __packed addba_req; struct{ u8 action_code; u8 dialog_token; __le16 status; __le16 capab; __le16 timeout; - } __attribute__((packed)) addba_resp; + } __packed addba_resp; struct{ u8 action_code; __le16 params; __le16 reason_code; - } __attribute__((packed)) delba; + } __packed delba; struct { u8 action_code; u8 variable[0]; - } __attribute__((packed)) self_prot; + } __packed self_prot; struct{ u8 action_code; u8 variable[0]; - } __attribute__((packed)) mesh_action; + } __packed mesh_action; struct { u8 action; u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; - } __attribute__ ((packed)) sa_query; + } __packed sa_query; struct { u8 action; u8 smps_control; - } __attribute__ ((packed)) ht_smps; + } __packed ht_smps; struct { u8 action_code; u8 dialog_token; @@ -828,9 +828,9 @@ struct ieee80211_mgmt { u8 variable[0]; } __packed tdls_discover_resp; } u; - } __attribute__ ((packed)) action; + } __packed action; } u; -} __attribute__ ((packed)); +} __packed; /* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */ #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 @@ -846,7 +846,7 @@ struct ieee80211_mmie { __le16 key_id; u8 sequence_number[6]; u8 mic[8]; -} __attribute__ ((packed)); +} __packed; struct ieee80211_vendor_ie { u8 element_id; @@ -861,20 +861,20 @@ struct ieee80211_rts { __le16 duration; u8 ra[6]; u8 ta[6]; -} __attribute__ ((packed)); +} __packed; struct ieee80211_cts { __le16 frame_control; __le16 duration; u8 ra[6]; -} __attribute__ ((packed)); +} __packed; struct ieee80211_pspoll { __le16 frame_control; __le16 aid; u8 bssid[6]; u8 ta[6]; -} __attribute__ ((packed)); +} __packed; /* TDLS */ @@ -967,7 +967,7 @@ struct ieee80211_bar { __u8 ta[6]; __le16 control; __le16 start_seq_num; -} __attribute__((packed)); +} __packed; /* 802.11 BAR control masks */ #define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 @@ -992,7 +992,7 @@ struct ieee80211_mcs_info { __le16 rx_highest; u8 tx_params; u8 reserved[3]; -} __attribute__((packed)); +} __packed; /* 802.11n HT capability MSC set */ #define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff @@ -1031,7 +1031,7 @@ struct ieee80211_ht_cap { __le16 extended_ht_cap_info; __le32 tx_BF_cap_info; u8 antenna_selection_info; -} __attribute__ ((packed)); +} __packed; /* 802.11n HT capabilities masks (for cap_info) */ #define IEEE80211_HT_CAP_LDPC_CODING 0x0001 @@ -1102,7 +1102,7 @@ struct ieee80211_ht_operation { __le16 operation_mode; __le16 stbc_param; u8 basic_set[16]; -} __attribute__ ((packed)); +} __packed; /* for ht_param */ #define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03 @@ -1839,14 +1839,14 @@ struct ieee80211_country_ie_triplet { u8 first_channel; u8 num_channels; s8 max_power; - } __attribute__ ((packed)) chans; + } __packed chans; struct { u8 reg_extension_id; u8 reg_class; u8 coverage_class; - } __attribute__ ((packed)) ext; + } __packed ext; }; -} __attribute__ ((packed)); +} __packed; enum ieee80211_timeout_interval_type { WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */, From ec61cd63dd3f3bf982180b2bcc1b325160d73837 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 12:12:10 +0100 Subject: [PATCH 165/478] mac80211: support HT notify channel width action Support the HT notify channel width action frame to update the rate scaling about the bandwidth the peer can receive in. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 9 +++++++++ net/mac80211/rx.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 5db76ebe8810..ccf9ee1dca8c 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -701,6 +701,11 @@ enum ieee80211_rann_flags { RANN_FLAG_IS_GATE = 1 << 0, }; +enum ieee80211_ht_chanwidth_values { + IEEE80211_HT_CHANWIDTH_20MHZ = 0, + IEEE80211_HT_CHANWIDTH_ANY = 1, +}; + #define WLAN_SA_QUERY_TR_ID_LEN 2 struct ieee80211_mgmt { @@ -821,6 +826,10 @@ struct ieee80211_mgmt { u8 action; u8 smps_control; } __packed ht_smps; + struct { + u8 action_code; + u8 chanwidth; + } __packed ht_notify_cw; struct { u8 action_code; u8 dialog_token; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 580704eba8b8..a19089565c4b 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2353,7 +2353,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) sdata->vif.type != NL80211_IFTYPE_ADHOC) break; - /* verify action & smps_control are present */ + /* verify action & smps_control/chanwidth are present */ if (len < IEEE80211_MIN_ACTION_SIZE + 2) goto invalid; @@ -2392,6 +2392,35 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) IEEE80211_RC_SMPS_CHANGED); goto handled; } + case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { + struct ieee80211_supported_band *sband; + u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; + bool old_40mhz, new_40mhz; + + /* If it doesn't support 40 MHz it can't change ... */ + if (!rx->sta->supports_40mhz) + goto handled; + + old_40mhz = rx->sta->sta.ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40; + new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY; + + if (old_40mhz == new_40mhz) + goto handled; + + if (new_40mhz) + rx->sta->sta.ht_cap.cap |= + IEEE80211_HT_CAP_SUP_WIDTH_20_40; + else + rx->sta->sta.ht_cap.cap &= + ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + sband = rx->local->hw.wiphy->bands[status->band]; + + rate_control_rate_update(local, sband, rx->sta, + IEEE80211_RC_BW_CHANGED); + goto handled; + } default: goto invalid; } From 1c06ef9831c78648f719a174e4598ae2600b0cf7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 12:22:02 +0100 Subject: [PATCH 166/478] wireless: use __aligned Use __aligned(...) instead of __attribute__((aligned(...))) in mac80211 and cfg80211. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++-- include/net/mac80211.h | 6 +++--- net/wireless/core.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1f74360b527c..e5f085c89221 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1256,7 +1256,7 @@ struct cfg80211_bss { u8 bssid[ETH_ALEN]; - u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); + u8 priv[0] __aligned(sizeof(void *)); }; /** @@ -2392,7 +2392,7 @@ struct wiphy { const struct iw_handler_def *wext; #endif - char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); + char priv[0] __aligned(NETDEV_ALIGN); }; static inline struct net *wiphy_net(struct wiphy *wiphy) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a464f4fb36a0..23daed3c78ed 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -173,7 +173,7 @@ struct ieee80211_chanctx_conf { u8 rx_chains_static, rx_chains_dynamic; - u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); + u8 drv_priv[0] __aligned(sizeof(void *)); }; /** @@ -1059,7 +1059,7 @@ struct ieee80211_vif { u32 driver_flags; /* must be last */ - u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); + u8 drv_priv[0] __aligned(sizeof(void *)); }; static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) @@ -1209,7 +1209,7 @@ struct ieee80211_sta { u8 max_sp; /* must be last */ - u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); + u8 drv_priv[0] __aligned(sizeof(void *)); }; /** diff --git a/net/wireless/core.h b/net/wireless/core.h index b8f4630c6ada..f342267e3620 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -89,7 +89,7 @@ struct cfg80211_registered_device { /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ - struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); + struct wiphy wiphy __aligned(NETDEV_ALIGN); }; static inline From 21add825f90a50407e0121833c8b7cd2c071c478 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 15:01:57 +0100 Subject: [PATCH 167/478] mac80211: unset peer's HT 40 MHz support if not usable When an interface is configured to a 20 MHz channel and the device as well as the peer are 40 MHz capable the HT capabilities of the peer are not restricted to 20 MHz, even though they're supposed to be restricted to the currently possible capabilities. Unset the 40 MHz HT capability bits in this case. Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index df46cd8e55f5..61ac7c48ac0c 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -120,6 +120,21 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_DSSSCCK40)); + + /* Unset 40 MHz if we're not using a 40 MHz channel */ + switch (sdata->vif.bss_conf.chandef.width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40; + ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + break; + case NL80211_CHAN_WIDTH_40: + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + break; + } + /* * The STBC bits are asymmetric -- if we don't have * TX then mask out the peer's RX and vice versa. From ba23d2068d85f6616ea5f92320c04e87d4b9e141 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 27 Dec 2012 17:32:09 +0100 Subject: [PATCH 168/478] cfg80211: disallow more station changes The following changes are invalid and should be disallowed when a station already exists: * supported rates changes, except for TDLS peers * listen interval changes * HT capability changes Disallow them and also update a mac80211 comment explaining how they would be racy. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 9 +++++---- net/wireless/nl80211.c | 22 ++++++++-------------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f4d12c71928d..7d290bce0927 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1208,10 +1208,11 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->sta.aid = params->aid; /* - * FIXME: updating the following information is racy when this - * function is called from ieee80211_change_station(). - * However, all this information should be static so - * maybe we should just reject attemps to change it. + * Some of the following updates would be racy if called on an + * existing station, via ieee80211_change_station(). However, + * all such changes are rejected by cfg80211 except for updates + * changing the supported rates on an existing but not yet used + * TDLS peer. */ if (params->listen_interval >= 0) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 087f68ba6d7a..9bd8340af999 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3188,13 +3188,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); } - if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - params.listen_interval = - nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); - - if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) - params.ht_capa = - nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] || + info->attrs[NL80211_ATTR_HT_CAPABILITY]) + return -EINVAL; if (!rdev->ops->change_station) return -EOPNOTSUPP; @@ -3246,6 +3242,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) BIT(NL80211_STA_FLAG_ASSOCIATED))) return -EINVAL; + /* reject other things that can't change */ + if (params.supported_rates) + return -EINVAL; + /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); if (IS_ERR(params.vlan)) @@ -3265,10 +3265,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* disallow things sta doesn't support */ if (params.plink_action) return -EINVAL; - if (params.ht_capa) - return -EINVAL; - if (params.listen_interval >= 0) - return -EINVAL; /* reject any changes other than AUTHORIZED */ if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) return -EINVAL; @@ -3277,9 +3273,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* disallow things mesh doesn't support */ if (params.vlan) return -EINVAL; - if (params.ht_capa) - return -EINVAL; - if (params.listen_interval >= 0) + if (params.supported_rates) return -EINVAL; /* * No special handling for TDLS here -- the userspace From 392d4cad7907f6cb4ffc85e135a01abfddc89027 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 27 Dec 2012 21:37:04 +0100 Subject: [PATCH 169/478] iwlwifi: fix PCIe interrupt handle return value By accident, commit eb6476441bc2fecf6232a87d0313a85f8e3da7f4 ("iwlwifi: protect use_ict with irq_lock") changed the return value of the iwl_pcie_isr() function in case it handles an interrupt -- it now returns IRQ_NONE instead of IRQ_HANDLED. Put back the correct return value. Cc: stable@vger.kernel.org Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/rx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index dad4c4aad91f..8389cd38338b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -1166,6 +1166,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); + return IRQ_HANDLED; none: /* re-enable interrupts here since we don't have anything to service. */ From f590dcec944552f9a4a61155810f3abd17d6465d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 31 Dec 2012 09:26:10 +0200 Subject: [PATCH 170/478] iwlwifi: fix the reclaimed packet tracking upon flush queue There's a bug in the currently released firmware version, the sequence control in the Tx response isn't updated in all cases. Take it from the packet as a workaround. Cc: stable@vger.kernel.org Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/tx.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index da21328ca8ed..a790599fe2c2 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1151,13 +1151,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, next_reclaimed = ssn; } - if (tid != IWL_TID_NON_QOS) { - priv->tid_data[sta_id][tid].next_reclaimed = - next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", - next_reclaimed); - } - iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); iwlagn_check_ratid_empty(priv, sta_id, tid); @@ -1208,11 +1201,28 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); + /* + * W/A for FW bug - the seq_ctl isn't updated when the + * queues are flushed. Fetch it from the packet itself + */ + if (!is_agg && status == TX_STATUS_FAIL_FIFO_FLUSHED) { + next_reclaimed = le16_to_cpu(hdr->seq_ctrl); + next_reclaimed = + SEQ_TO_SN(next_reclaimed + 0x10); + } + is_offchannel_skb = (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); freed++; } + if (tid != IWL_TID_NON_QOS) { + priv->tid_data[sta_id][tid].next_reclaimed = + next_reclaimed; + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", + next_reclaimed); + } + WARN_ON(!is_agg && freed != 1); /* From 75ea719cb4773b05cc48e741feb4e267522b9656 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 3 Jan 2013 00:09:46 -0500 Subject: [PATCH 171/478] mac80211: fix mesh_hwmp kerneldoc One of the function names was wrong and some parameters were missing. Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 47aeee2d8db1..9044296c8876 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -219,12 +219,14 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, } /** - * mesh_send_path error - Sends a PERR mesh management frame + * mesh_path_error_tx - Sends a PERR mesh management frame * + * @ttl: allowed remaining hops * @target: broken destination * @target_sn: SN of the broken destination * @target_rcode: reason code for this PERR * @ra: node this frame is addressed to + * @sdata: local mesh subif * * Note: This function may be called with driver locks taken that the driver * also acquires in the TX path. To avoid a deadlock we don't transmit the @@ -350,6 +352,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, * @sdata: local mesh subif * @mgmt: mesh management frame * @hwmp_ie: hwmp information element (PREP or PREQ) + * @action: type of hwmp ie * * This function updates the path routing information to the originator and the * transmitter of a HWMP PREQ or PREP frame. From 45034bfb8c9ecc62db40b438bea7fe728ac8facf Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 2 Jan 2013 14:55:16 -0800 Subject: [PATCH 172/478] mac80211_hwsim: fix tsf adjustment Make hwsim TSF offset adjustments cumulative and relative to the fake PHY TSF. Now adding 1000us, then adding -1000us doesn't result in a tsf_offset of -1000. Also the beacon timestamp can now correctly be expressed as (tsf + data->tsf_offset), which will be done in a later patch. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index d248a4cc6656..807d713414f8 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -361,7 +361,7 @@ struct mac80211_hwsim_data { int power_level; /* difference between this hw's clock and the real clock, in usecs */ - u64 tsf_offset; + s64 tsf_offset; }; @@ -427,9 +427,10 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct mac80211_hwsim_data *data = hw->priv; - struct timeval tv = ktime_to_timeval(ktime_get_real()); - u64 now = tv.tv_sec * USEC_PER_SEC + tv.tv_usec; - data->tsf_offset = tsf - now; + u64 now = mac80211_hwsim_get_tsf(hw, vif); + s64 delta = tsf - now; + + data->tsf_offset += delta; } static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, From 01e59e467ecf976c782eecd4dc99644802cc60e2 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 2 Jan 2013 14:55:17 -0800 Subject: [PATCH 173/478] mac80211_hwsim: hrtimer beacon For testing various timing-sensitive protocols (power save, MBCA, etc.), a beacon accuracy of jiffies is not sufficient. A tasklet_hrtimer is used for the beacon since it runs the callback in soft-IRQ context with hrtimer resolution. Also handle BSS_CHANGED_BEACON_ENABLED for hwsim. Signed-off-by: Thomas Pedersen [simplify timer container_of] Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 63 ++++++++++++++++++--------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 807d713414f8..c54c5d17a037 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -337,11 +337,11 @@ struct mac80211_hwsim_data { int scan_chan_idx; struct ieee80211_channel *channel; - unsigned long beacon_int; /* in jiffies unit */ + u64 beacon_int /* beacon interval in us */; unsigned int rx_filter; bool started, idle, scanning; struct mutex mutex; - struct timer_list beacon_timer; + struct tasklet_hrtimer beacon_timer; enum ps_mode { PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL } ps; @@ -917,7 +917,7 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw) { struct mac80211_hwsim_data *data = hw->priv; data->started = false; - del_timer(&data->beacon_timer); + tasklet_hrtimer_cancel(&data->beacon_timer); wiphy_debug(hw->wiphy, "%s\n", __func__); } @@ -1001,21 +1001,28 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, rcu_dereference(vif->chanctx_conf)->def.chan); } - -static void mac80211_hwsim_beacon(unsigned long arg) +static enum hrtimer_restart +mac80211_hwsim_beacon(struct hrtimer *timer) { - struct ieee80211_hw *hw = (struct ieee80211_hw *) arg; - struct mac80211_hwsim_data *data = hw->priv; + struct mac80211_hwsim_data *data = + container_of(timer, struct mac80211_hwsim_data, + beacon_timer.timer); + struct ieee80211_hw *hw = data->hw; + u64 bcn_int = data->beacon_int; + ktime_t next_bcn; if (!data->started) - return; + goto out; ieee80211_iterate_active_interfaces_atomic( hw, IEEE80211_IFACE_ITER_NORMAL, mac80211_hwsim_beacon_tx, hw); - data->beacon_timer.expires = jiffies + data->beacon_int; - add_timer(&data->beacon_timer); + next_bcn = ktime_add(hrtimer_get_expires(timer), + ns_to_ktime(bcn_int * 1000)); + tasklet_hrtimer_start(&data->beacon_timer, next_bcn, HRTIMER_MODE_ABS); +out: + return HRTIMER_NORESTART; } static const char *hwsim_chantypes[] = { @@ -1053,9 +1060,12 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) data->power_level = conf->power_level; if (!data->started || !data->beacon_int) - del_timer(&data->beacon_timer); - else - mod_timer(&data->beacon_timer, jiffies + data->beacon_int); + tasklet_hrtimer_cancel(&data->beacon_timer); + else if (!hrtimer_is_queued(&data->beacon_timer.timer)) { + tasklet_hrtimer_start(&data->beacon_timer, + ns_to_ktime(data->beacon_int * 1000), + HRTIMER_MODE_REL); + } return 0; } @@ -1105,12 +1115,22 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BEACON_INT) { wiphy_debug(hw->wiphy, " BCNINT: %d\n", info->beacon_int); - data->beacon_int = 1024 * info->beacon_int / 1000 * HZ / 1000; - if (WARN_ON(!data->beacon_int)) - data->beacon_int = 1; - if (data->started) - mod_timer(&data->beacon_timer, - jiffies + data->beacon_int); + data->beacon_int = info->beacon_int * 1024; + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + wiphy_debug(hw->wiphy, " BCN EN: %d\n", info->enable_beacon); + if (data->started && + !hrtimer_is_queued(&data->beacon_timer.timer) && + info->enable_beacon) { + if (WARN_ON(!data->beacon_int)) + data->beacon_int = 1000 * 1024; + tasklet_hrtimer_start(&data->beacon_timer, + ns_to_ktime(data->beacon_int * + 1000), + HRTIMER_MODE_REL); + } else if (!info->enable_beacon) + tasklet_hrtimer_cancel(&data->beacon_timer); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { @@ -2393,8 +2413,9 @@ static int __init init_mac80211_hwsim(void) data->debugfs, data, &hwsim_fops_group); - setup_timer(&data->beacon_timer, mac80211_hwsim_beacon, - (unsigned long) hw); + tasklet_hrtimer_init(&data->beacon_timer, + mac80211_hwsim_beacon, + CLOCK_REALTIME, HRTIMER_MODE_ABS); list_add_tail(&data->list, &hwsim_radios); } From c51f878379b1d0677619798b1d9358d053bdbdb1 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 2 Jan 2013 14:55:18 -0800 Subject: [PATCH 174/478] mac80211_hwsim: fix beacon timing A beacon period starts at TSF time 0. Spoof this by rounding the starting beacon time to a multiple of the beacon interval, and keep TBTT aligned on TSF adjustment. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index c54c5d17a037..b6e2caa024a7 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -362,6 +362,7 @@ struct mac80211_hwsim_data { /* difference between this hw's clock and the real clock, in usecs */ s64 tsf_offset; + s64 bcn_delta; }; @@ -428,9 +429,12 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, { struct mac80211_hwsim_data *data = hw->priv; u64 now = mac80211_hwsim_get_tsf(hw, vif); + u32 bcn_int = data->beacon_int; s64 delta = tsf - now; data->tsf_offset += delta; + /* adjust after beaconing with new timestamp at old TBTT */ + data->bcn_delta = do_div(delta, bcn_int); } static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, @@ -1018,6 +1022,12 @@ mac80211_hwsim_beacon(struct hrtimer *timer) hw, IEEE80211_IFACE_ITER_NORMAL, mac80211_hwsim_beacon_tx, hw); + /* beacon at new TBTT + beacon interval */ + if (data->bcn_delta) { + bcn_int -= data->bcn_delta; + data->bcn_delta = 0; + } + next_bcn = ktime_add(hrtimer_get_expires(timer), ns_to_ktime(bcn_int * 1000)); tasklet_hrtimer_start(&data->beacon_timer, next_bcn, HRTIMER_MODE_ABS); @@ -1062,8 +1072,12 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) if (!data->started || !data->beacon_int) tasklet_hrtimer_cancel(&data->beacon_timer); else if (!hrtimer_is_queued(&data->beacon_timer.timer)) { + u64 tsf = mac80211_hwsim_get_tsf(hw, NULL); + u32 bcn_int = data->beacon_int; + u64 until_tbtt = bcn_int - do_div(tsf, bcn_int); + tasklet_hrtimer_start(&data->beacon_timer, - ns_to_ktime(data->beacon_int * 1000), + ns_to_ktime(until_tbtt * 1000), HRTIMER_MODE_REL); } @@ -1123,11 +1137,15 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, if (data->started && !hrtimer_is_queued(&data->beacon_timer.timer) && info->enable_beacon) { + u64 tsf, until_tbtt; + u32 bcn_int; if (WARN_ON(!data->beacon_int)) data->beacon_int = 1000 * 1024; + tsf = mac80211_hwsim_get_tsf(hw, vif); + bcn_int = data->beacon_int; + until_tbtt = bcn_int - do_div(tsf, bcn_int); tasklet_hrtimer_start(&data->beacon_timer, - ns_to_ktime(data->beacon_int * - 1000), + ns_to_ktime(until_tbtt * 1000), HRTIMER_MODE_REL); } else if (!info->enable_beacon) tasklet_hrtimer_cancel(&data->beacon_timer); From 9a6f7347f59f55433ec50578e8d42d305e0d53ae Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 3 Jan 2013 10:31:19 +0200 Subject: [PATCH 175/478] ath9k: Fix Kconfig for ATH9K_HTC Ath9k consists of 2 different sub-modules: ATH9K and ATH9K_HTC. Both uses common Atheros code from ath.ko and need ATH_COMMON. However, while ATH9K selects ATH_COMMON, ATH9K_HTC does not. As result, if ATH9K_HTC is the only Atheros card selected, compilation fails with unresolved symbols. This patch moves ATH_COMMON selection to the common part for both ATH9K and ATH9K_HTC Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 581913ee6ef1..7647ed6b73d7 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -2,6 +2,7 @@ config ATH9K_HW tristate config ATH9K_COMMON tristate + select ATH_COMMON config ATH9K_DFS_DEBUGFS def_bool y depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED @@ -17,7 +18,6 @@ config ATH9K_BTCOEX_SUPPORT config ATH9K tristate "Atheros 802.11n wireless cards support" depends on MAC80211 - select ATH_COMMON select ATH9K_HW select MAC80211_LEDS select LEDS_CLASS From 9c969d8ccb1e17bd20742f4ac9f00c1a64487234 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Wed, 2 Jan 2013 16:07:35 -0800 Subject: [PATCH 176/478] mwifiex: check wait_event_interruptible return value wait_event_interruptible function returns -ERESTARTSYS if it's interrupted by a signal. Driver should check the return value and handle this case properly. In mwifiex_wait_queue_complete() routine, as we are now checking wait_event_interruptible return value, the condition check is not required. Also, we have removed mwifiex_cancel_pending_ioctl() call to avoid a chance of sending second command to FW by other path as soon as we clear current command node. FW can not handle two commands simultaneously. Cc: "3.6+" Signed-off-by: Bing Zhao Signed-off-by: Amitkumar Karwar Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sta_ioctl.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index cb682561c438..60e88b58039d 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -56,7 +56,6 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, */ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) { - bool cancel_flag = false; int status; struct cmd_ctrl_node *cmd_queued; @@ -70,14 +69,11 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) atomic_inc(&adapter->cmd_pending); /* Wait for completion */ - wait_event_interruptible(adapter->cmd_wait_q.wait, - *(cmd_queued->condition)); - if (!*(cmd_queued->condition)) - cancel_flag = true; - - if (cancel_flag) { - mwifiex_cancel_pending_ioctl(adapter); - dev_dbg(adapter->dev, "cmd cancel\n"); + status = wait_event_interruptible(adapter->cmd_wait_q.wait, + *(cmd_queued->condition)); + if (status) { + dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status); + return status; } status = adapter->cmd_wait_q.status; @@ -496,8 +492,11 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) return false; } - wait_event_interruptible(adapter->hs_activate_wait_q, - adapter->hs_activate_wait_q_woken); + if (wait_event_interruptible(adapter->hs_activate_wait_q, + adapter->hs_activate_wait_q_woken)) { + dev_err(adapter->dev, "hs_activate_wait_q terminated\n"); + return false; + } return true; } From c3ff0b2dff5b6c63f2deda8f934c0a21fb74850d Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 2 Jan 2013 17:32:19 -0800 Subject: [PATCH 177/478] mwifiex: fix typo in setting up ibss network parameters commit 683b6d3... "cfg80211: pass a channel definition struct" accidentally changed "==" to "!=". Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index a875499f8945..efe525be27dd 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1709,7 +1709,7 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, NL80211_CHAN_NO_HT) config_bands |= BAND_GN; } else { - if (cfg80211_get_chandef_type(¶ms->chandef) != + if (cfg80211_get_chandef_type(¶ms->chandef) == NL80211_CHAN_NO_HT) config_bands = BAND_A; else From 2be7d22f062535de59babdb4b5e9de9ff31e817e Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 20 Dec 2012 13:13:19 -0800 Subject: [PATCH 178/478] wireless: add new wil6210 802.11ad 60GHz driver This adds support for the 60 GHz 802.11ad Wilocity card through a new driver, wil6210. Wilocity implemented the firmware, QCA maintains the device driver. Currently supported: - STA: with security - AP: limited to 1 connected STA, security disabled - Monitor: due to a hardware/firmware limitation either control or non-control frames are monitored Using a STA and AP with this drive, one can assemble a fully functional BSS. Throughput of 1.2Gbps is achieved with iperf. The wil6210 cards have on-board flash memory for the firmware, the cards comes pre-flashed and no firmware download is required. For more details see: http://wireless.kernel.org/en/users/Drivers/wil6210 Signed-off-by: Vladimir Kondratiev Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- MAINTAINERS | 8 + drivers/net/wireless/ath/Kconfig | 1 + drivers/net/wireless/ath/Makefile | 1 + drivers/net/wireless/ath/wil6210/Kconfig | 29 + drivers/net/wireless/ath/wil6210/Makefile | 13 + drivers/net/wireless/ath/wil6210/cfg80211.c | 573 +++++++++ .../net/wireless/ath/wil6210/dbg_hexdump.h | 30 + drivers/net/wireless/ath/wil6210/debugfs.c | 603 +++++++++ drivers/net/wireless/ath/wil6210/interrupt.c | 471 +++++++ drivers/net/wireless/ath/wil6210/main.c | 407 ++++++ drivers/net/wireless/ath/wil6210/netdev.c | 157 +++ drivers/net/wireless/ath/wil6210/pcie_bus.c | 223 ++++ drivers/net/wireless/ath/wil6210/txrx.c | 871 +++++++++++++ drivers/net/wireless/ath/wil6210/txrx.h | 362 ++++++ drivers/net/wireless/ath/wil6210/wil6210.h | 363 ++++++ drivers/net/wireless/ath/wil6210/wmi.c | 975 ++++++++++++++ drivers/net/wireless/ath/wil6210/wmi.h | 1116 +++++++++++++++++ 17 files changed, 6203 insertions(+) create mode 100644 drivers/net/wireless/ath/wil6210/Kconfig create mode 100644 drivers/net/wireless/ath/wil6210/Makefile create mode 100644 drivers/net/wireless/ath/wil6210/cfg80211.c create mode 100644 drivers/net/wireless/ath/wil6210/dbg_hexdump.h create mode 100644 drivers/net/wireless/ath/wil6210/debugfs.c create mode 100644 drivers/net/wireless/ath/wil6210/interrupt.c create mode 100644 drivers/net/wireless/ath/wil6210/main.c create mode 100644 drivers/net/wireless/ath/wil6210/netdev.c create mode 100644 drivers/net/wireless/ath/wil6210/pcie_bus.c create mode 100644 drivers/net/wireless/ath/wil6210/txrx.c create mode 100644 drivers/net/wireless/ath/wil6210/txrx.h create mode 100644 drivers/net/wireless/ath/wil6210/wil6210.h create mode 100644 drivers/net/wireless/ath/wil6210/wmi.c create mode 100644 drivers/net/wireless/ath/wil6210/wmi.h diff --git a/MAINTAINERS b/MAINTAINERS index 4e2a1f67a1fc..16c3506da16c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1353,6 +1353,14 @@ W: http://wireless.kernel.org/en/users/Drivers/ath9k S: Supported F: drivers/net/wireless/ath/ath9k/ +WILOCITY WIL6210 WIRELESS DRIVER +M: Vladimir Kondratiev +L: linux-wireless@vger.kernel.org +L: wil6210@qca.qualcomm.com +S: Supported +W: http://wireless.kernel.org/en/users/Drivers/wil6210 +F: drivers/net/wireless/ath/wil6210/ + CARL9170 LINUX COMMUNITY WIRELESS DRIVER M: Christian Lamparter L: linux-wireless@vger.kernel.org diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 1a67a4f829fe..2c02b4e84094 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig" +source "drivers/net/wireless/ath/wil6210/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 1e18621326dc..97b964ded2be 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ +obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig new file mode 100644 index 000000000000..bac3d98a0cfb --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -0,0 +1,29 @@ +config WIL6210 + tristate "Wilocity 60g WiFi card wil6210 support" + depends on CFG80211 + depends on PCI + default n + ---help--- + This module adds support for wireless adapter based on + wil6210 chip by Wilocity. It supports operation on the + 60 GHz band, covered by the IEEE802.11ad standard. + + http://wireless.kernel.org/en/users/Drivers/wil6210 + + If you choose to build it as a module, it will be called + wil6210 + +config WIL6210_ISR_COR + bool "Use Clear-On-Read mode for ISR registers for wil6210" + depends on WIL6210 + default y + ---help--- + ISR registers on wil6210 chip may operate in either + COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode. + For production code, use COR (say y); is default since + it saves extra target transaction; + For ISR debug, use W1C (say n); is allows to monitor ISR + registers with debugfs. If COR were used, ISR would + self-clear when accessed for debug purposes, it makes + such monitoring impossible. + Say y unless you debug interrupts diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile new file mode 100644 index 000000000000..9396dc9fe3c5 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_WIL6210) += wil6210.o + +wil6210-objs := main.o +wil6210-objs += netdev.o +wil6210-objs += cfg80211.o +wil6210-objs += pcie_bus.o +wil6210-objs += debugfs.o +wil6210-objs += wmi.o +wil6210-objs += interrupt.o +wil6210-objs += txrx.o + +subdir-ccflags-y += -Werror +subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c new file mode 100644 index 000000000000..116f4e807ae1 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" + +#define CHAN60G(_channel, _flags) { \ + .band = IEEE80211_BAND_60GHZ, \ + .center_freq = 56160 + (2160 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 40, \ +} + +static struct ieee80211_channel wil_60ghz_channels[] = { + CHAN60G(1, 0), + CHAN60G(2, 0), + CHAN60G(3, 0), +/* channel 4 not supported yet */ +}; + +static struct ieee80211_supported_band wil_band_60ghz = { + .channels = wil_60ghz_channels, + .n_channels = ARRAY_SIZE(wil_60ghz_channels), + .ht_cap = { + .ht_supported = true, + .cap = 0, /* TODO */ + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ + .mcs = { + /* MCS 1..12 - SC PHY */ + .rx_mask = {0xfe, 0x1f}, /* 1..12 */ + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ + }, + }, +}; + +static const struct ieee80211_txrx_stypes +wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, +}; + +static const u32 wil_cipher_suites[] = { + WLAN_CIPHER_SUITE_GCMP, +}; + +int wil_iftype_nl2wmi(enum nl80211_iftype type) +{ + static const struct { + enum nl80211_iftype nl; + enum wmi_network_type wmi; + } __nl2wmi[] = { + {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC}, + {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA}, + {NL80211_IFTYPE_AP, WMI_NETTYPE_AP}, + {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P}, + {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P}, + {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */ + }; + uint i; + + for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { + if (__nl2wmi[i].nl == type) + return __nl2wmi[i].wmi; + } + + return -EOPNOTSUPP; +} + +static int wil_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *ndev, + u8 *mac, struct station_info *sinfo) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + struct wmi_notify_req_cmd cmd = { + .cid = 0, + .interval_usec = 0, + }; + + if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) + return -ENOENT; + + /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ + rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), + WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); + if (rc) + return rc; + + sinfo->generation = wil->sinfo_gen; + + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->txrate.mcs = wil->stats.bf_mcs; + sinfo->filled |= STATION_INFO_RX_BITRATE; + sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->rxrate.mcs = wil->stats.last_mcs_rx; + + if (test_bit(wil_status_fwconnected, &wil->status)) { + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = 12; /* TODO: provide real value */ + } + + return 0; +} + +static int wil_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + + switch (type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + break; + case NL80211_IFTYPE_MONITOR: + if (flags) + wil->monitor_flags = *flags; + else + wil->monitor_flags = 0; + + break; + default: + return -EOPNOTSUPP; + } + + wdev->iftype = type; + + return 0; +} + +static int wil_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + struct { + struct wmi_start_scan_cmd cmd; + u16 chnl[4]; + } __packed cmd; + uint i, n; + + if (wil->scan_request) { + wil_err(wil, "Already scanning\n"); + return -EAGAIN; + } + + /* check we are client side */ + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + break; + default: + return -EOPNOTSUPP; + + } + + /* FW don't support scan after connection attempt */ + if (test_bit(wil_status_dontscan, &wil->status)) { + wil_err(wil, "Scan after connect attempt not supported\n"); + return -EBUSY; + } + + wil->scan_request = request; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd.num_channels = 0; + n = min(request->n_channels, 4U); + for (i = 0; i < n; i++) { + int ch = request->channels[i]->hw_value; + if (ch == 0) { + wil_err(wil, + "Scan requested for unknown frequency %dMhz\n", + request->channels[i]->center_freq); + continue; + } + /* 0-based channel indexes */ + cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; + wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch, + request->channels[i]->center_freq); + } + + return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); +} + +static int wil_cfg80211_connect(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct cfg80211_bss *bss; + struct wmi_connect_cmd conn; + const u8 *ssid_eid; + const u8 *rsn_eid; + int ch; + int rc = 0; + + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, + sme->ssid, sme->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!bss) { + wil_err(wil, "Unable to find BSS\n"); + return -ENOENT; + } + + ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (!ssid_eid) { + wil_err(wil, "No SSID\n"); + rc = -ENOENT; + goto out; + } + + rsn_eid = sme->ie ? + cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : + NULL; + if (rsn_eid) { + if (sme->ie_len > WMI_MAX_IE_LEN) { + rc = -ERANGE; + wil_err(wil, "IE too large (%td bytes)\n", + sme->ie_len); + goto out; + } + /* + * For secure assoc, send: + * (1) WMI_DELETE_CIPHER_KEY_CMD + * (2) WMI_SET_APPIE_CMD + */ + rc = wmi_del_cipher_key(wil, 0, bss->bssid); + if (rc) { + wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n"); + goto out; + } + /* WMI_SET_APPIE_CMD */ + rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); + if (rc) { + wil_err(wil, "WMI_SET_APPIE_CMD failed\n"); + goto out; + } + } + + /* WMI_CONNECT_CMD */ + memset(&conn, 0, sizeof(conn)); + switch (bss->capability & 0x03) { + case WLAN_CAPABILITY_DMG_TYPE_AP: + conn.network_type = WMI_NETTYPE_INFRA; + break; + case WLAN_CAPABILITY_DMG_TYPE_PBSS: + conn.network_type = WMI_NETTYPE_P2P; + break; + default: + wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", + bss->capability); + goto out; + } + if (rsn_eid) { + conn.dot11_auth_mode = WMI_AUTH11_SHARED; + conn.auth_mode = WMI_AUTH_WPA2_PSK; + conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; + conn.pairwise_crypto_len = 16; + } else { + conn.dot11_auth_mode = WMI_AUTH11_OPEN; + conn.auth_mode = WMI_AUTH_NONE; + } + + conn.ssid_len = min_t(u8, ssid_eid[1], 32); + memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); + + ch = bss->channel->hw_value; + if (ch == 0) { + wil_err(wil, "BSS at unknown frequency %dMhz\n", + bss->channel->center_freq); + rc = -EOPNOTSUPP; + goto out; + } + conn.channel = ch - 1; + + memcpy(conn.bssid, bss->bssid, 6); + memcpy(conn.dst_mac, bss->bssid, 6); + /* + * FW don't support scan after connection attempt + */ + set_bit(wil_status_dontscan, &wil->status); + + rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); + if (rc == 0) { + /* Connect can take lots of time */ + mod_timer(&wil->connect_timer, + jiffies + msecs_to_jiffies(2000)); + } + + out: + cfg80211_put_bss(bss); + + return rc; +} + +static int wil_cfg80211_disconnect(struct wiphy *wiphy, + struct net_device *ndev, + u16 reason_code) +{ + int rc; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); + + return rc; +} + +static int wil_cfg80211_set_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = wil->wdev; + + wdev->preset_chandef = *chandef; + + return 0; +} + +static int wil_cfg80211_add_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr, + struct key_params *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + /* group key is not used */ + if (!pairwise) + return 0; + + return wmi_add_cipher_key(wil, key_index, mac_addr, + params->key_len, params->key); +} + +static int wil_cfg80211_del_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool pairwise, + const u8 *mac_addr) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + /* group key is not used */ + if (!pairwise) + return 0; + + return wmi_del_cipher_key(wil, key_index, mac_addr); +} + +/* Need to be present or wiphy_new() will WARN */ +static int wil_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index, bool unicast, + bool multicast) +{ + return 0; +} + +static int wil_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_ap_settings *info) +{ + int rc = 0; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + struct ieee80211_channel *channel = info->chandef.chan; + struct cfg80211_beacon_data *bcon = &info->beacon; + u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + if (!channel) { + wil_err(wil, "AP: No channel???\n"); + return -EINVAL; + } + + wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, + channel->center_freq, info->privacy ? "secure" : "open"); + print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, + info->ssid, info->ssid_len); + + rc = wil_reset(wil); + if (rc) + return rc; + + rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); + if (rc) + return rc; + + rc = wmi_set_channel(wil, channel->hw_value); + if (rc) + return rc; + + /* MAC address - pre-requisite for other commands */ + wmi_set_mac_address(wil, ndev->dev_addr); + + /* IE's */ + /* bcon 'head IE's are not relevant for 60g band */ + wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, + bcon->beacon_ies); + wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, + bcon->proberesp_ies); + wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, + bcon->assocresp_ies); + + wil->secure_pcp = info->privacy; + + rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype); + if (rc) + return rc; + + /* Rx VRING. After MAC and beacon */ + rc = wil_rx_init(wil); + + netif_carrier_on(ndev); + + return rc; +} + +static int wil_cfg80211_stop_ap(struct wiphy *wiphy, + struct net_device *ndev) +{ + int rc = 0; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + /* To stop beaconing, set BI to 0 */ + rc = wmi_set_bcon(wil, 0, wmi_nettype); + + return rc; +} + +static struct cfg80211_ops wil_cfg80211_ops = { + .scan = wil_cfg80211_scan, + .connect = wil_cfg80211_connect, + .disconnect = wil_cfg80211_disconnect, + .change_virtual_intf = wil_cfg80211_change_iface, + .get_station = wil_cfg80211_get_station, + .set_monitor_channel = wil_cfg80211_set_channel, + .add_key = wil_cfg80211_add_key, + .del_key = wil_cfg80211_del_key, + .set_default_key = wil_cfg80211_set_default_key, + /* AP mode */ + .start_ap = wil_cfg80211_start_ap, + .stop_ap = wil_cfg80211_stop_ap, +}; + +static void wil_wiphy_init(struct wiphy *wiphy) +{ + /* TODO: set real value */ + wiphy->max_scan_ssids = 10; + wiphy->max_num_pmkids = 0 /* TODO: */; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MONITOR); + /* TODO: enable P2P when integrated with supplicant: + * BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) + */ + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", + __func__, wiphy->flags); + wiphy->probe_resp_offload = + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + + wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; + + /* TODO: figure this out */ + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->cipher_suites = wil_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); + wiphy->mgmt_stypes = wil_mgmt_stypes; +} + +struct wireless_dev *wil_cfg80211_init(struct device *dev) +{ + int rc = 0; + struct wireless_dev *wdev; + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + wdev->wiphy = wiphy_new(&wil_cfg80211_ops, + sizeof(struct wil6210_priv)); + if (!wdev->wiphy) { + rc = -ENOMEM; + goto out; + } + + set_wiphy_dev(wdev->wiphy, dev); + wil_wiphy_init(wdev->wiphy); + + rc = wiphy_register(wdev->wiphy); + if (rc < 0) + goto out_failed_reg; + + return wdev; + +out_failed_reg: + wiphy_free(wdev->wiphy); +out: + kfree(wdev); + + return ERR_PTR(rc); +} + +void wil_wdev_free(struct wil6210_priv *wil) +{ + struct wireless_dev *wdev = wil_to_wdev(wil); + + if (!wdev) + return; + + wiphy_unregister(wdev->wiphy); + wiphy_free(wdev->wiphy); + kfree(wdev); +} diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h new file mode 100644 index 000000000000..6a315ba5aa7d --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h @@ -0,0 +1,30 @@ +#ifndef WIL_DBG_HEXDUMP_H_ +#define WIL_DBG_HEXDUMP_H_ + +#if defined(CONFIG_DYNAMIC_DEBUG) +#define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ +do { \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \ + __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ + print_hex_dump(KERN_DEBUG, prefix_str, \ + prefix_type, rowsize, groupsize, \ + buf, len, ascii); \ +} while (0) + +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \ + wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true) +#else /* defined(CONFIG_DYNAMIC_DEBUG) */ +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) +#endif /* defined(CONFIG_DYNAMIC_DEBUG) */ + +#endif /* WIL_DBG_HEXDUMP_H_ */ diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c new file mode 100644 index 000000000000..65fc9683bfd8 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "txrx.h" + +/* Nasty hack. Better have per device instances */ +static u32 mem_addr; +static u32 dbg_txdesc_index; + +static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, + const char *name, struct vring *vring) +{ + void __iomem *x = wmi_addr(wil, vring->hwtail); + + seq_printf(s, "VRING %s = {\n", name); + seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); + seq_printf(s, " va = 0x%p\n", vring->va); + seq_printf(s, " size = %d\n", vring->size); + seq_printf(s, " swtail = %d\n", vring->swtail); + seq_printf(s, " swhead = %d\n", vring->swhead); + seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail); + if (x) + seq_printf(s, "0x%08x\n", ioread32(x)); + else + seq_printf(s, "???\n"); + + if (vring->va && (vring->size < 1025)) { + uint i; + for (i = 0; i < vring->size; i++) { + volatile struct vring_tx_desc *d = &vring->va[i].tx; + if ((i % 64) == 0 && (i != 0)) + seq_printf(s, "\n"); + seq_printf(s, "%s", (d->dma.status & BIT(0)) ? + "S" : (vring->ctx[i] ? "H" : "h")); + } + seq_printf(s, "\n"); + } + seq_printf(s, "}\n"); +} + +static int wil_vring_debugfs_show(struct seq_file *s, void *data) +{ + uint i; + struct wil6210_priv *wil = s->private; + + wil_print_vring(s, wil, "rx", &wil->vring_rx); + + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + struct vring *vring = &(wil->vring_tx[i]); + if (vring->va) { + char name[10]; + snprintf(name, sizeof(name), "tx_%2d", i); + wil_print_vring(s, wil, name, vring); + } + } + + return 0; +} + +static int wil_vring_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_vring_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_vring = { + .open = wil_vring_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static void wil_print_ring(struct seq_file *s, const char *prefix, + void __iomem *off) +{ + struct wil6210_priv *wil = s->private; + struct wil6210_mbox_ring r; + int rsize; + uint i; + + wil_memcpy_fromio_32(&r, off, sizeof(r)); + wil_mbox_ring_le2cpus(&r); + /* + * we just read memory block from NIC. This memory may be + * garbage. Check validity before using it. + */ + rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); + + seq_printf(s, "ring %s = {\n", prefix); + seq_printf(s, " base = 0x%08x\n", r.base); + seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); + seq_printf(s, " tail = 0x%08x\n", r.tail); + seq_printf(s, " head = 0x%08x\n", r.head); + seq_printf(s, " entry size = %d\n", r.entry_size); + + if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { + seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", + sizeof(struct wil6210_mbox_ring_desc)); + goto out; + } + + if (!wmi_addr(wil, r.base) || + !wmi_addr(wil, r.tail) || + !wmi_addr(wil, r.head)) { + seq_printf(s, " ??? pointers are garbage?\n"); + goto out; + } + + for (i = 0; i < rsize; i++) { + struct wil6210_mbox_ring_desc d; + struct wil6210_mbox_hdr hdr; + size_t delta = i * sizeof(d); + void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; + + wil_memcpy_fromio_32(&d, x, sizeof(d)); + + seq_printf(s, " [%2x] %s %s%s 0x%08x", i, + d.sync ? "F" : "E", + (r.tail - r.base == delta) ? "t" : " ", + (r.head - r.base == delta) ? "h" : " ", + le32_to_cpu(d.addr)); + if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { + u16 len = le16_to_cpu(hdr.len); + seq_printf(s, " -> %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, + le16_to_cpu(hdr.type), hdr.flags); + if (len <= MAX_MBOXITEM_SIZE) { + int n = 0; + unsigned char printbuf[16 * 3 + 2]; + unsigned char databuf[MAX_MBOXITEM_SIZE]; + void __iomem *src = wmi_buffer(wil, d.addr) + + sizeof(struct wil6210_mbox_hdr); + /* + * No need to check @src for validity - + * we already validated @d.addr while + * reading header + */ + wil_memcpy_fromio_32(databuf, src, len); + while (n < len) { + int l = min(len - n, 16); + hex_dump_to_buffer(databuf + n, l, + 16, 1, printbuf, + sizeof(printbuf), + false); + seq_printf(s, " : %s\n", printbuf); + n += l; + } + } + } else { + seq_printf(s, "\n"); + } + } + out: + seq_printf(s, "}\n"); +} + +static int wil_mbox_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + + wil_print_ring(s, "tx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx)); + wil_print_ring(s, "rx", wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx)); + + return 0; +} + +static int wil_mbox_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_mbox_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_mbox = { + .open = wil_mbox_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_debugfs_iomem_x32_set(void *data, u64 val) +{ + iowrite32(val, (void __iomem *)data); + wmb(); /* make sure write propagated to HW */ + + return 0; +} + +static int wil_debugfs_iomem_x32_get(void *data, u64 *val) +{ + *val = ioread32((void __iomem *)data); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, + wil_debugfs_iomem_x32_set, "0x%08llx\n"); + +static struct dentry *wil_debugfs_create_iomem_x32(const char *name, + mode_t mode, + struct dentry *parent, + void __iomem *value) +{ + return debugfs_create_file(name, mode, parent, (void * __force)value, + &fops_iomem_x32); +} + +static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, + const char *name, + struct dentry *parent, u32 off) +{ + struct dentry *d = debugfs_create_dir(name, parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d, + wil->csr + off); + wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d, + wil->csr + off + 4); + wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d, + wil->csr + off + 8); + wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d, + wil->csr + off + 12); + wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d, + wil->csr + off + 16); + wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d, + wil->csr + off + 20); + wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d, + wil->csr + off + 24); + + return 0; +} + +static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, + struct dentry *parent) +{ + struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); + + return 0; +} + +static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, + struct dentry *parent) +{ + struct dentry *d = debugfs_create_dir("ITR_CNT", parent); + + if (IS_ERR_OR_NULL(d)) + return -ENODEV; + + wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); + wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_DATA)); + wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + + return 0; +} + +static int wil_memread_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr)); + + if (a) + seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a)); + else + seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); + + return 0; +} + +static int wil_memread_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_memread_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_memread = { + .open = wil_memread_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +static int wil_default_open(struct inode *inode, struct file *file) +{ + if (inode->i_private) + file->private_data = inode->i_private; + + return 0; +} + +static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + enum { max_count = 4096 }; + struct debugfs_blob_wrapper *blob = file->private_data; + loff_t pos = *ppos; + size_t available = blob->size; + void *buf; + size_t ret; + + if (pos < 0) + return -EINVAL; + + if (pos >= available || !count) + return 0; + + if (count > available - pos) + count = available - pos; + if (count > max_count) + count = max_count; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + + pos, count); + + ret = copy_to_user(user_buf, buf, count); + kfree(buf); + if (ret == count) + return -EFAULT; + + count -= ret; + *ppos = pos + count; + + return count; +} + +static const struct file_operations fops_ioblob = { + .read = wil_read_file_ioblob, + .open = wil_default_open, + .llseek = default_llseek, +}; + +static +struct dentry *wil_debugfs_create_ioblob(const char *name, + mode_t mode, + struct dentry *parent, + struct debugfs_blob_wrapper *blob) +{ + return debugfs_create_file(name, mode, parent, blob, &fops_ioblob); +} +/*---reset---*/ +static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct net_device *ndev = wil_to_ndev(wil); + + /** + * BUG: + * this code does NOT sync device state with the rest of system + * use with care, debug only!!! + */ + rtnl_lock(); + dev_close(ndev); + ndev->flags &= ~IFF_UP; + rtnl_unlock(); + wil_reset(wil); + + return len; +} + +static const struct file_operations fops_reset = { + .write = wil_write_file_reset, + .open = wil_default_open, +}; +/*---------Tx descriptor------------*/ + +static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + struct vring *vring = &(wil->vring_tx[0]); + + if (!vring->va) { + seq_printf(s, "No Tx VRING\n"); + return 0; + } + + if (dbg_txdesc_index < vring->size) { + volatile struct vring_tx_desc *d = + &(vring->va[dbg_txdesc_index].tx); + volatile u32 *u = (volatile u32 *)d; + struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; + + seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); + seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[0], u[1], u[2], u[3]); + seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", + u[4], u[5], u[6], u[7]); + seq_printf(s, " SKB = %p\n", skb); + + if (skb) { + unsigned char printbuf[16 * 3 + 2]; + int i = 0; + int len = skb_headlen(skb); + void *p = skb->data; + + seq_printf(s, " len = %d\n", len); + + while (i < len) { + int l = min(len - i, 16); + hex_dump_to_buffer(p + i, l, 16, 1, printbuf, + sizeof(printbuf), false); + seq_printf(s, " : %s\n", printbuf); + i += l; + } + } + seq_printf(s, "}\n"); + } else { + seq_printf(s, "TxDesc index (%d) >= size (%d)\n", + dbg_txdesc_index, vring->size); + } + + return 0; +} + +static int wil_txdesc_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_txdesc_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_txdesc = { + .open = wil_txdesc_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + +/*---------beamforming------------*/ +static int wil_bf_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + seq_printf(s, + "TSF : 0x%016llx\n" + "TxMCS : %d\n" + "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n", + wil->stats.tsf, wil->stats.bf_mcs, + wil->stats.my_rx_sector, wil->stats.my_tx_sector, + wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); + return 0; +} + +static int wil_bf_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_bf_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_bf = { + .open = wil_bf_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; +/*---------SSID------------*/ +static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wireless_dev *wdev = wil_to_wdev(wil); + + return simple_read_from_buffer(user_buf, count, ppos, + wdev->ssid, wdev->ssid_len); +} + +static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + struct wireless_dev *wdev = wil_to_wdev(wil); + struct net_device *ndev = wil_to_ndev(wil); + + if (*ppos != 0) { + wil_err(wil, "Unable to set SSID substring from [%d]\n", + (int)*ppos); + return -EINVAL; + } + + if (count > sizeof(wdev->ssid)) { + wil_err(wil, "SSID too long, len = %d\n", (int)count); + return -EINVAL; + } + if (netif_running(ndev)) { + wil_err(wil, "Unable to change SSID on running interface\n"); + return -EINVAL; + } + + wdev->ssid_len = count; + return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, + buf, count); +} + +static const struct file_operations fops_ssid = { + .read = wil_read_file_ssid, + .write = wil_write_file_ssid, + .open = wil_default_open, +}; + +/*----------------*/ +int wil6210_debugfs_init(struct wil6210_priv *wil) +{ + struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, + wil_to_wiphy(wil)->debugfsdir); + + if (IS_ERR_OR_NULL(dbg)) + return -ENODEV; + + debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); + debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); + debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); + debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, + &dbg_txdesc_index); + debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); + debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); + debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, + &wil->secure_pcp); + + wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, + HOSTADDR(RGF_USER_USER_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg, + HOSTADDR(RGF_DMA_EP_TX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg, + HOSTADDR(RGF_DMA_EP_RX_ICR)); + wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg, + HOSTADDR(RGF_DMA_EP_MISC_ICR)); + wil6210_debugfs_create_pseudo_ISR(wil, dbg); + wil6210_debugfs_create_ITR_CNT(wil, dbg); + + debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); + debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); + + debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); + + wil->rgf_blob.data = (void * __force)wil->csr + 0; + wil->rgf_blob.size = 0xa000; + wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); + + wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; + wil->fw_code_blob.size = 0x40000; + wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, + &wil->fw_code_blob); + + wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; + wil->fw_data_blob.size = 0x8000; + wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, + &wil->fw_data_blob); + + wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; + wil->fw_peri_blob.size = 0x18000; + wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, + &wil->fw_peri_blob); + + wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; + wil->uc_code_blob.size = 0x10000; + wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, + &wil->uc_code_blob); + + wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000; + wil->uc_data_blob.size = 0x4000; + wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, + &wil->uc_data_blob); + + return 0; +} + +void wil6210_debugfs_remove(struct wil6210_priv *wil) +{ + debugfs_remove_recursive(wil->debug); + wil->debug = NULL; +} diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c new file mode 100644 index 000000000000..38049da71049 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "wil6210.h" + +/** + * Theory of operation: + * + * There is ISR pseudo-cause register, + * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE + * Its bits represents OR'ed bits from 3 real ISR registers: + * TX, RX, and MISC. + * + * Registers may be configured to either "write 1 to clear" or + * "clear on read" mode + * + * When handling interrupt, one have to mask/unmask interrupts for the + * real ISR registers, or hardware may malfunction. + * + */ + +#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) +#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE +#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ + BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) +#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) + +#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ + BIT_DMA_PSEUDO_CAUSE_TX | \ + BIT_DMA_PSEUDO_CAUSE_MISC)) + +#if defined(CONFIG_WIL6210_ISR_COR) +/* configure to Clear-On-Read mode */ +#define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + +} +#else /* defined(CONFIG_WIL6210_ISR_COR) */ +/* configure to Write-1-to-Clear mode */ +#define WIL_ICR_ICC_VALUE (0UL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + iowrite32(x, addr); +} +#endif /* defined(CONFIG_WIL6210_ISR_COR) */ + +static inline u32 wil_ioread32_and_clear(void __iomem *addr) +{ + u32 x = ioread32(addr); + + wil_icr_clear(x, addr); + + return x; +} + +static void wil6210_mask_irq_tx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_rx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_misc(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + iowrite32(WIL6210_IRQ_DISABLE, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + + clear_bit(wil_status_irqen, &wil->status); +} + +static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_TX, wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_RX, wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) +{ + iowrite32(WIL6210_IMC_MISC, wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + set_bit(wil_status_irqen, &wil->status); + + iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); +} + +void wil6210_disable_irq(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + wil6210_mask_irq_tx(wil); + wil6210_mask_irq_rx(wil); + wil6210_mask_irq_misc(wil); + wil6210_mask_irq_pseudo(wil); +} + +void wil6210_enable_irq(struct wil6210_priv *wil) +{ + wil_dbg_IRQ(wil, "%s()\n", __func__); + + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICC)); + iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICC)); + + wil6210_unmask_irq_pseudo(wil); + wil6210_unmask_irq_tx(wil); + wil6210_unmask_irq_rx(wil); + wil6210_unmask_irq_misc(wil); +} + +static irqreturn_t wil6210_irq_rx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: RX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_rx(wil); + + if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { + wil_dbg_IRQ(wil, "RX done\n"); + isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; + wil_rx_handle(wil); + } + + if (isr) + wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); + + wil6210_unmask_irq_rx(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_tx(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: TX\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_tx(wil); + + if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { + uint i; + wil_dbg_IRQ(wil, "TX done\n"); + isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; + for (i = 0; i < 24; i++) { + u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); + if (isr & mask) { + isr &= ~mask; + wil_dbg_IRQ(wil, "TX done(%i)\n", i); + wil_tx_complete(wil, i); + } + } + } + + if (isr) + wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); + + wil6210_unmask_irq_tx(wil); + + return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_misc(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + + wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr); + + if (!isr) { + wil_err(wil, "spurious IRQ: MISC\n"); + return IRQ_NONE; + } + + wil6210_mask_irq_misc(wil); + + if (isr & ISR_MISC_FW_READY) { + wil_dbg_IRQ(wil, "IRQ: FW ready\n"); + /** + * Actual FW ready indicated by the + * WMI_FW_READY_EVENTID + */ + isr &= ~ISR_MISC_FW_READY; + } + + wil->isr_misc = isr; + + if (isr) { + return IRQ_WAKE_THREAD; + } else { + wil6210_unmask_irq_misc(wil); + return IRQ_HANDLED; + } +} + +static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + u32 isr = wil->isr_misc; + + wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr); + + if (isr & ISR_MISC_MBOX_EVT) { + wil_dbg_IRQ(wil, "MBOX event\n"); + wmi_recv_cmd(wil); + isr &= ~ISR_MISC_MBOX_EVT; + } + + if (isr) + wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); + + wil->isr_misc = 0; + + wil6210_unmask_irq_misc(wil); + + return IRQ_HANDLED; +} + +/** + * thread IRQ handler + */ +static irqreturn_t wil6210_thread_irq(int irq, void *cookie) +{ + struct wil6210_priv *wil = cookie; + + wil_dbg_IRQ(wil, "Thread IRQ\n"); + /* Discover real IRQ cause */ + if (wil->isr_misc) + wil6210_irq_misc_thread(irq, cookie); + + wil6210_unmask_irq_pseudo(wil); + + return IRQ_HANDLED; +} + +/* DEBUG + * There is subtle bug in hardware that causes IRQ to raise when it should be + * masked. It is quite rare and hard to debug. + * + * Catch irq issue if it happens and print all I can. + */ +static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) +{ + if (!test_bit(wil_status_irqen, &wil->status)) { + u32 icm_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_rx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_rx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_RX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_tx = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_tx = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_TX_ICR) + + offsetof(struct RGF_ICR, IMV)); + u32 icm_misc = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICM)); + u32 icr_misc = wil_ioread32_and_clear(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR)); + u32 imv_misc = ioread32(wil->csr + + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, IMV)); + wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" + "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" + "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", + pseudo_cause, + icm_rx, icr_rx, imv_rx, + icm_tx, icr_tx, imv_tx, + icm_misc, icr_misc, imv_misc); + + return -EINVAL; + } + + return 0; +} + +static irqreturn_t wil6210_hardirq(int irq, void *cookie) +{ + irqreturn_t rc = IRQ_HANDLED; + struct wil6210_priv *wil = cookie; + u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + + /** + * pseudo_cause is Clear-On-Read, no need to ACK + */ + if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)) + return IRQ_NONE; + + /* FIXME: IRQ mask debug */ + if (wil6210_debug_irq_mask(wil, pseudo_cause)) + return IRQ_NONE; + + wil6210_mask_irq_pseudo(wil); + + /* Discover real IRQ cause + * There are 2 possible phases for every IRQ: + * - hard IRQ handler called right here + * - threaded handler called later + * + * Hard IRQ handler reads and clears ISR. + * + * If threaded handler requested, hard IRQ handler + * returns IRQ_WAKE_THREAD and saves ISR register value + * for the threaded handler use. + * + * voting for wake thread - need at least 1 vote + */ + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && + (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && + (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && + (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) + rc = IRQ_WAKE_THREAD; + + /* if thread is requested, it will unmask IRQ */ + if (rc != IRQ_WAKE_THREAD) + wil6210_unmask_irq_pseudo(wil); + + wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause); + + return rc; +} + +static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) +{ + int rc; + /* + * IRQ's are in the following order: + * - Tx + * - Rx + * - Misc + */ + + rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED, + WIL_NAME"_tx", wil); + if (rc) + return rc; + + rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED, + WIL_NAME"_rx", wil); + if (rc) + goto free0; + + rc = request_threaded_irq(irq + 2, wil6210_irq_misc, + wil6210_irq_misc_thread, + IRQF_SHARED, WIL_NAME"_misc", wil); + if (rc) + goto free1; + + return 0; + /* error branch */ +free1: + free_irq(irq + 1, wil); +free0: + free_irq(irq, wil); + + return rc; +} + +int wil6210_init_irq(struct wil6210_priv *wil, int irq) +{ + int rc; + if (wil->n_msi == 3) + rc = wil6210_request_3msi(wil, irq); + else + rc = request_threaded_irq(irq, wil6210_hardirq, + wil6210_thread_irq, + wil->n_msi ? 0 : IRQF_SHARED, + WIL_NAME, wil); + if (rc) + return rc; + + wil6210_enable_irq(wil); + + return 0; +} + +void wil6210_fini_irq(struct wil6210_priv *wil, int irq) +{ + wil6210_disable_irq(wil); + free_irq(irq, wil); + if (wil->n_msi == 3) { + free_irq(irq + 1, wil); + free_irq(irq + 2, wil); + } +} diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c new file mode 100644 index 000000000000..95fcd361322b --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" + +/* + * Due to a hardware issue, + * one has to read/write to/from NIC in 32-bit chunks; + * regular memcpy_fromio and siblings will + * not work on 64-bit platform - it uses 64-bit transactions + * + * Force 32-bit transactions to enable NIC on 64-bit platforms + * + * To avoid byte swap on big endian host, __raw_{read|write}l + * should be used - {read|write}l would swap bytes to provide + * little endian on PCI value in host endianness. + */ +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count) +{ + u32 *d = dst; + const volatile u32 __iomem *s = src; + + /* size_t is unsigned, if (count%4 != 0) it will wrap */ + for (count += 4; count > 4; count -= 4) + *d++ = __raw_readl(s++); +} + +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, + size_t count) +{ + volatile u32 __iomem *d = dst; + const u32 *s = src; + + for (count += 4; count > 4; count -= 4) + __raw_writel(*s++, d++); +} + +static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + uint i; + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + + wil_dbg(wil, "%s()\n", __func__); + + wil_link_off(wil); + clear_bit(wil_status_fwconnected, &wil->status); + + switch (wdev->sme_state) { + case CFG80211_SME_CONNECTED: + cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, + NULL, 0, GFP_KERNEL); + break; + case CFG80211_SME_CONNECTING: + cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + break; + default: + ; + } + + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) + wil_vring_fini_tx(wil, i); +} + +static void wil_disconnect_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, + struct wil6210_priv, disconnect_worker); + + _wil6210_disconnect(wil, NULL); +} + +static void wil_connect_timer_fn(ulong x) +{ + struct wil6210_priv *wil = (void *)x; + + wil_dbg(wil, "Connect timeout\n"); + + /* reschedule to thread context - disconnect won't + * run from atomic context + */ + schedule_work(&wil->disconnect_worker); +} + +int wil_priv_init(struct wil6210_priv *wil) +{ + wil_dbg(wil, "%s()\n", __func__); + + mutex_init(&wil->mutex); + mutex_init(&wil->wmi_mutex); + + init_completion(&wil->wmi_ready); + + wil->pending_connect_cid = -1; + setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); + + INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker); + INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); + INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); + + INIT_LIST_HEAD(&wil->pending_wmi_ev); + spin_lock_init(&wil->wmi_ev_lock); + + wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); + if (!wil->wmi_wq) + return -EAGAIN; + + wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); + if (!wil->wmi_wq_conn) { + destroy_workqueue(wil->wmi_wq); + return -EAGAIN; + } + + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + + return 0; +} + +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ + del_timer_sync(&wil->connect_timer); + _wil6210_disconnect(wil, bssid); +} + +void wil_priv_deinit(struct wil6210_priv *wil) +{ + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + wmi_event_flush(wil); + destroy_workqueue(wil->wmi_wq_conn); + destroy_workqueue(wil->wmi_wq); +} + +static void wil_target_reset(struct wil6210_priv *wil) +{ + wil_dbg(wil, "Resetting...\n"); + + /* register write */ +#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) + /* register set = read, OR, write */ +#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ + wil->csr + HOSTADDR(a)) + + /* hpal_perst_from_pad_src_n_mask */ + S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); + /* car_perst_rst_src_n_mask */ + S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); + + W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ + W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ + + msleep(100); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); + + msleep(100); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + + msleep(2000); + + W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */ + + msleep(2000); + + wil_dbg(wil, "Reset completed\n"); + +#undef W +#undef S +} + +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) +{ + le32_to_cpus(&r->base); + le16_to_cpus(&r->entry_size); + le16_to_cpus(&r->size); + le32_to_cpus(&r->tail); + le32_to_cpus(&r->head); +} + +static int wil_wait_for_fw_ready(struct wil6210_priv *wil) +{ + ulong to = msecs_to_jiffies(1000); + ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); + if (0 == left) { + wil_err(wil, "Firmware not ready\n"); + return -ETIME; + } else { + wil_dbg(wil, "FW ready after %d ms\n", + jiffies_to_msecs(to-left)); + } + return 0; +} + +/* + * We reset all the structures, and we reset the UMAC. + * After calling this routine, you're expected to reload + * the firmware. + */ +int wil_reset(struct wil6210_priv *wil) +{ + int rc; + + cancel_work_sync(&wil->disconnect_worker); + wil6210_disconnect(wil, NULL); + + wmi_event_flush(wil); + + flush_workqueue(wil->wmi_wq); + flush_workqueue(wil->wmi_wq_conn); + + wil6210_disable_irq(wil); + wil->status = 0; + + /* TODO: put MAC in reset */ + wil_target_reset(wil); + + /* init after reset */ + wil->pending_connect_cid = -1; + INIT_COMPLETION(wil->wmi_ready); + + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + + /* TODO: release MAC reset */ + wil6210_enable_irq(wil); + + /* we just started MAC, wait for FW ready */ + rc = wil_wait_for_fw_ready(wil); + + return rc; +} + + +void wil_link_on(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + wil_dbg(wil, "%s()\n", __func__); + + netif_carrier_on(ndev); + netif_tx_wake_all_queues(ndev); +} + +void wil_link_off(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + wil_dbg(wil, "%s()\n", __func__); + + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); +} + +static int __wil_up(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct ieee80211_channel *channel = wdev->preset_chandef.chan; + int rc; + int bi; + u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + + rc = wil_reset(wil); + if (rc) + return rc; + + /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ + wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + wil_dbg(wil, "type: STATION\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_AP: + wil_dbg(wil, "type: AP\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_CLIENT: + wil_dbg(wil, "type: P2P_CLIENT\n"); + bi = 0; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_P2P_GO: + wil_dbg(wil, "type: P2P_GO\n"); + bi = 100; + ndev->type = ARPHRD_ETHER; + break; + case NL80211_IFTYPE_MONITOR: + wil_dbg(wil, "type: Monitor\n"); + bi = 0; + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ + break; + default: + return -EOPNOTSUPP; + } + + /* Apply profile in the following order: */ + /* SSID and channel for the AP */ + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (wdev->ssid_len == 0) { + wil_err(wil, "SSID not set\n"); + return -EINVAL; + } + wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); + if (channel) + wmi_set_channel(wil, channel->hw_value); + break; + default: + ; + } + + /* MAC address - pre-requisite for other commands */ + wmi_set_mac_address(wil, ndev->dev_addr); + + /* Set up beaconing if required. */ + rc = wmi_set_bcon(wil, bi, wmi_nettype); + if (rc) + return rc; + + /* Rx VRING. After MAC and beacon */ + wil_rx_init(wil); + + return 0; +} + +int wil_up(struct wil6210_priv *wil) +{ + int rc; + + mutex_lock(&wil->mutex); + rc = __wil_up(wil); + mutex_unlock(&wil->mutex); + + return rc; +} + +static int __wil_down(struct wil6210_priv *wil) +{ + if (wil->scan_request) { + cfg80211_scan_done(wil->scan_request, true); + wil->scan_request = NULL; + } + + wil6210_disconnect(wil, NULL); + wil_rx_fini(wil); + + return 0; +} + +int wil_down(struct wil6210_priv *wil) +{ + int rc; + + mutex_lock(&wil->mutex); + rc = __wil_down(wil); + mutex_unlock(&wil->mutex); + + return rc; +} diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c new file mode 100644 index 000000000000..3068b5cb53a7 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "wil6210.h" + +static int wil_open(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + return wil_up(wil); +} + +static int wil_stop(struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + return wil_down(wil); +} + +/* + * AC to queue mapping + * + * AC_VO -> queue 3 + * AC_VI -> queue 2 + * AC_BE -> queue 1 + * AC_BK -> queue 0 + */ +static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) +{ + static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; + struct wil6210_priv *wil = ndev_to_wil(ndev); + u16 rc; + + skb->priority = cfg80211_classify8021d(skb); + + rc = wil_1d_to_queue[skb->priority]; + + wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority, + (int)rc); + + return rc; +} + +static const struct net_device_ops wil_netdev_ops = { + .ndo_open = wil_open, + .ndo_stop = wil_stop, + .ndo_start_xmit = wil_start_xmit, + .ndo_select_queue = wil_select_queue, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +void *wil_if_alloc(struct device *dev, void __iomem *csr) +{ + struct net_device *ndev; + struct wireless_dev *wdev; + struct wil6210_priv *wil; + struct ieee80211_channel *ch; + int rc = 0; + + wdev = wil_cfg80211_init(dev); + if (IS_ERR(wdev)) { + dev_err(dev, "wil_cfg80211_init failed\n"); + return wdev; + } + + wil = wdev_to_wil(wdev); + wil->csr = csr; + wil->wdev = wdev; + + rc = wil_priv_init(wil); + if (rc) { + dev_err(dev, "wil_priv_init failed\n"); + goto out_wdev; + } + + wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ + /* default monitor channel */ + ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; + cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); + + ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); + if (!ndev) { + dev_err(dev, "alloc_netdev_mqs failed\n"); + rc = -ENOMEM; + goto out_priv; + } + + ndev->netdev_ops = &wil_netdev_ops; + ndev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); + wdev->netdev = ndev; + + wil_link_off(wil); + + return wil; + + out_priv: + wil_priv_deinit(wil); + + out_wdev: + wil_wdev_free(wil); + + return ERR_PTR(rc); +} + +void wil_if_free(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + if (!ndev) + return; + + free_netdev(ndev); + wil_priv_deinit(wil); + wil_wdev_free(wil); +} + +int wil_if_add(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + int rc; + + rc = register_netdev(ndev); + if (rc < 0) { + dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); + return rc; + } + + wil_link_off(wil); + + return 0; +} + +void wil_if_remove(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + + unregister_netdev(ndev); +} diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c new file mode 100644 index 000000000000..0fc83edd6bad --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" + +static int use_msi = 1; +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, + " Use MSI interrupt: " + "0 - don't, 1 - (default) - single, or 3"); + +/* Bus ops */ +static int wil_if_pcie_enable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + int rc; + + pci_set_master(pdev); + + /* + * how many MSI interrupts to request? + */ + switch (use_msi) { + case 3: + case 1: + case 0: + break; + default: + wil_err(wil, "Invalid use_msi=%d, default to 1\n", + use_msi); + use_msi = 1; + } + wil->n_msi = use_msi; + if (wil->n_msi) { + wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi); + rc = pci_enable_msi_block(pdev, wil->n_msi); + if (rc && (wil->n_msi == 3)) { + wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); + wil->n_msi = 1; + rc = pci_enable_msi_block(pdev, wil->n_msi); + } + if (rc) { + wil_err(wil, "pci_enable_msi failed, use INTx\n"); + wil->n_msi = 0; + } + } else { + wil_dbg(wil, "MSI interrupts disabled, use INTx\n"); + } + + rc = wil6210_init_irq(wil, pdev->irq); + if (rc) + goto stop_master; + + /* need reset here to obtain MAC */ + rc = wil_reset(wil); + if (rc) + goto release_irq; + + return 0; + + release_irq: + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + stop_master: + pci_clear_master(pdev); + return rc; +} + +static int wil_if_pcie_disable(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + + pci_clear_master(pdev); + /* disable and release IRQ */ + wil6210_fini_irq(wil, pdev->irq); + /* safe to call if no MSI */ + pci_disable_msi(pdev); + /* TODO: disable HW */ + + return 0; +} + +static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct wil6210_priv *wil; + struct device *dev = &pdev->dev; + void __iomem *csr; + int rc; + + /* check HW */ + dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", + (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); + + if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { + dev_err(&pdev->dev, "Not " WIL_NAME "? " + "BAR0 size is %lu while expecting %lu\n", + (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); + return -ENODEV; + } + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + /* rollback to err_disable_pdev */ + + rc = pci_request_region(pdev, 0, WIL_NAME); + if (rc) { + dev_err(&pdev->dev, "pci_request_region failed\n"); + goto err_disable_pdev; + } + /* rollback to err_release_reg */ + + csr = pci_ioremap_bar(pdev, 0); + if (!csr) { + dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); + rc = -ENODEV; + goto err_release_reg; + } + /* rollback to err_iounmap */ + dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); + + wil = wil_if_alloc(dev, csr); + if (IS_ERR(wil)) { + rc = (int)PTR_ERR(wil); + dev_err(dev, "wil_if_alloc failed: %d\n", rc); + goto err_iounmap; + } + /* rollback to if_free */ + + pci_set_drvdata(pdev, wil); + wil->pdev = pdev; + + /* FW should raise IRQ when ready */ + rc = wil_if_pcie_enable(wil); + if (rc) { + wil_err(wil, "Enable device failed\n"); + goto if_free; + } + /* rollback to bus_disable */ + + rc = wil_if_add(wil); + if (rc) { + wil_err(wil, "wil_if_add failed: %d\n", rc); + goto bus_disable; + } + + wil6210_debugfs_init(wil); + + /* check FW is alive */ + wmi_echo(wil); + + return 0; + + bus_disable: + wil_if_pcie_disable(wil); + if_free: + wil_if_free(wil); + err_iounmap: + pci_iounmap(pdev, csr); + err_release_reg: + pci_release_region(pdev, 0); + err_disable_pdev: + pci_disable_device(pdev); + + return rc; +} + +static void wil_pcie_remove(struct pci_dev *pdev) +{ + struct wil6210_priv *wil = pci_get_drvdata(pdev); + + wil6210_debugfs_remove(wil); + wil_if_pcie_disable(wil); + wil_if_remove(wil); + wil_if_free(wil); + pci_iounmap(pdev, wil->csr); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { + { PCI_DEVICE(0x1ae9, 0x0301) }, + { /* end: all zeroes */ }, +}; +MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); + +static struct pci_driver wil6210_driver = { + .probe = wil_pcie_probe, + .remove = wil_pcie_remove, + .id_table = wil6210_pcie_ids, + .name = WIL_NAME, +}; + +module_pci_driver(wil6210_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Qualcomm Atheros "); +MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c new file mode 100644 index 000000000000..f29c294413cf --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" +#include "txrx.h" + +static bool rtap_include_phy_info; +module_param(rtap_include_phy_info, bool, S_IRUGO); +MODULE_PARM_DESC(rtap_include_phy_info, + " Include PHY info in the radiotap header, default - no"); + +static inline int wil_vring_is_empty(struct vring *vring) +{ + return vring->swhead == vring->swtail; +} + +static inline u32 wil_vring_next_tail(struct vring *vring) +{ + return (vring->swtail + 1) % vring->size; +} + +static inline void wil_vring_advance_head(struct vring *vring, int n) +{ + vring->swhead = (vring->swhead + n) % vring->size; +} + +static inline int wil_vring_is_full(struct vring *vring) +{ + return wil_vring_next_tail(vring) == vring->swhead; +} +/* + * Available space in Tx Vring + */ +static inline int wil_vring_avail_tx(struct vring *vring) +{ + u32 swhead = vring->swhead; + u32 swtail = vring->swtail; + int used = (vring->size + swhead - swtail) % vring->size; + + return vring->size - used - 1; +} + +static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + uint i; + + BUILD_BUG_ON(sizeof(vring->va[0]) != 32); + + vring->swhead = 0; + vring->swtail = 0; + vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); + if (!vring->ctx) { + wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n", + vring->size); + vring->va = NULL; + return -ENOMEM; + } + /* + * vring->va should be aligned on its size rounded up to power of 2 + * This is granted by the dma_alloc_coherent + */ + vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); + if (!vring->va) { + wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n", + vring->size); + kfree(vring->ctx); + vring->ctx = NULL; + return -ENOMEM; + } + /* initially, all descriptors are SW owned + * For Tx and Rx, ownership bit is at the same location, thus + * we can use any + */ + for (i = 0; i < vring->size; i++) { + volatile struct vring_tx_desc *d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + } + + wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, + vring->va, (unsigned long long)vring->pa, vring->ctx); + + return 0; +} + +static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, + int tx) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = vring->size * sizeof(vring->va[0]); + + while (!wil_vring_is_empty(vring)) { + if (tx) { + volatile struct vring_tx_desc *d = + &vring->va[vring->swtail].tx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, + DMA_TO_DEVICE); + } + vring->swtail = wil_vring_next_tail(vring); + } else { /* rx */ + volatile struct vring_rx_desc *d = + &vring->va[vring->swtail].rx; + dma_addr_t pa = d->dma.addr_low | + ((u64)d->dma.addr_high << 32); + struct sk_buff *skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, d->dma.length, + DMA_FROM_DEVICE); + kfree_skb(skb); + wil_vring_advance_head(vring, 1); + } + } + dma_free_coherent(dev, sz, (void *)vring->va, vring->pa); + kfree(vring->ctx); + vring->pa = 0; + vring->va = NULL; + vring->ctx = NULL; +} + +/** + * Allocate one skb for Rx VRING + * + * Safe to call from IRQ + */ +static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, + u32 i, int headroom) +{ + struct device *dev = wil_to_dev(wil); + unsigned int sz = RX_BUF_LEN; + volatile struct vring_rx_desc *d = &(vring->va[i].rx); + dma_addr_t pa; + + /* TODO align */ + struct sk_buff *skb = dev_alloc_skb(sz + headroom); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, headroom); + skb_put(skb, sz); + + pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) { + kfree_skb(skb); + return -ENOMEM; + } + + d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + /* ip_length don't care */ + /* b11 don't care */ + /* error don't care */ + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = sz; + vring->ctx[i] = skb; + + return 0; +} + +/** + * Adds radiotap header + * + * Any error indicated as "Bad FCS" + * + * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: + * - Rx descriptor: 32 bytes + * - Phy info + */ +static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, + struct sk_buff *skb, + volatile struct vring_rx_desc *d) +{ + struct wireless_dev *wdev = wil->wdev; + struct wil6210_rtap { + struct ieee80211_radiotap_header rthdr; + /* fields should be in the order of bits in rthdr.it_present */ + /* flags */ + u8 flags; + /* channel */ + __le16 chnl_freq __aligned(2); + __le16 chnl_flags; + /* MCS */ + u8 mcs_present; + u8 mcs_flags; + u8 mcs_index; + } __packed; + struct wil6210_rtap_vendor { + struct wil6210_rtap rtap; + /* vendor */ + u8 vendor_oui[3] __aligned(2); + u8 vendor_ns; + __le16 vendor_skip; + u8 vendor_data[0]; + } __packed; + struct wil6210_rtap_vendor *rtap_vendor; + int rtap_len = sizeof(struct wil6210_rtap); + int phy_length = 0; /* phy info header size, bytes */ + static char phy_data[128]; + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + if (rtap_include_phy_info) { + rtap_len = sizeof(*rtap_vendor) + sizeof(*d); + /* calculate additional length */ + if (d->dma.status & RX_DMA_STATUS_PHY_INFO) { + /** + * PHY info starts from 8-byte boundary + * there are 8-byte lines, last line may be partially + * written (HW bug), thus FW configures for last line + * to be excessive. Driver skips this last line. + */ + int len = min_t(int, 8 + sizeof(phy_data), + wil_rxdesc_phy_length(d)); + if (len > 8) { + void *p = skb_tail_pointer(skb); + void *pa = PTR_ALIGN(p, 8); + if (skb_tailroom(skb) >= len + (pa - p)) { + phy_length = len - 8; + memcpy(phy_data, pa, phy_length); + } + } + } + rtap_len += phy_length; + } + + if (skb_headroom(skb) < rtap_len && + pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { + wil_err(wil, "Unable to expand headrom to %d\n", rtap_len); + return; + } + + rtap_vendor = (void *)skb_push(skb, rtap_len); + memset(rtap_vendor, 0, rtap_len); + + rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION; + rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len); + rtap_vendor->rtap.rthdr.it_present = cpu_to_le32( + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_MCS)); + if (d->dma.status & RX_DMA_STATUS_ERROR) + rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS; + + rtap_vendor->rtap.chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320); + rtap_vendor->rtap.chnl_flags = cpu_to_le16(0); + + rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; + rtap_vendor->rtap.mcs_flags = 0; + rtap_vendor->rtap.mcs_index = wil_rxdesc_mcs(d); + + if (rtap_include_phy_info) { + rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 << + IEEE80211_RADIOTAP_VENDOR_NAMESPACE); + /* OUI for Wilocity 04:ce:14 */ + rtap_vendor->vendor_oui[0] = 0x04; + rtap_vendor->vendor_oui[1] = 0xce; + rtap_vendor->vendor_oui[2] = 0x14; + rtap_vendor->vendor_ns = 1; + /* Rx descriptor + PHY data */ + rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) + + phy_length); + memcpy(rtap_vendor->vendor_data, (void *)d, sizeof(*d)); + memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data, + phy_length); + } +} + +/* + * Fast swap in place between 2 registers + */ +static void wil_swap_u16(u16 *a, u16 *b) +{ + *a ^= *b; + *b ^= *a; + *a ^= *b; +} + +static void wil_swap_ethaddr(void *data) +{ + struct ethhdr *eth = data; + u16 *s = (u16 *)eth->h_source; + u16 *d = (u16 *)eth->h_dest; + + wil_swap_u16(s++, d++); + wil_swap_u16(s++, d++); + wil_swap_u16(s, d); +} + +/** + * reap 1 frame from @swhead + * + * Safe to call from IRQ + */ +static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, + struct vring *vring) +{ + struct device *dev = wil_to_dev(wil); + struct net_device *ndev = wil_to_ndev(wil); + volatile struct vring_rx_desc *d; + struct sk_buff *skb; + dma_addr_t pa; + unsigned int sz = RX_BUF_LEN; + u8 ftype; + u8 ds_bits; + + if (wil_vring_is_empty(vring)) + return NULL; + + d = &(vring->va[vring->swhead].rx); + if (!(d->dma.status & RX_DMA_STATUS_DU)) { + /* it is not error, we just reached end of Rx done area */ + return NULL; + } + + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swhead]; + dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); + skb_trim(skb, d->dma.length); + + wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); + + /* use radiotap header only if required */ + if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) + wil_rx_add_radiotap_header(wil, skb, d); + + wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); + wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + wil_vring_advance_head(vring, 1); + + /* no extra checks if in sniffer mode */ + if (ndev->type != ARPHRD_ETHER) + return skb; + /* + * Non-data frames may be delivered through Rx DMA channel (ex: BAR) + * Driver should recognize it by frame type, that is found + * in Rx descriptor. If type is not data, it is 802.11 frame as is + */ + ftype = wil_rxdesc_ftype(d) << 2; + if (ftype != IEEE80211_FTYPE_DATA) { + wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype); + /* TODO: process it */ + kfree_skb(skb); + return NULL; + } + + if (skb->len < ETH_HLEN) { + wil_err(wil, "Short frame, len = %d\n", skb->len); + /* TODO: process it (i.e. BAR) */ + kfree_skb(skb); + return NULL; + } + + ds_bits = wil_rxdesc_ds_bits(d); + if (ds_bits == 1) { + /* + * HW bug - in ToDS mode, i.e. Rx on AP side, + * addresses get swapped + */ + wil_swap_ethaddr(skb->data); + } + + return skb; +} + +/** + * allocate and fill up to @count buffers in rx ring + * buffers posted at @swtail + */ +static int wil_rx_refill(struct wil6210_priv *wil, int count) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + u32 next_tail; + int rc = 0; + int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? + WIL6210_RTAP_SIZE : 0; + + for (; next_tail = wil_vring_next_tail(v), + (next_tail != v->swhead) && (count-- > 0); + v->swtail = next_tail) { + rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom); + if (rc) { + wil_err(wil, "Error %d in wil_rx_refill[%d]\n", + rc, v->swtail); + break; + } + } + iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail)); + + return rc; +} + +/* + * Pass Rx packet to the netif. Update statistics. + */ +static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) +{ + int rc; + unsigned int len = skb->len; + + if (in_interrupt()) + rc = netif_rx(skb); + else + rc = netif_rx_ni(skb); + + if (likely(rc == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + + } else { + ndev->stats.rx_dropped++; + } +} + +/** + * Proceed all completed skb's from Rx VRING + * + * Safe to call from IRQ + */ +void wil_rx_handle(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring *v = &wil->vring_rx; + struct sk_buff *skb; + + if (!v->va) { + wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); + return; + } + wil_dbg_TXRX(wil, "%s()\n", __func__); + while (NULL != (skb = wil_vring_reap_rx(wil, v))) { + wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + skb_orphan(skb); + + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + skb->dev = ndev; + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + + } else { + skb->protocol = eth_type_trans(skb, ndev); + } + + wil_netif_rx_any(skb, ndev); + } + wil_rx_refill(wil, v->size); +} + +int wil_rx_init(struct wil6210_priv *wil) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct vring *vring = &wil->vring_rx; + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = WMI_RX_CHAIN_ADD, + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + .mid = 0, /* TODO - what is it? */ + .decap_trans_type = WMI_DECAP_TYPE_802_3, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event evt; + } __packed evt; + + vring->size = WIL6210_RX_RING_SIZE; + rc = wil_vring_alloc(wil, vring); + if (rc) + return rc; + + cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size); + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); + if (ch) + cmd.sniffer_cfg.channel = ch->hw_value - 1; + cmd.sniffer_cfg.phy_info_mode = + cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); + cmd.sniffer_cfg.phy_support = + cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) + ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); + } + /* typical time for secure PCP is 840ms */ + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); + if (rc) + goto err_free; + + vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); + + wil_dbg(wil, "Rx init: status %d tail 0x%08x\n", + le32_to_cpu(evt.evt.status), vring->hwtail); + + rc = wil_rx_refill(wil, vring->size); + if (rc) + goto err_free; + + return 0; + err_free: + wil_vring_free(wil, vring, 0); + + return rc; +} + +void wil_rx_fini(struct wil6210_priv *wil) +{ + struct vring *vring = &wil->vring_rx; + + if (vring->va) { + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = cpu_to_le32(WMI_RX_CHAIN_DEL), + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event cfg; + } __packed wmi_rx_cfg_reply; + + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, + &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), + 100); + wil_vring_free(wil, vring, 0); + } +} + +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid) +{ + int rc; + struct wmi_vring_cfg_cmd cmd = { + .action = cpu_to_le32(WMI_VRING_CMD_ADD), + .vring_cfg = { + .tx_sw_ring = { + .max_mpdu_size = cpu_to_le16(TX_BUF_LEN), + }, + .ringid = id, + .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), + .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, + .mac_ctrl = 0, + .to_resolution = 0, + .agg_max_wsize = 16, + .schd_params = { + .priority = cpu_to_le16(0), + .timeslot_us = cpu_to_le16(0xfff), + }, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_vring_cfg_done_event cmd; + } __packed reply; + struct vring *vring = &wil->vring_tx[id]; + + if (vring->va) { + wil_err(wil, "Tx ring [%d] already allocated\n", id); + rc = -EINVAL; + goto out; + } + + vring->size = size; + rc = wil_vring_alloc(wil, vring); + if (rc) + goto out; + + cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size); + + rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + if (rc) + goto out_free; + + if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) { + wil_err(wil, "Tx config failed, status 0x%02x\n", + reply.cmd.status); + goto out_free; + } + vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + + return 0; + out_free: + wil_vring_free(wil, vring, 1); + out: + + return rc; +} + +void wil_vring_fini_tx(struct wil6210_priv *wil, int id) +{ + struct vring *vring = &wil->vring_tx[id]; + + if (!vring->va) + return; + + wil_vring_free(wil, vring, 1); +} + +static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct vring *v = &wil->vring_tx[0]; + + if (v->va) + return v; + + return NULL; +} + +static int wil_tx_desc_map(volatile struct vring_tx_desc *d, + dma_addr_t pa, u32 len) +{ + d->dma.addr_low = lower_32_bits(pa); + d->dma.addr_high = (u16)upper_32_bits(pa); + d->dma.ip_length = 0; + /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ + d->dma.b11 = 0/*14 | BIT(7)*/; + d->dma.error = 0; + d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ + d->dma.length = len; + d->dma.d0 = 0; + d->mac.d[0] = 0; + d->mac.d[1] = 0; + d->mac.d[2] = 0; + d->mac.ucode_cmd = 0; + /* use dst index 0 */ + d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) | + (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS); + /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ + d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | + (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); + + return 0; +} + +static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, + struct sk_buff *skb) +{ + struct device *dev = wil_to_dev(wil); + volatile struct vring_tx_desc *d; + u32 swhead = vring->swhead; + int avail = wil_vring_avail_tx(vring); + int nr_frags = skb_shinfo(skb)->nr_frags; + uint f; + int vring_index = vring - wil->vring_tx; + uint i = swhead; + dma_addr_t pa; + + wil_dbg_TXRX(wil, "%s()\n", __func__); + + if (avail < vring->size/8) + netif_tx_stop_all_queues(wil_to_ndev(wil)); + if (avail < 1 + nr_frags) { + wil_err(wil, "Tx ring full. No space for %d fragments\n", + 1 + nr_frags); + return -ENOMEM; + } + d = &(vring->va[i].tx); + + /* FIXME FW can accept only unicast frames for the peer */ + memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); + + pa = dma_map_single(dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + + wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), + skb->data, (unsigned long long)pa); + wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + if (unlikely(dma_mapping_error(dev, pa))) + return -EINVAL; + /* 1-st segment */ + wil_tx_desc_map(d, pa, skb_headlen(skb)); + d->mac.d[2] |= ((nr_frags + 1) << + MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); + /* middle segments */ + for (f = 0; f < nr_frags; f++) { + const struct skb_frag_struct *frag = + &skb_shinfo(skb)->frags[f]; + int len = skb_frag_size(frag); + i = (swhead + f + 1) % vring->size; + d = &(vring->va[i].tx); + pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) + goto dma_error; + wil_tx_desc_map(d, pa, len); + vring->ctx[i] = NULL; + } + /* for the last seg only */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); + d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ + d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); + d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); + + wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + /* advance swhead */ + wil_vring_advance_head(vring, nr_frags + 1); + wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); + iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); + /* hold reference to skb + * to prevent skb release before accounting + * in case of immediate "tx done" + */ + vring->ctx[i] = skb_get(skb); + + return 0; + dma_error: + /* unmap what we have mapped */ + /* Note: increment @f to operate with positive index */ + for (f++; f > 0; f--) { + i = (swhead + f) % vring->size; + d = &(vring->va[i].tx); + d->dma.status = TX_DMA_STATUS_DU; + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + if (vring->ctx[i]) + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + else + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + + return -EINVAL; +} + + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + struct vring *vring; + int rc; + + wil_dbg_TXRX(wil, "%s()\n", __func__); + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + goto drop; + } + if (!test_bit(wil_status_fwconnected, &wil->status)) { + wil_err(wil, "FW not connected\n"); + goto drop; + } + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + wil_err(wil, "Xmit in monitor mode not supported\n"); + goto drop; + } + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + rc = wmi_tx_eapol(wil, skb); + } else { + /* find vring */ + vring = wil_find_tx_vring(wil, skb); + if (!vring) { + wil_err(wil, "No Tx VRING available\n"); + goto drop; + } + /* set up vring entry */ + rc = wil_tx_vring(wil, vring, skb); + } + switch (rc) { + case 0: + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + case -ENOMEM: + return NETDEV_TX_BUSY; + default: + ; /* goto drop; */ + break; + } + drop: + netif_tx_stop_all_queues(ndev); + ndev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + + return NET_XMIT_DROP; +} + +/** + * Clean up transmitted skb's from the Tx VRING + * + * Safe to call from IRQ + */ +void wil_tx_complete(struct wil6210_priv *wil, int ringid) +{ + struct device *dev = wil_to_dev(wil); + struct vring *vring = &wil->vring_tx[ringid]; + + if (!vring->va) { + wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); + return; + } + + wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid); + + while (!wil_vring_is_empty(vring)) { + volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; + dma_addr_t pa; + struct sk_buff *skb; + if (!(d->dma.status & TX_DMA_STATUS_DU)) + break; + + wil_dbg_TXRX(wil, + "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", + vring->swtail, d->dma.length, d->dma.status, + d->dma.error); + wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); + + pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + skb = vring->ctx[vring->swtail]; + if (skb) { + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + vring->ctx[vring->swtail] = NULL; + } else { + dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + } + d->dma.addr_low = 0; + d->dma.addr_high = 0; + d->dma.length = 0; + d->dma.status = TX_DMA_STATUS_DU; + vring->swtail = wil_vring_next_tail(vring); + } + if (wil_vring_avail_tx(vring) > vring->size/4) + netif_tx_wake_all_queues(wil_to_ndev(wil)); +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h new file mode 100644 index 000000000000..45a61f597c5c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WIL6210_TXRX_H +#define WIL6210_TXRX_H + +#define BUF_SW_OWNED (1) +#define BUF_HW_OWNED (0) + +/* size of max. Rx packet */ +#define RX_BUF_LEN (2048) +#define TX_BUF_LEN (2048) +/* how many bytes to reserve for rtap header? */ +#define WIL6210_RTAP_SIZE (128) + +/* Tx/Rx path */ +/* + * Tx descriptor - MAC part + * [dword 0] + * bit 0.. 9 : lifetime_expiry_value:10 + * bit 10 : interrup_en:1 + * bit 11 : status_en:1 + * bit 12..13 : txss_override:2 + * bit 14 : timestamp_insertion:1 + * bit 15 : duration_preserve:1 + * bit 16..21 : reserved0:6 + * bit 22..26 : mcs_index:5 + * bit 27 : mcs_en:1 + * bit 28..29 : reserved1:2 + * bit 30 : reserved2:1 + * bit 31 : sn_preserved:1 + * [dword 1] + * bit 0.. 3 : pkt_mode:4 + * bit 4 : pkt_mode_en:1 + * bit 5.. 7 : reserved0:3 + * bit 8..13 : reserved1:6 + * bit 14 : reserved2:1 + * bit 15 : ack_policy_en:1 + * bit 16..19 : dst_index:4 + * bit 20 : dst_index_en:1 + * bit 21..22 : ack_policy:2 + * bit 23 : lifetime_en:1 + * bit 24..30 : max_retry:7 + * bit 31 : max_retry_en:1 + * [dword 2] + * bit 0.. 7 : num_of_descriptors:8 + * bit 8..17 : reserved:10 + * bit 18..19 : l2_translation_type:2 + * bit 20 : snap_hdr_insertion_en:1 + * bit 21 : vlan_removal_en:1 + * bit 22..31 : reserved0:10 + * [dword 3] + * bit 0.. 31: ucode_cmd:32 + */ +struct vring_tx_mac { + u32 d[3]; + u32 ucode_cmd; +} __packed; + +/* TX MAC Dword 0 */ +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF + +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400 + +#define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11 +#define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800 + +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000 + +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000 + +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000 + +#define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000 + +#define MAC_CFG_DESC_TX_0_MCS_EN_POS 27 +#define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000 + +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000 + +/* TX MAC Dword 1 */ +#define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0 +#define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF + +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16 +#define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4 +#define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000 + +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000 + +/* TX MAC Dword 2 */ +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF + +#define MAC_CFG_DESC_TX_2_RESERVED_POS 8 +#define MAC_CFG_DESC_TX_2_RESERVED_LEN 10 +#define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00 + +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000 + +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000 + +/* TX MAC Dword 3 */ +#define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF + +/* TX DMA Dword 0 */ +#define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF + +#define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8 +#define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100 + +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400 + +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800 + +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000 + +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000 + +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000 + +#define DMA_CFG_DESC_TX_0_QID_POS 16 +#define DMA_CFG_DESC_TX_0_QID_LEN 5 +#define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000 + +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000 + +#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30 +#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2 +#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 + + +#define TX_DMA_STATUS_DU BIT(0) + +struct vring_tx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; /* 0..6: mac_length; 7:ip_version */ + u8 error; /* 0..2: err; 3..7: reserved; */ + u8 status; /* 0: used; 1..7; reserved */ + u16 length; +} __packed; + +/* + * Rx descriptor - MAC part + * [dword 0] + * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field + * bit 4.. 6 : connection_id:3 :The Source index that was found during + * Parsing the TA. This field is used to define the source of the packet + * bit 7 : reserved:1 + * bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero) + * bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type + * (management, data, control and extension) + * bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype + * bit 16..27 : seq_number:12 The received Sequence number field + * bit 28..31 : extended:4 extended subtype + * [dword 1] + * bit 0.. 3 : reserved + * bit 4.. 5 : key_id:2 + * bit 6 : decrypt_bypass:1 + * bit 7 : security:1 + * bit 8.. 9 : ds_bits:2 + * bit 10 : a_msdu_present:1 from qos header + * bit 11 : a_msdu_type:1 from qos header + * bit 12 : a_mpdu:1 part of AMPDU aggregation + * bit 13 : broadcast:1 + * bit 14 : mutlicast:1 + * bit 15 : reserved:1 + * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet + * is received from + * bit 21..24 : mcs:4 + * bit 25..28 : mic_icr:4 + * bit 29..31 : reserved:3 + * [dword 2] + * bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received + * bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version + * bit 4 : fc_order:1 The FC Control (b15) -Order + * bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field + * bit 8 : esop:1 The QoS (b4) ESOP field + * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field + * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field + * bit 15 : qos_ac_constraint:1 + * bit 16..31 : pn_15_0:16 low 2 bytes of PN + * [dword 3] + * bit 0..31 : pn_47_16:32 high 4 bytes of PN + */ +struct vring_rx_mac { + u32 d0; + u32 d1; + u16 w4; + u16 pn_15_0; + u32 pn_47_16; +} __packed; + +/* + * Rx descriptor - DMA part + * [dword 0] + * bit 0.. 7 : l4_length:8 layer 4 length + * bit 8.. 9 : reserved:2 + * bit 10 : cmd_dma_it:1 + * bit 11..15 : reserved:5 + * bit 16..29 : phy_info_length:14 + * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field + * [dword 1] + * bit 0..31 : addr_low:32 The payload buffer low address + * [dword 2] + * bit 0..15 : addr_high:16 The payload buffer high address + * bit 16..23 : ip_length:8 + * bit 24..30 : mac_length:7 + * bit 31 : ip_version:1 + * [dword 3] + * [byte 12] error + * [byte 13] status + * bit 0 : du:1 + * bit 1 : eop:1 + * bit 2 : error:1 + * bit 3 : mi:1 + * bit 4 : l3_identified:1 + * bit 5 : l4_identified:1 + * bit 6 : phy_info_included:1 + * bit 7 : reserved:1 + * [word 7] length + * + */ + +#define RX_DMA_D0_CMD_DMA_IT BIT(10) + +#define RX_DMA_STATUS_DU BIT(0) +#define RX_DMA_STATUS_ERROR BIT(2) +#define RX_DMA_STATUS_PHY_INFO BIT(6) + +struct vring_rx_dma { + u32 d0; + u32 addr_low; + u16 addr_high; + u8 ip_length; + u8 b11; + u8 error; + u8 status; + u16 length; +} __packed; + +struct vring_tx_desc { + struct vring_tx_mac mac; + struct vring_tx_dma dma; +} __packed; + +struct vring_rx_desc { + struct vring_rx_mac mac; + struct vring_rx_dma dma; +} __packed; + +union vring_desc { + struct vring_tx_desc tx; + struct vring_rx_desc rx; +} __packed; + +static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->dma.d0, 16, 29); +} + +static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 21, 24); +} + +static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 8, 9); +} + +static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d0, 10, 11); +} + +#endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h new file mode 100644 index 000000000000..9bcfffa4006c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WIL6210_H__ +#define __WIL6210_H__ + +#include +#include +#include + +#include "dbg_hexdump.h" + +#define WIL_NAME "wil6210" + +/** + * extract bits [@b0:@b1] (inclusive) from the value @x + * it should be @b0 <= @b1, or result is incorrect + */ +static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) +{ + return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); +} + +#define WIL6210_MEM_SIZE (2*1024*1024UL) + +#define WIL6210_TX_QUEUES (4) + +#define WIL6210_RX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_MAX_TX_RINGS (24) + +/* Hardware definitions begin */ + +/* + * Mapping + * RGF File | Host addr | FW addr + * | | + * user_rgf | 0x000000 | 0x880000 + * dma_rgf | 0x001000 | 0x881000 + * pcie_rgf | 0x002000 | 0x882000 + * | | + */ + +/* Where various structures placed in host address space */ +#define WIL6210_FW_HOST_OFF (0x880000UL) + +#define HOSTADDR(fwaddr) (fwaddr - WIL6210_FW_HOST_OFF) + +/* + * Interrupt control registers block + * + * each interrupt controlled by the same bit in all registers + */ +struct RGF_ICR { + u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */ + u32 ICR; /* Cause, W1C/COR depending on ICC */ + u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */ + u32 ICS; /* Cause Set, WO */ + u32 IMV; /* Mask, RW+S/C */ + u32 IMS; /* Mask Set, write 1 to set */ + u32 IMC; /* Mask Clear, write 1 to clear */ +} __packed; + +/* registers - FW addresses */ +#define RGF_USER_USER_SCRATCH_PAD (0x8802bc) +#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ + #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) +#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) +#define RGF_USER_MAC_CPU_0 (0x8801fc) +#define RGF_USER_USER_CPU_0 (0x8801e0) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10) + +#define RGF_DMA_PSEUDO_CAUSE (0x881c68) +#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) +#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) + #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) + #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) + #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) + +#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ + #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) + #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */ +#define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */ + #define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0) +#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */ + #define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0) + #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1) + #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28) + #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29) + +/* Interrupt moderation control */ +#define RGF_DMA_ITR_CNT_TRSH (0x881c5c) +#define RGF_DMA_ITR_CNT_DATA (0x881c60) +#define RGF_DMA_ITR_CNT_CRL (0x881C64) + #define BIT_DMA_ITR_CNT_CRL_EN BIT(0) + #define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1) + #define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2) + #define BIT_DMA_ITR_CNT_CRL_CLR BIT(3) + #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4) + +/* popular locations */ +#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) +#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ + offsetof(struct RGF_ICR, ICS)) +#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 + +/* ISR register bits */ +#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 +#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 + +/* Hardware definitions end */ + +struct wil6210_mbox_ring { + u32 base; + u16 entry_size; /* max. size of mbox entry, incl. all headers */ + u16 size; + u32 tail; + u32 head; +} __packed; + +struct wil6210_mbox_ring_desc { + __le32 sync; + __le32 addr; +} __packed; + +/* at HOST_OFF_WIL6210_MBOX_CTL */ +struct wil6210_mbox_ctl { + struct wil6210_mbox_ring tx; + struct wil6210_mbox_ring rx; +} __packed; + +struct wil6210_mbox_hdr { + __le16 seq; + __le16 len; /* payload, bytes after this header */ + __le16 type; + u8 flags; + u8 reserved; +} __packed; + +#define WIL_MBOX_HDR_TYPE_WMI (0) + +/* max. value for wil6210_mbox_hdr.len */ +#define MAX_MBOXITEM_SIZE (240) + +struct wil6210_mbox_hdr_wmi { + u8 reserved0[2]; + __le16 id; + __le16 info1; /* bits [0..3] - device_id, rest - unused */ + u8 reserved1[2]; +} __packed; + +struct pending_wmi_event { + struct list_head list; + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + u8 data[0]; + } __packed event; +}; + +union vring_desc; + +struct vring { + dma_addr_t pa; + volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */ + u16 size; /* number of vring_desc elements */ + u32 swtail; + u32 swhead; + u32 hwtail; /* write here to inform hw */ + void **ctx; /* void *ctx[size] - software context */ +}; + +enum { /* for wil6210_priv.status */ + wil_status_fwready = 0, + wil_status_fwconnected, + wil_status_dontscan, + wil_status_irqen, /* FIXME: interrupts enabled - for debug */ +}; + +struct pci_dev; + +struct wil6210_stats { + u64 tsf; + u32 snr; + u16 last_mcs_rx; + u16 bf_mcs; /* last BF, used for Tx */ + u16 my_rx_sector; + u16 my_tx_sector; + u16 peer_rx_sector; + u16 peer_tx_sector; +}; + +struct wil6210_priv { + struct pci_dev *pdev; + int n_msi; + struct wireless_dev *wdev; + void __iomem *csr; + ulong status; + /* profile */ + u32 monitor_flags; + u32 secure_pcp; /* create secure PCP? */ + int sinfo_gen; + /* cached ISR registers */ + u32 isr_misc; + /* mailbox related */ + struct mutex wmi_mutex; + struct wil6210_mbox_ctl mbox_ctl; + struct completion wmi_ready; + u16 wmi_seq; + u16 reply_id; /**< wait for this WMI event */ + void *reply_buf; + u16 reply_size; + struct workqueue_struct *wmi_wq; /* for deferred calls */ + struct work_struct wmi_event_worker; + struct workqueue_struct *wmi_wq_conn; /* for connect worker */ + struct work_struct wmi_connect_worker; + struct work_struct disconnect_worker; + struct timer_list connect_timer; + int pending_connect_cid; + struct list_head pending_wmi_ev; + /* + * protect pending_wmi_ev + * - fill in IRQ from wil6210_irq_misc, + * - consumed in thread by wmi_event_worker + */ + spinlock_t wmi_ev_lock; + /* DMA related */ + struct vring vring_rx; + struct vring vring_tx[WIL6210_MAX_TX_RINGS]; + u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; + /* scan */ + struct cfg80211_scan_request *scan_request; + + struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ + /* statistics */ + struct wil6210_stats stats; + /* debugfs */ + struct dentry *debug; + struct debugfs_blob_wrapper fw_code_blob; + struct debugfs_blob_wrapper fw_data_blob; + struct debugfs_blob_wrapper fw_peri_blob; + struct debugfs_blob_wrapper uc_code_blob; + struct debugfs_blob_wrapper uc_data_blob; + struct debugfs_blob_wrapper rgf_blob; +}; + +#define wil_to_wiphy(i) (i->wdev->wiphy) +#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) +#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) +#define wil_to_wdev(i) (i->wdev) +#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) +#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) +#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) + +#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) +#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) +#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) + +#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) +#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) +#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) + +#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) + +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, + size_t count); +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, + size_t count); + +void *wil_if_alloc(struct device *dev, void __iomem *csr); +void wil_if_free(struct wil6210_priv *wil); +int wil_if_add(struct wil6210_priv *wil); +void wil_if_remove(struct wil6210_priv *wil); +int wil_priv_init(struct wil6210_priv *wil); +void wil_priv_deinit(struct wil6210_priv *wil); +int wil_reset(struct wil6210_priv *wil); +void wil_link_on(struct wil6210_priv *wil); +void wil_link_off(struct wil6210_priv *wil); +int wil_up(struct wil6210_priv *wil); +int wil_down(struct wil6210_priv *wil); +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); + +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, + struct wil6210_mbox_hdr *hdr); +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len); +void wmi_recv_cmd(struct wil6210_priv *wil); +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, + u16 reply_id, void *reply, u8 reply_size, int to_msec); +void wmi_connect_worker(struct work_struct *work); +void wmi_event_worker(struct work_struct *work); +void wmi_event_flush(struct wil6210_priv *wil); +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); +int wmi_set_channel(struct wil6210_priv *wil, int channel); +int wmi_get_channel(struct wil6210_priv *wil, int *channel); +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr); +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr, int key_len, const void *key); +int wmi_echo(struct wil6210_priv *wil); +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); + +int wil6210_init_irq(struct wil6210_priv *wil, int irq); +void wil6210_fini_irq(struct wil6210_priv *wil, int irq); +void wil6210_disable_irq(struct wil6210_priv *wil); +void wil6210_enable_irq(struct wil6210_priv *wil); + +int wil6210_debugfs_init(struct wil6210_priv *wil); +void wil6210_debugfs_remove(struct wil6210_priv *wil); + +struct wireless_dev *wil_cfg80211_init(struct device *dev); +void wil_wdev_free(struct wil6210_priv *wil); + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype); +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); + +int wil_rx_init(struct wil6210_priv *wil); +void wil_rx_fini(struct wil6210_priv *wil); + +/* TX API */ +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, + int cid, int tid); +void wil_vring_fini_tx(struct wil6210_priv *wil, int id); + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); +void wil_tx_complete(struct wil6210_priv *wil, int ringid); + +/* RX API */ +void wil_rx_handle(struct wil6210_priv *wil); + +int wil_iftype_nl2wmi(enum nl80211_iftype type); + +#endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c new file mode 100644 index 000000000000..12915f6e7617 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "wil6210.h" +#include "wmi.h" + +/** + * WMI event receiving - theory of operations + * + * When firmware about to report WMI event, it fills memory area + * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for + * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. + * + * @wmi_recv_cmd reads event, allocates memory chunk and attaches it to the + * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up + * and handles events within the @wmi_event_worker. Every event get detached + * from list, processed and deleted. + * + * Purpose for this mechanism is to release IRQ thread; otherwise, + * if WMI event handling involves another WMI command flow, this 2-nd flow + * won't be completed because of blocked IRQ thread. + */ + +/** + * Addressing - theory of operations + * + * There are several buses present on the WIL6210 card. + * Same memory areas are visible at different address on + * the different busses. There are 3 main bus masters: + * - MAC CPU (ucode) + * - User CPU (firmware) + * - AHB (host) + * + * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing + * AHB addresses starting from 0x880000 + * + * Internally, firmware uses addresses that allows faster access but + * are invisible from the host. To read from these addresses, alternative + * AHB address must be used. + * + * Memory mapping + * Linker address PCI/Host address + * 0x880000 .. 0xa80000 2Mb BAR0 + * 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM + * 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH + */ + +/** + * @fw_mapping provides memory remapping table + */ +static const struct { + u32 from; /* linker address - from, inclusive */ + u32 to; /* linker address - to, exclusive */ + u32 host; /* PCI/Host address - BAR0 + 0x880000 */ +} fw_mapping[] = { + {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ + {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ + {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ + {0x880000, 0x88a000, 0x880000}, /* various RGF */ + {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ + /* + * 920000..930000 ucode code RAM + * 930000..932000 ucode data RAM + */ +}; + +/** + * return AHB address for given firmware/ucode internal (linker) address + * @x - internal address + * If address have no valid AHB mapping, return 0 + */ +static u32 wmi_addr_remap(u32 x) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { + if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) + return x + fw_mapping[i].host - fw_mapping[i].from; + } + + return 0; +} + +/** + * Check address validity for WMI buffer; remap if needed + * @ptr - internal (linker) fw/ucode address + * + * Valid buffer should be DWORD aligned + * + * return address for accessing buffer from the host; + * if buffer is not valid, return NULL. + */ +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) +{ + u32 off; + u32 ptr = le32_to_cpu(ptr_); + + if (ptr % 4) + return NULL; + + ptr = wmi_addr_remap(ptr); + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + + return wil->csr + off; +} + +/** + * Check address validity + */ +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) +{ + u32 off; + + if (ptr % 4) + return NULL; + + if (ptr < WIL6210_FW_HOST_OFF) + return NULL; + + off = HOSTADDR(ptr); + if (off > WIL6210_MEM_SIZE - 4) + return NULL; + + return wil->csr + off; +} + +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, + struct wil6210_mbox_hdr *hdr) +{ + void __iomem *src = wmi_buffer(wil, ptr); + if (!src) + return -EINVAL; + + wil_memcpy_fromio_32(hdr, src, sizeof(*hdr)); + + return 0; +} + +static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ + struct { + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_hdr_wmi wmi; + } __packed cmd = { + .hdr = { + .type = WIL_MBOX_HDR_TYPE_WMI, + .flags = 0, + .len = cpu_to_le16(sizeof(cmd.wmi) + len), + }, + .wmi = { + .id = cpu_to_le16(cmdid), + .info1 = 0, + }, + }; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; + struct wil6210_mbox_ring_desc d_head; + u32 next_head; + void __iomem *dst; + void __iomem *head = wmi_addr(wil, r->head); + uint retry; + + if (sizeof(cmd) + len > r->entry_size) { + wil_err(wil, "WMI size too large: %d bytes, max is %d\n", + (int)(sizeof(cmd) + len), r->entry_size); + return -ERANGE; + + } + + might_sleep(); + + if (!test_bit(wil_status_fwready, &wil->status)) { + wil_err(wil, "FW not ready\n"); + return -EAGAIN; + } + + if (!head) { + wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); + return -EINVAL; + } + /* read Tx head till it is not busy */ + for (retry = 5; retry > 0; retry--) { + wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); + if (d_head.sync == 0) + break; + msleep(20); + } + if (d_head.sync != 0) { + wil_err(wil, "WMI head busy\n"); + return -EBUSY; + } + /* next head */ + next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); + wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); + /* wait till FW finish with previous command */ + for (retry = 5; retry > 0; retry--) { + r->tail = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.tail)); + if (next_head != r->tail) + break; + msleep(20); + } + if (next_head == r->tail) { + wil_err(wil, "WMI ring full\n"); + return -EBUSY; + } + dst = wmi_buffer(wil, d_head.addr); + if (!dst) { + wil_err(wil, "invalid WMI buffer: 0x%08x\n", + le32_to_cpu(d_head.addr)); + return -EINVAL; + } + cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); + /* set command */ + wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len); + wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, + sizeof(cmd), true); + wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, + len, true); + wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); + wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); + /* mark entry as full */ + iowrite32(1, wil->csr + HOSTADDR(r->head) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* advance next ptr */ + iowrite32(r->head = next_head, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, tx.head)); + + /* interrupt to FW */ + iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); + + return 0; +} + +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ + int rc; + + mutex_lock(&wil->wmi_mutex); + rc = __wmi_send(wil, cmdid, buf, len); + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +/*=== Event handlers ===*/ +static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct wmi_ready_event *evt = d; + u32 ver = le32_to_cpu(evt->sw_version); + + wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); + + if (!is_valid_ether_addr(ndev->dev_addr)) { + memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); + memcpy(ndev->perm_addr, evt->mac, ETH_ALEN); + } + snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), + "%d", ver); +} + +static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, + int len) +{ + wil_dbg_WMI(wil, "WMI: FW ready\n"); + + set_bit(wil_status_fwready, &wil->status); + /* reuse wmi_ready for the firmware ready indication */ + complete(&wil->wmi_ready); +} + +static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct wmi_rx_mgmt_packet_event *data = d; + struct wiphy *wiphy = wil_to_wiphy(wil); + struct ieee80211_mgmt *rx_mgmt_frame = + (struct ieee80211_mgmt *)data->payload; + int ch_no = data->info.channel+1; + u32 freq = ieee80211_channel_to_frequency(ch_no, + IEEE80211_BAND_60GHZ); + struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); + /* TODO convert LE to CPU */ + s32 signal = 0; /* TODO */ + __le16 fc = rx_mgmt_frame->frame_control; + u32 d_len = le32_to_cpu(data->info.len); + u16 d_status = le16_to_cpu(data->info.status); + + wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n", + data->info.channel, data->info.mcs, data->info.snr); + wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, + le16_to_cpu(data->info.stype)); + wil_dbg_WMI(wil, "qid %d mid %d cid %d\n", + data->info.qid, data->info.mid, data->info.cid); + + if (!channel) { + wil_err(wil, "Frame on unsupported channel\n"); + return; + } + + if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { + struct cfg80211_bss *bss; + u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); + u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); + u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); + const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; + size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, + u.beacon.variable); + wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap); + + bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, + tsf, cap, bi, ie_buf, ie_len, + signal, GFP_KERNEL); + if (bss) { + wil_dbg_WMI(wil, "Added BSS %pM\n", + rx_mgmt_frame->bssid); + cfg80211_put_bss(bss); + } else { + wil_err(wil, "cfg80211_inform_bss() failed\n"); + } + } +} + +static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, + void *d, int len) +{ + if (wil->scan_request) { + struct wmi_scan_complete_event *data = d; + bool aborted = (data->status != 0); + + wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + cfg80211_scan_done(wil->scan_request, aborted); + wil->scan_request = NULL; + } else { + wil_err(wil, "SCAN_COMPLETE while not scanning\n"); + } +} + +static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + struct wmi_connect_event *evt = d; + int ch; /* channel number */ + struct station_info sinfo; + u8 *assoc_req_ie, *assoc_resp_ie; + size_t assoc_req_ielen, assoc_resp_ielen; + /* capinfo(u16) + listen_interval(u16) + IEs */ + const size_t assoc_req_ie_offset = sizeof(u16) * 2; + /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ + const size_t assoc_resp_ie_offset = sizeof(u16) * 3; + + if (len < sizeof(*evt)) { + wil_err(wil, "Connect event too short : %d bytes\n", len); + return; + } + if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len + + evt->assoc_resp_len) { + wil_err(wil, + "Connect event corrupted : %d != %d + %d + %d + %d\n", + len, (int)sizeof(*evt), evt->beacon_ie_len, + evt->assoc_req_len, evt->assoc_resp_len); + return; + } + ch = evt->channel + 1; + wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n", + evt->bssid, ch, evt->cid); + wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, + evt->assoc_info, len - sizeof(*evt), true); + + /* figure out IE's */ + assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len + + assoc_req_ie_offset]; + assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset; + if (evt->assoc_req_len <= assoc_req_ie_offset) { + assoc_req_ie = NULL; + assoc_req_ielen = 0; + } + + assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len + + evt->assoc_req_len + + assoc_resp_ie_offset]; + assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset; + if (evt->assoc_resp_len <= assoc_resp_ie_offset) { + assoc_resp_ie = NULL; + assoc_resp_ielen = 0; + } + + if ((wdev->iftype == NL80211_IFTYPE_STATION) || + (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wil_err(wil, "Not in connecting state\n"); + return; + } + del_timer_sync(&wil->connect_timer); + cfg80211_connect_result(ndev, evt->bssid, + assoc_req_ie, assoc_req_ielen, + assoc_resp_ie, assoc_resp_ielen, + WLAN_STATUS_SUCCESS, GFP_KERNEL); + + } else if ((wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { + memset(&sinfo, 0, sizeof(sinfo)); + + sinfo.generation = wil->sinfo_gen++; + + if (assoc_req_ie) { + sinfo.assoc_req_ies = assoc_req_ie; + sinfo.assoc_req_ies_len = assoc_req_ielen; + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; + } + + cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); + } + set_bit(wil_status_fwconnected, &wil->status); + + /* FIXME FW can transmit only ucast frames to peer */ + /* FIXME real ring_id instead of hard coded 0 */ + memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); + + wil->pending_connect_cid = evt->cid; + queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker); +} + +static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct wmi_disconnect_event *evt = d; + + wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n", + evt->bssid, + evt->protocol_reason_status, evt->disconnect_reason); + + wil->sinfo_gen++; + + wil6210_disconnect(wil, evt->bssid); + clear_bit(wil_status_dontscan, &wil->status); +} + +static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) +{ + struct wmi_notify_req_done_event *evt = d; + + if (len < sizeof(*evt)) { + wil_err(wil, "Short NOTIFY event\n"); + return; + } + + wil->stats.tsf = le64_to_cpu(evt->tsf); + wil->stats.snr = le32_to_cpu(evt->snr_val); + wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs); + wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector); + wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); + wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); + wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); + wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n" + "BF status 0x%08x SNR 0x%08x\n" + "Tx Tpt %d goodput %d Rx goodput %d\n" + "Sectors(rx:tx) my %d:%d peer %d:%d\n", + wil->stats.bf_mcs, wil->stats.tsf, evt->status, + wil->stats.snr, le32_to_cpu(evt->tx_tpt), + le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), + wil->stats.my_rx_sector, wil->stats.my_tx_sector, + wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); +} + +/* + * Firmware reports EAPOL frame using WME event. + * Reconstruct Ethernet frame and deliver it via normal Rx + */ +static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, + void *d, int len) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wmi_eapol_rx_event *evt = d; + u16 eapol_len = le16_to_cpu(evt->eapol_len); + int sz = eapol_len + ETH_HLEN; + struct sk_buff *skb; + struct ethhdr *eth; + + wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len, + evt->src_mac); + + if (eapol_len > 196) { /* TODO: revisit size limit */ + wil_err(wil, "EAPOL too large\n"); + return; + } + + skb = alloc_skb(sz, GFP_KERNEL); + if (!skb) { + wil_err(wil, "Failed to allocate skb\n"); + return; + } + eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); + memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); + memcpy(eth->h_source, evt->src_mac, ETH_ALEN); + eth->h_proto = cpu_to_be16(ETH_P_PAE); + memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); + skb->protocol = eth_type_trans(skb, ndev); + if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + } else { + ndev->stats.rx_dropped++; + } +} + +static const struct { + int eventid; + void (*handler)(struct wil6210_priv *wil, int eventid, + void *data, int data_len); +} wmi_evt_handlers[] = { + {WMI_READY_EVENTID, wmi_evt_ready}, + {WMI_FW_READY_EVENTID, wmi_evt_fw_ready}, + {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, + {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, + {WMI_CONNECT_EVENTID, wmi_evt_connect}, + {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, + {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify}, + {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, +}; + +/* + * Run in IRQ context + * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev + * that will be eventually handled by the @wmi_event_worker in the thread + * context of thread "wil6210_wmi" + */ +void wmi_recv_cmd(struct wil6210_priv *wil) +{ + struct wil6210_mbox_ring_desc d_tail; + struct wil6210_mbox_hdr hdr; + struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; + struct pending_wmi_event *evt; + u8 *cmd; + void __iomem *src; + ulong flags; + + for (;;) { + u16 len; + + r->head = ioread32(wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.head)); + if (r->tail == r->head) + return; + + /* read cmd from tail */ + wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), + sizeof(struct wil6210_mbox_ring_desc)); + if (d_tail.sync == 0) { + wil_err(wil, "Mbox evt not owned by FW?\n"); + return; + } + + if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { + wil_err(wil, "Mbox evt at 0x%08x?\n", + le32_to_cpu(d_tail.addr)); + return; + } + + len = le16_to_cpu(hdr.len); + src = wmi_buffer(wil, d_tail.addr) + + sizeof(struct wil6210_mbox_hdr); + evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, + event.wmi) + len, 4), + GFP_KERNEL); + if (!evt) { + wil_err(wil, "kmalloc for WMI event (%d) failed\n", + len); + return; + } + evt->event.hdr = hdr; + cmd = (void *)&evt->event.wmi; + wil_memcpy_fromio_32(cmd, src, len); + /* mark entry as empty */ + iowrite32(0, wil->csr + HOSTADDR(r->tail) + + offsetof(struct wil6210_mbox_ring_desc, sync)); + /* indicate */ + wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), + hdr.flags); + if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && + (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + wil_dbg_WMI(wil, "WMI event 0x%04x\n", + evt->event.wmi.id); + } + wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1, + &evt->event.hdr, sizeof(hdr) + len, true); + + /* advance tail */ + r->tail = r->base + ((r->tail - r->base + + sizeof(struct wil6210_mbox_ring_desc)) % r->size); + iowrite32(r->tail, wil->csr + HOST_MBOX + + offsetof(struct wil6210_mbox_ctl, rx.tail)); + + /* add to the pending list */ + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + list_add_tail(&evt->list, &wil->pending_wmi_ev); + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + { + int q = queue_work(wil->wmi_wq, + &wil->wmi_event_worker); + wil_dbg_WMI(wil, "queue_work -> %d\n", q); + } + } +} + +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, + u16 reply_id, void *reply, u8 reply_size, int to_msec) +{ + int rc; + int remain; + + mutex_lock(&wil->wmi_mutex); + + rc = __wmi_send(wil, cmdid, buf, len); + if (rc) + goto out; + + wil->reply_id = reply_id; + wil->reply_buf = reply; + wil->reply_size = reply_size; + remain = wait_for_completion_timeout(&wil->wmi_ready, + msecs_to_jiffies(to_msec)); + if (0 == remain) { + wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", + cmdid, reply_id, to_msec); + rc = -ETIME; + } else { + wil_dbg_WMI(wil, + "wmi_call(0x%04x->0x%04x) completed in %d msec\n", + cmdid, reply_id, + to_msec - jiffies_to_msecs(remain)); + } + wil->reply_id = 0; + wil->reply_buf = NULL; + wil->reply_size = 0; + out: + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +int wmi_echo(struct wil6210_priv *wil) +{ + struct wmi_echo_cmd cmd = { + .value = cpu_to_le32(0x12345678), + }; + + return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), + WMI_ECHO_RSP_EVENTID, NULL, 0, 20); +} + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) +{ + struct wmi_set_mac_address_cmd cmd; + + memcpy(cmd.mac, addr, ETH_ALEN); + + wil_dbg_WMI(wil, "Set MAC %pM\n", addr); + + return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype) +{ + struct wmi_bcon_ctrl_cmd cmd = { + .bcon_interval = cpu_to_le16(bi), + .network_type = wmi_nettype, + .disable_sec_offload = 1, + }; + + if (!wil->secure_pcp) + cmd.disable_sec = 1; + + return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) +{ + struct wmi_set_ssid_cmd cmd = { + .ssid_len = cpu_to_le32(ssid_len), + }; + + if (ssid_len > sizeof(cmd.ssid)) + return -EINVAL; + + memcpy(cmd.ssid, ssid, ssid_len); + + return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_set_ssid_cmd cmd; + } __packed reply; + int len; /* reply.cmd.ssid_len in CPU order */ + + rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID, + &reply, sizeof(reply), 20); + if (rc) + return rc; + + len = le32_to_cpu(reply.cmd.ssid_len); + if (len > sizeof(reply.cmd.ssid)) + return -EINVAL; + + *ssid_len = len; + memcpy(ssid, reply.cmd.ssid, len); + + return 0; +} + +int wmi_set_channel(struct wil6210_priv *wil, int channel) +{ + struct wmi_set_pcp_channel_cmd cmd = { + .channel = channel - 1, + }; + + return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_channel(struct wil6210_priv *wil, int *channel) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_set_pcp_channel_cmd cmd; + } __packed reply; + + rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0, + WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); + if (rc) + return rc; + + if (reply.cmd.channel > 3) + return -EINVAL; + + *channel = reply.cmd.channel + 1; + + return 0; +} + +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) +{ + struct wmi_eapol_tx_cmd *cmd; + struct ethhdr *eth; + u16 eapol_len = skb->len - ETH_HLEN; + void *eapol = skb->data + ETH_HLEN; + uint i; + int rc; + + skb_set_mac_header(skb, 0); + eth = eth_hdr(skb); + wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) + goto found_dest; + } + + return -EINVAL; + + found_dest: + /* find out eapol data & len */ + cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); + if (!cmd) + return -EINVAL; + + memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); + cmd->eapol_len = cpu_to_le16(eapol_len); + memcpy(cmd->eapol, eapol, eapol_len); + rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); + kfree(cmd); + + return rc; +} + +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr) +{ + struct wmi_delete_cipher_key_cmd cmd = { + .key_index = key_index, + }; + + if (mac_addr) + memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + + return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, + const void *mac_addr, int key_len, const void *key) +{ + struct wmi_add_cipher_key_cmd cmd = { + .key_index = key_index, + .key_usage = WMI_KEY_USE_PAIRWISE, + .key_len = key_len, + }; + + if (!key || (key_len > sizeof(cmd.key))) + return -EINVAL; + + memcpy(cmd.key, key, key_len); + if (mac_addr) + memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + + return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) +{ + int rc; + u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; + struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL); + if (!cmd) { + wil_err(wil, "kmalloc(%d) failed\n", len); + return -ENOMEM; + } + + cmd->mgmt_frm_type = type; + /* BUG: FW API define ieLen as u8. Will fix FW */ + cmd->ie_len = cpu_to_le16(ie_len); + memcpy(cmd->ie_info, ie, ie_len); + rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len); + kfree(cmd); + + return rc; +} + +void wmi_event_flush(struct wil6210_priv *wil) +{ + struct pending_wmi_event *evt, *t; + + wil_dbg_WMI(wil, "%s()\n", __func__); + + list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { + list_del(&evt->list); + kfree(evt); + } +} + +static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, + void *d, int len) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { + if (wmi_evt_handlers[i].eventid == id) { + wmi_evt_handlers[i].handler(wil, id, d, len); + return true; + } + } + + return false; +} + +static void wmi_event_handle(struct wil6210_priv *wil, + struct wil6210_mbox_hdr *hdr) +{ + u16 len = le16_to_cpu(hdr->len); + + if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && + (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { + struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); + void *evt_data = (void *)(&wmi[1]); + u16 id = le16_to_cpu(wmi->id); + /* check if someone waits for this event */ + if (wil->reply_id && wil->reply_id == id) { + if (wil->reply_buf) { + memcpy(wil->reply_buf, wmi, + min(len, wil->reply_size)); + } else { + wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi)); + } + wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id); + complete(&wil->wmi_ready); + return; + } + /* unsolicited event */ + /* search for handler */ + if (!wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi))) { + wil_err(wil, "Unhandled event 0x%04x\n", id); + } + } else { + wil_err(wil, "Unknown event type\n"); + print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, + hdr, sizeof(*hdr) + len, true); + } +} + +/* + * Retrieve next WMI event from the pending list + */ +static struct list_head *next_wmi_ev(struct wil6210_priv *wil) +{ + ulong flags; + struct list_head *ret = NULL; + + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + + if (!list_empty(&wil->pending_wmi_ev)) { + ret = wil->pending_wmi_ev.next; + list_del(ret); + } + + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + + return ret; +} + +/* + * Handler for the WMI events + */ +void wmi_event_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + wmi_event_worker); + struct pending_wmi_event *evt; + struct list_head *lh; + + while ((lh = next_wmi_ev(wil)) != NULL) { + evt = list_entry(lh, struct pending_wmi_event, list); + wmi_event_handle(wil, &evt->event.hdr); + kfree(evt); + } +} + +void wmi_connect_worker(struct work_struct *work) +{ + int rc; + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + wmi_connect_worker); + + if (wil->pending_connect_cid < 0) { + wil_err(wil, "No connection pending\n"); + return; + } + + wil_dbg_WMI(wil, "Configure for connection CID %d\n", + wil->pending_connect_cid); + + rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, + wil->pending_connect_cid, 0); + wil->pending_connect_cid = -1; + if (rc == 0) + wil_link_on(wil); +} diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h new file mode 100644 index 000000000000..3bbf87572b07 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2006-2012 Wilocity . + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file contains the definitions of the WMI protocol specified in the + * Wireless Module Interface (WMI) for the Wilocity + * MARLON 60 Gigabit wireless solution. + * It includes definitions of all the commands and events. + * Commands are messages from the host to the WM. + * Events are messages from the WM to the host. + */ + +#ifndef __WILOCITY_WMI_H__ +#define __WILOCITY_WMI_H__ + +/* General */ + +#define WMI_MAC_LEN (6) +#define WMI_PROX_RANGE_NUM (3) + +/* List of Commands */ +enum wmi_command_id { + WMI_CONNECT_CMDID = 0x0001, + WMI_DISCONNECT_CMDID = 0x0003, + WMI_START_SCAN_CMDID = 0x0007, + WMI_SET_BSS_FILTER_CMDID = 0x0009, + WMI_SET_PROBED_SSID_CMDID = 0x000a, + WMI_SET_LISTEN_INT_CMDID = 0x000b, + WMI_BCON_CTRL_CMDID = 0x000f, + WMI_ADD_CIPHER_KEY_CMDID = 0x0016, + WMI_DELETE_CIPHER_KEY_CMDID = 0x0017, + WMI_SET_APPIE_CMDID = 0x003f, + WMI_GET_APPIE_CMDID = 0x0040, + WMI_SET_WSC_STATUS_CMDID = 0x0041, + WMI_PXMT_RANGE_CFG_CMDID = 0x0042, + WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043, + WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, + WMI_MEM_READ_CMDID = 0x0800, + WMI_MEM_WR_CMDID = 0x0801, + WMI_ECHO_CMDID = 0x0803, + WMI_DEEP_ECHO_CMDID = 0x0804, + WMI_CONFIG_MAC_CMDID = 0x0805, + WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806, + WMI_ADD_STATION_CMDID = 0x0807, + WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808, + WMI_PHY_GET_STATISTICS_CMDID = 0x0809, + WMI_FS_TUNE_CMDID = 0x080a, + WMI_CORR_MEASURE_CMDID = 0x080b, + WMI_TEMP_SENSE_CMDID = 0x080e, + WMI_DC_CALIB_CMDID = 0x080f, + WMI_SEND_TONE_CMDID = 0x0810, + WMI_IQ_TX_CALIB_CMDID = 0x0811, + WMI_IQ_RX_CALIB_CMDID = 0x0812, + WMI_SET_UCODE_IDLE_CMDID = 0x0813, + WMI_SET_WORK_MODE_CMDID = 0x0815, + WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816, + WMI_MARLON_R_ACTIVATE_CMDID = 0x0817, + WMI_MARLON_R_READ_CMDID = 0x0818, + WMI_MARLON_R_WRITE_CMDID = 0x0819, + WMI_MARLON_R_TXRX_SEL_CMDID = 0x081a, + MAC_IO_STATIC_PARAMS_CMDID = 0x081b, + MAC_IO_DYNAMIC_PARAMS_CMDID = 0x081c, + WMI_SILENT_RSSI_CALIB_CMDID = 0x081d, + WMI_CFG_RX_CHAIN_CMDID = 0x0820, + WMI_VRING_CFG_CMDID = 0x0821, + WMI_RX_ON_CMDID = 0x0822, + WMI_VRING_BA_EN_CMDID = 0x0823, + WMI_VRING_BA_DIS_CMDID = 0x0824, + WMI_RCP_ADDBA_RESP_CMDID = 0x0825, + WMI_RCP_DELBA_CMDID = 0x0826, + WMI_SET_SSID_CMDID = 0x0827, + WMI_GET_SSID_CMDID = 0x0828, + WMI_SET_PCP_CHANNEL_CMDID = 0x0829, + WMI_GET_PCP_CHANNEL_CMDID = 0x082a, + WMI_SW_TX_REQ_CMDID = 0x082b, + WMI_RX_OFF_CMDID = 0x082c, + WMI_READ_MAC_RXQ_CMDID = 0x0830, + WMI_READ_MAC_TXQ_CMDID = 0x0831, + WMI_WRITE_MAC_RXQ_CMDID = 0x0832, + WMI_WRITE_MAC_TXQ_CMDID = 0x0833, + WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x0834, + WMI_MLME_PUSH_CMDID = 0x0835, + WMI_BEAMFORMING_MGMT_CMDID = 0x0836, + WMI_BF_TXSS_MGMT_CMDID = 0x0837, + WMI_BF_SM_MGMT_CMDID = 0x0838, + WMI_BF_RXSS_MGMT_CMDID = 0x0839, + WMI_SET_SECTORS_CMDID = 0x0849, + WMI_MAINTAIN_PAUSE_CMDID = 0x0850, + WMI_MAINTAIN_RESUME_CMDID = 0x0851, + WMI_RS_MGMT_CMDID = 0x0852, + WMI_RF_MGMT_CMDID = 0x0853, + /* Performance monitoring commands */ + WMI_BF_CTRL_CMDID = 0x0862, + WMI_NOTIFY_REQ_CMDID = 0x0863, + WMI_GET_STATUS_CMDID = 0x0864, + WMI_UNIT_TEST_CMDID = 0x0900, + WMI_HICCUP_CMDID = 0x0901, + WMI_FLASH_READ_CMDID = 0x0902, + WMI_FLASH_WRITE_CMDID = 0x0903, + WMI_SECURITY_UNIT_TEST_CMDID = 0x0904, + + WMI_SET_MAC_ADDRESS_CMDID = 0xf003, + WMI_ABORT_SCAN_CMDID = 0xf007, + WMI_SET_PMK_CMDID = 0xf028, + + WMI_SET_PROMISCUOUS_MODE_CMDID = 0xf041, + WMI_GET_PMK_CMDID = 0xf048, + WMI_SET_PASSPHRASE_CMDID = 0xf049, + WMI_SEND_ASSOC_RES_CMDID = 0xf04a, + WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xf04b, + WMI_EAPOL_TX_CMDID = 0xf04c, + WMI_MAC_ADDR_REQ_CMDID = 0xf04d, + WMI_FW_VER_CMDID = 0xf04e, +}; + +/* + * Commands data structures + */ + +/* + * Frame Types + */ +enum wmi_mgmt_frame_type { + WMI_FRAME_BEACON = 0, + WMI_FRAME_PROBE_REQ = 1, + WMI_FRAME_PROBE_RESP = 2, + WMI_FRAME_ASSOC_REQ = 3, + WMI_FRAME_ASSOC_RESP = 4, + WMI_NUM_MGMT_FRAME, +}; + +/* + * WMI_CONNECT_CMDID + */ +enum wmi_network_type { + WMI_NETTYPE_INFRA = 0x01, + WMI_NETTYPE_ADHOC = 0x02, + WMI_NETTYPE_ADHOC_CREATOR = 0x04, + WMI_NETTYPE_AP = 0x10, + WMI_NETTYPE_P2P = 0x20, + WMI_NETTYPE_WBE = 0x40, /* PCIE over 60g */ +}; + +enum wmi_dot11_auth_mode { + WMI_AUTH11_OPEN = 0x01, + WMI_AUTH11_SHARED = 0x02, + WMI_AUTH11_LEAP = 0x04, + WMI_AUTH11_WSC = 0x08, +}; + +enum wmi_auth_mode { + WMI_AUTH_NONE = 0x01, + WMI_AUTH_WPA = 0x02, + WMI_AUTH_WPA2 = 0x04, + WMI_AUTH_WPA_PSK = 0x08, + WMI_AUTH_WPA2_PSK = 0x10, + WMI_AUTH_WPA_CCKM = 0x20, + WMI_AUTH_WPA2_CCKM = 0x40, +}; + +enum wmi_crypto_type { + WMI_CRYPT_NONE = 0x01, + WMI_CRYPT_WEP = 0x02, + WMI_CRYPT_TKIP = 0x04, + WMI_CRYPT_AES = 0x08, + WMI_CRYPT_AES_GCMP = 0x20, +}; + + +enum wmi_connect_ctrl_flag_bits { + WMI_CONNECT_ASSOC_POLICY_USER = 0x0001, + WMI_CONNECT_SEND_REASSOC = 0x0002, + WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, + WMI_CONNECT_PROFILE_MATCH_DONE = 0x0008, + WMI_CONNECT_IGNORE_AAC_BEACON = 0x0010, + WMI_CONNECT_CSA_FOLLOW_BSS = 0x0020, + WMI_CONNECT_DO_WPA_OFFLOAD = 0x0040, + WMI_CONNECT_DO_NOT_DEAUTH = 0x0080, +}; + +#define WMI_MAX_SSID_LEN (32) + +struct wmi_connect_cmd { + u8 network_type; + u8 dot11_auth_mode; + u8 auth_mode; + u8 pairwise_crypto_type; + u8 pairwise_crypto_len; + u8 group_crypto_type; + u8 group_crypto_len; + u8 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; + u8 channel; + u8 reserved0; + u8 bssid[WMI_MAC_LEN]; + __le32 ctrl_flags; + u8 dst_mac[WMI_MAC_LEN]; + u8 reserved1[2]; +} __packed; + + +/* + * WMI_RECONNECT_CMDID + */ +struct wmi_reconnect_cmd { + u8 channel; /* hint */ + u8 reserved; + u8 bssid[WMI_MAC_LEN]; /* mandatory if set */ +} __packed; + + +/* + * WMI_SET_PMK_CMDID + */ + +#define WMI_MIN_KEY_INDEX (0) +#define WMI_MAX_KEY_INDEX (3) +#define WMI_MAX_KEY_LEN (32) +#define WMI_PASSPHRASE_LEN (64) +#define WMI_PMK_LEN (32) + +struct wmi_set_pmk_cmd { + u8 pmk[WMI_PMK_LEN]; +} __packed; + + +/* + * WMI_SET_PASSPHRASE_CMDID + */ +struct wmi_set_passphrase_cmd { + u8 ssid[WMI_MAX_SSID_LEN]; + u8 passphrase[WMI_PASSPHRASE_LEN]; + u8 ssid_len; + u8 passphrase_len; +} __packed; + +/* + * WMI_ADD_CIPHER_KEY_CMDID + */ +enum wmi_key_usage { + WMI_KEY_USE_PAIRWISE = 0, + WMI_KEY_USE_GROUP = 1, + WMI_KEY_USE_TX = 2, /* default Tx Key - Static WEP only */ +}; + +struct wmi_add_cipher_key_cmd { + u8 key_index; + u8 key_type; + u8 key_usage; /* enum wmi_key_usage */ + u8 key_len; + u8 key_rsc[8]; /* key replay sequence counter */ + u8 key[WMI_MAX_KEY_LEN]; + u8 key_op_ctrl; /* Additional Key Control information */ + u8 mac[WMI_MAC_LEN]; +} __packed; + +/* + * WMI_DELETE_CIPHER_KEY_CMDID + */ +struct wmi_delete_cipher_key_cmd { + u8 key_index; + u8 mac[WMI_MAC_LEN]; +} __packed; + + +/* + * WMI_START_SCAN_CMDID + * + * Start L1 scan operation + * + * Returned events: + * - WMI_RX_MGMT_PACKET_EVENTID - for every probe resp. + * - WMI_SCAN_COMPLETE_EVENTID + */ +enum wmi_scan_type { + WMI_LONG_SCAN = 0, + WMI_SHORT_SCAN = 1, +}; + +struct wmi_start_scan_cmd { + u8 reserved[8]; + __le32 home_dwell_time; /* Max duration in the home channel(ms) */ + __le32 force_scan_interval; /* Time interval between scans (ms)*/ + u8 scan_type; /* wmi_scan_type */ + u8 num_channels; /* how many channels follow */ + struct { + u8 channel; + u8 reserved; + } channel_list[0]; /* channels ID's */ + /* 0 - 58320 MHz */ + /* 1 - 60480 MHz */ + /* 2 - 62640 MHz */ +} __packed; + +/* + * WMI_SET_PROBED_SSID_CMDID + */ +#define MAX_PROBED_SSID_INDEX (15) + +enum wmi_ssid_flag { + WMI_SSID_FLAG_DISABLE = 0, /* disables entry */ + WMI_SSID_FLAG_SPECIFIC = 1, /* probes specified ssid */ + WMI_SSID_FLAG_ANY = 2, /* probes for any ssid */ +}; + +struct wmi_probed_ssid_cmd { + u8 entry_index; /* 0 to MAX_PROBED_SSID_INDEX */ + u8 flag; /* enum wmi_ssid_flag */ + u8 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_APPIE_CMDID + * Add Application specified IE to a management frame + */ +struct wmi_set_appie_cmd { + u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ + u8 reserved; + __le16 ie_len; /* Length of the IE to be added to MGMT frame */ + u8 ie_info[0]; +} __packed; + +#define WMI_MAX_IE_LEN (1024) + +struct wmi_pxmt_range_cfg_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 range; +} __packed; + +struct wmi_pxmt_snr2_range_cfg_cmd { + s8 snr2range_arr[WMI_PROX_RANGE_NUM-1]; +} __packed; + +/* + * WMI_RF_MGMT_CMDID + */ +enum wmi_rf_mgmt_type { + WMI_RF_MGMT_W_DISABLE = 0, + WMI_RF_MGMT_W_ENABLE = 1, + WMI_RF_MGMT_GET_STATUS = 2, +}; + +struct wmi_rf_mgmt_cmd { + __le32 rf_mgmt_type; +} __packed; + +/* + * WMI_SET_SSID_CMDID + */ +struct wmi_set_ssid_cmd { + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_PCP_CHANNEL_CMDID + */ +struct wmi_set_pcp_channel_cmd { + u8 channel; + u8 reserved[3]; +} __packed; + +/* + * WMI_BCON_CTRL_CMDID + */ +struct wmi_bcon_ctrl_cmd { + __le16 bcon_interval; + __le16 frag_num; + __le64 ss_mask; + u8 network_type; + u8 reserved; + u8 disable_sec_offload; + u8 disable_sec; +} __packed; + +/* + * WMI_SW_TX_REQ_CMDID + */ +struct wmi_sw_tx_req_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 len; + u8 payload[0]; +} __packed; + +/* + * WMI_VRING_CFG_CMDID + */ + +struct wmi_sw_ring_cfg { + __le64 ring_mem_base; + __le16 ring_size; + __le16 max_mpdu_size; +} __packed; + +struct wmi_vring_cfg_schd { + __le16 priority; + __le16 timeslot_us; +} __packed; + +enum wmi_vring_cfg_encap_trans_type { + WMI_VRING_ENC_TYPE_802_3 = 0, + WMI_VRING_ENC_TYPE_NATIVE_WIFI = 1, +}; + +enum wmi_vring_cfg_ds_cfg { + WMI_VRING_DS_PBSS = 0, + WMI_VRING_DS_STATION = 1, + WMI_VRING_DS_AP = 2, + WMI_VRING_DS_ADDR4 = 3, +}; + +enum wmi_vring_cfg_nwifi_ds_trans_type { + WMI_NWIFI_TX_TRANS_MODE_NO = 0, + WMI_NWIFI_TX_TRANS_MODE_AP2PBSS = 1, + WMI_NWIFI_TX_TRANS_MODE_STA2PBSS = 2, +}; + +enum wmi_vring_cfg_schd_params_priority { + WMI_SCH_PRIO_REGULAR = 0, + WMI_SCH_PRIO_HIGH = 1, +}; + +struct wmi_vring_cfg { + struct wmi_sw_ring_cfg tx_sw_ring; + u8 ringid; /* 0-23 vrings */ + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 encap_trans_type; + u8 ds_cfg; /* 802.3 DS cfg */ + u8 nwifi_ds_trans_type; + + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_POS (0) + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_LEN (1) + #define VRING_CFG_MAC_CTRL_LIFETIME_EN_MSK (0x1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_POS (1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_LEN (1) + #define VRING_CFG_MAC_CTRL_AGGR_EN_MSK (0x2) + u8 mac_ctrl; + + #define VRING_CFG_TO_RESOLUTION_VALUE_POS (0) + #define VRING_CFG_TO_RESOLUTION_VALUE_LEN (6) + #define VRING_CFG_TO_RESOLUTION_VALUE_MSK (0x3F) + u8 to_resolution; + u8 agg_max_wsize; + struct wmi_vring_cfg_schd schd_params; +} __packed; + +enum wmi_vring_cfg_cmd_action { + WMI_VRING_CMD_ADD = 0, + WMI_VRING_CMD_MODIFY = 1, + WMI_VRING_CMD_DELETE = 2, +}; + +struct wmi_vring_cfg_cmd { + __le32 action; + struct wmi_vring_cfg vring_cfg; +} __packed; + +/* + * WMI_VRING_BA_EN_CMDID + */ +struct wmi_vring_ba_en_cmd { + u8 ringid; + u8 agg_max_wsize; + __le16 ba_timeout; +} __packed; + +/* + * WMI_VRING_BA_DIS_CMDID + */ +struct wmi_vring_ba_dis_cmd { + u8 ringid; + u8 reserved; + __le16 reason; +} __packed; + +/* + * WMI_NOTIFY_REQ_CMDID + */ +struct wmi_notify_req_cmd { + u8 cid; + u8 reserved[3]; + __le32 interval_usec; +} __packed; + +/* + * WMI_CFG_RX_CHAIN_CMDID + */ +enum wmi_sniffer_cfg_mode { + WMI_SNIFFER_OFF = 0, + WMI_SNIFFER_ON = 1, +}; + +enum wmi_sniffer_cfg_phy_info_mode { + WMI_SNIFFER_PHY_INFO_DISABLED = 0, + WMI_SNIFFER_PHY_INFO_ENABLED = 1, +}; + +enum wmi_sniffer_cfg_phy_support { + WMI_SNIFFER_CP = 0, + WMI_SNIFFER_DP = 1, + WMI_SNIFFER_BOTH_PHYS = 2, +}; + +struct wmi_sniffer_cfg { + __le32 mode; /* enum wmi_sniffer_cfg_mode */ + __le32 phy_info_mode; /* enum wmi_sniffer_cfg_phy_info_mode */ + __le32 phy_support; /* enum wmi_sniffer_cfg_phy_support */ + u8 channel; + u8 reserved[3]; +} __packed; + +enum wmi_cfg_rx_chain_cmd_action { + WMI_RX_CHAIN_ADD = 0, + WMI_RX_CHAIN_DEL = 1, +}; + +enum wmi_cfg_rx_chain_cmd_decap_trans_type { + WMI_DECAP_TYPE_802_3 = 0, + WMI_DECAP_TYPE_NATIVE_WIFI = 1, +}; + +enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { + WMI_NWIFI_RX_TRANS_MODE_NO = 0, + WMI_NWIFI_RX_TRANS_MODE_PBSS2AP = 1, + WMI_NWIFI_RX_TRANS_MODE_PBSS2STA = 2, +}; + +struct wmi_cfg_rx_chain_cmd { + __le32 action; + struct wmi_sw_ring_cfg rx_sw_ring; + u8 mid; + u8 decap_trans_type; + + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0) + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1) + #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1) + u8 l2_802_3_offload_ctrl; + + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_LEN (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_MSK (0x1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_POS (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_LEN (1) + #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_MSK (0x2) + u8 l2_nwifi_offload_ctrl; + + u8 vlan_id; + u8 nwifi_ds_trans_type; + + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_POS (0) + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_LEN (1) + #define L3_L4_CTRL_IPV4_CHECKSUM_EN_MSK (0x1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS (1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_LEN (1) + #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_MSK (0x2) + u8 l3_l4_ctrl; + + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_POS (0) + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_MSK (0x1) + #define RING_CTRL_OVERRIDE_WB_THRSH_POS (1) + #define RING_CTRL_OVERRIDE_WB_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_WB_THRSH_MSK (0x2) + #define RING_CTRL_OVERRIDE_ITR_THRSH_POS (2) + #define RING_CTRL_OVERRIDE_ITR_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_ITR_THRSH_MSK (0x4) + #define RING_CTRL_OVERRIDE_HOST_THRSH_POS (3) + #define RING_CTRL_OVERRIDE_HOST_THRSH_LEN (1) + #define RING_CTRL_OVERRIDE_HOST_THRSH_MSK (0x8) + u8 ring_ctrl; + + __le16 prefetch_thrsh; + __le16 wb_thrsh; + __le32 itr_value; + __le16 host_thrsh; + u8 reserved[2]; + struct wmi_sniffer_cfg sniffer_cfg; +} __packed; + +/* + * WMI_RCP_ADDBA_RESP_CMDID + */ +struct wmi_rcp_addba_resp_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + __le16 status_code; + __le16 ba_param_set; /* ieee80211_ba_parameterset field to send */ + __le16 ba_timeout; +} __packed; + +/* + * WMI_RCP_DELBA_CMDID + */ +struct wmi_rcp_delba_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 reserved; + __le16 reason; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_CMDID + */ +struct wmi_rcp_addba_req_cmd { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + /* ieee80211_ba_parameterset field as it received */ + __le16 ba_param_set; + __le16 ba_timeout; + /* ieee80211_ba_seqstrl field as it received */ + __le16 ba_seq_ctrl; +} __packed; + +/* + * WMI_SET_MAC_ADDRESS_CMDID + */ +struct wmi_set_mac_address_cmd { + u8 mac[WMI_MAC_LEN]; + u8 reserved[2]; +} __packed; + + +/* +* WMI_EAPOL_TX_CMDID +*/ +struct wmi_eapol_tx_cmd { + u8 dst_mac[WMI_MAC_LEN]; + __le16 eapol_len; + u8 eapol[0]; +} __packed; + +/* + * WMI_ECHO_CMDID + * + * Check FW is alive + * + * WMI_DEEP_ECHO_CMDID + * + * Check FW and ucode are alive + * + * Returned event: WMI_ECHO_RSP_EVENTID + * same event for both commands + */ +struct wmi_echo_cmd { + __le32 value; +} __packed; + +/* + * WMI Events + */ + +/* + * List of Events (target to host) + */ +enum wmi_event_id { + WMI_IMM_RSP_EVENTID = 0x0000, + WMI_READY_EVENTID = 0x1001, + WMI_CONNECT_EVENTID = 0x1002, + WMI_DISCONNECT_EVENTID = 0x1003, + WMI_SCAN_COMPLETE_EVENTID = 0x100a, + WMI_REPORT_STATISTICS_EVENTID = 0x100b, + WMI_RD_MEM_RSP_EVENTID = 0x1800, + WMI_FW_READY_EVENTID = 0x1801, + WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200, + WMI_ECHO_RSP_EVENTID = 0x1803, + WMI_CONFIG_MAC_DONE_EVENTID = 0x1805, + WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806, + WMI_ADD_STATION_DONE_EVENTID = 0x1807, + WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808, + WMI_PHY_GET_STATISTICS_EVENTID = 0x1809, + WMI_FS_TUNE_DONE_EVENTID = 0x180a, + WMI_CORR_MEASURE_DONE_EVENTID = 0x180b, + WMI_TEMP_SENSE_DONE_EVENTID = 0x180e, + WMI_DC_CALIB_DONE_EVENTID = 0x180f, + WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811, + WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, + WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, + WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, + WMI_MARLON_R_ACTIVATE_DONE_EVENTID = 0x1817, + WMI_MARLON_R_READ_DONE_EVENTID = 0x1818, + WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819, + WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a, + WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181d, + + WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820, + WMI_VRING_CFG_DONE_EVENTID = 0x1821, + WMI_RX_ON_DONE_EVENTID = 0x1822, + WMI_BA_STATUS_EVENTID = 0x1823, + WMI_RCP_ADDBA_REQ_EVENTID = 0x1824, + WMI_ADDBA_RESP_SENT_EVENTID = 0x1825, + WMI_DELBA_EVENTID = 0x1826, + WMI_GET_SSID_EVENTID = 0x1828, + WMI_GET_PCP_CHANNEL_EVENTID = 0x182a, + WMI_SW_TX_COMPLETE_EVENTID = 0x182b, + WMI_RX_OFF_DONE_EVENTID = 0x182c, + + WMI_READ_MAC_RXQ_EVENTID = 0x1830, + WMI_READ_MAC_TXQ_EVENTID = 0x1831, + WMI_WRITE_MAC_RXQ_EVENTID = 0x1832, + WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, + WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, + + WMI_BEAFORMING_MGMT_DONE_EVENTID = 0x1836, + WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, + WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, + WMI_RS_MGMT_DONE_EVENTID = 0x1852, + WMI_RF_MGMT_STATUS_EVENTID = 0x1853, + WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, + WMI_RX_MGMT_PACKET_EVENTID = 0x1840, + + /* Performance monitoring events */ + WMI_DATA_PORT_OPEN_EVENTID = 0x1860, + WMI_WBE_LINKDOWN_EVENTID = 0x1861, + + WMI_BF_CTRL_DONE_EVENTID = 0x1862, + WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, + WMI_GET_STATUS_DONE_EVENTID = 0x1864, + + WMI_UNIT_TEST_EVENTID = 0x1900, + WMI_FLASH_READ_DONE_EVENTID = 0x1902, + WMI_FLASH_WRITE_DONE_EVENTID = 0x1903, + + WMI_SET_CHANNEL_EVENTID = 0x9000, + WMI_ASSOC_REQ_EVENTID = 0x9001, + WMI_EAPOL_RX_EVENTID = 0x9002, + WMI_MAC_ADDR_RESP_EVENTID = 0x9003, + WMI_FW_VER_EVENTID = 0x9004, +}; + +/* + * Events data structures + */ + +/* + * WMI_RF_MGMT_STATUS_EVENTID + */ +enum wmi_rf_status { + WMI_RF_ENABLED = 0, + WMI_RF_DISABLED_HW = 1, + WMI_RF_DISABLED_SW = 2, + WMI_RF_DISABLED_HW_SW = 3, +}; + +struct wmi_rf_mgmt_status_event { + __le32 rf_status; +} __packed; + +/* + * WMI_GET_STATUS_DONE_EVENTID + */ +struct wmi_get_status_done_event { + __le32 is_associated; + u8 cid; + u8 reserved0[3]; + u8 bssid[WMI_MAC_LEN]; + u8 channel; + u8 reserved1; + u8 network_type; + u8 reserved2[3]; + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; + __le32 rf_status; + __le32 is_secured; +} __packed; + +/* + * WMI_FW_VER_EVENTID + */ +struct wmi_fw_ver_event { + u8 major; + u8 minor; + __le16 subminor; + __le16 build; +} __packed; + +/* +* WMI_MAC_ADDR_RESP_EVENTID +*/ +struct wmi_mac_addr_resp_event { + u8 mac[WMI_MAC_LEN]; + u8 auth_mode; + u8 crypt_mode; + __le32 offload_mode; +} __packed; + +/* +* WMI_EAPOL_RX_EVENTID +*/ +struct wmi_eapol_rx_event { + u8 src_mac[WMI_MAC_LEN]; + __le16 eapol_len; + u8 eapol[0]; +} __packed; + +/* +* WMI_READY_EVENTID +*/ +enum wmi_phy_capability { + WMI_11A_CAPABILITY = 1, + WMI_11G_CAPABILITY = 2, + WMI_11AG_CAPABILITY = 3, + WMI_11NA_CAPABILITY = 4, + WMI_11NG_CAPABILITY = 5, + WMI_11NAG_CAPABILITY = 6, + WMI_11AD_CAPABILITY = 7, + WMI_11N_CAPABILITY_OFFSET = WMI_11NA_CAPABILITY - WMI_11A_CAPABILITY, +}; + +struct wmi_ready_event { + __le32 sw_version; + __le32 abi_version; + u8 mac[WMI_MAC_LEN]; + u8 phy_capability; /* enum wmi_phy_capability */ + u8 reserved; +} __packed; + +/* + * WMI_NOTIFY_REQ_DONE_EVENTID + */ +struct wmi_notify_req_done_event { + __le32 status; + __le64 tsf; + __le32 snr_val; + __le32 tx_tpt; + __le32 tx_goodput; + __le32 rx_goodput; + __le16 bf_mcs; + __le16 my_rx_sector; + __le16 my_tx_sector; + __le16 other_rx_sector; + __le16 other_tx_sector; + __le16 range; +} __packed; + +/* + * WMI_CONNECT_EVENTID + */ +struct wmi_connect_event { + u8 channel; + u8 reserved0; + u8 bssid[WMI_MAC_LEN]; + __le16 listen_interval; + __le16 beacon_interval; + u8 network_type; + u8 reserved1[3]; + u8 beacon_ie_len; + u8 assoc_req_len; + u8 assoc_resp_len; + u8 cid; + u8 reserved2[3]; + u8 assoc_info[0]; +} __packed; + +/* + * WMI_DISCONNECT_EVENTID + */ +enum wmi_disconnect_reason { + WMI_DIS_REASON_NO_NETWORK_AVAIL = 1, + WMI_DIS_REASON_LOST_LINK = 2, /* bmiss */ + WMI_DIS_REASON_DISCONNECT_CMD = 3, + WMI_DIS_REASON_BSS_DISCONNECTED = 4, + WMI_DIS_REASON_AUTH_FAILED = 5, + WMI_DIS_REASON_ASSOC_FAILED = 6, + WMI_DIS_REASON_NO_RESOURCES_AVAIL = 7, + WMI_DIS_REASON_CSERV_DISCONNECT = 8, + WMI_DIS_REASON_INVALID_PROFILE = 10, + WMI_DIS_REASON_DOT11H_CHANNEL_SWITCH = 11, + WMI_DIS_REASON_PROFILE_MISMATCH = 12, + WMI_DIS_REASON_CONNECTION_EVICTED = 13, + WMI_DIS_REASON_IBSS_MERGE = 14, +}; + +struct wmi_disconnect_event { + __le16 protocol_reason_status; /* reason code, see 802.11 spec. */ + u8 bssid[WMI_MAC_LEN]; /* set if known */ + u8 disconnect_reason; /* see wmi_disconnect_reason_e */ + u8 assoc_resp_len; + u8 assoc_info[0]; +} __packed; + +/* + * WMI_SCAN_COMPLETE_EVENTID + */ +struct wmi_scan_complete_event { + __le32 status; +} __packed; + +/* + * WMI_BA_STATUS_EVENTID + */ +enum wmi_vring_ba_status { + WMI_BA_AGREED = 0, + WMI_BA_NON_AGREED = 1, +}; + +struct wmi_vring_ba_status_event { + __le16 status; + u8 reserved[2]; + u8 ringid; + u8 agg_wsize; + __le16 ba_timeout; +} __packed; + +/* + * WMI_DELBA_EVENTID + */ +struct wmi_delba_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 from_initiator; + __le16 reason; +} __packed; + +/* + * WMI_VRING_CFG_DONE_EVENTID + */ +enum wmi_vring_cfg_done_event_status { + WMI_VRING_CFG_SUCCESS = 0, + WMI_VRING_CFG_FAILURE = 1, +}; + +struct wmi_vring_cfg_done_event { + u8 ringid; + u8 status; + u8 reserved[2]; + __le32 tx_vring_tail_ptr; +} __packed; + +/* + * WMI_ADDBA_RESP_SENT_EVENTID + */ +enum wmi_rcp_addba_resp_sent_event_status { + WMI_ADDBA_SUCCESS = 0, + WMI_ADDBA_FAIL = 1, +}; + +struct wmi_rcp_addba_resp_sent_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 reserved; + __le16 status; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_EVENTID + */ +struct wmi_rcp_addba_req_event { + + #define CIDXTID_CID_POS (0) + #define CIDXTID_CID_LEN (4) + #define CIDXTID_CID_MSK (0xF) + #define CIDXTID_TID_POS (4) + #define CIDXTID_TID_LEN (4) + #define CIDXTID_TID_MSK (0xF0) + u8 cidxtid; + + u8 dialog_token; + __le16 ba_param_set; /* ieee80211_ba_parameterset as it received */ + __le16 ba_timeout; + __le16 ba_seq_ctrl; /* ieee80211_ba_seqstrl field as it received */ +} __packed; + +/* + * WMI_CFG_RX_CHAIN_DONE_EVENTID + */ +enum wmi_cfg_rx_chain_done_event_status { + WMI_CFG_RX_CHAIN_SUCCESS = 1, +}; + +struct wmi_cfg_rx_chain_done_event { + __le32 rx_ring_tail_ptr; /* Rx V-Ring Tail pointer */ + __le32 status; +} __packed; + +/* + * WMI_WBE_LINKDOWN_EVENTID + */ +enum wmi_wbe_link_down_event_reason { + WMI_WBE_REASON_USER_REQUEST = 0, + WMI_WBE_REASON_RX_DISASSOC = 1, + WMI_WBE_REASON_BAD_PHY_LINK = 2, +}; + +struct wmi_wbe_link_down_event { + u8 cid; + u8 reserved[3]; + __le32 reason; +} __packed; + +/* + * WMI_DATA_PORT_OPEN_EVENTID + */ +struct wmi_data_port_open_event { + u8 cid; + u8 reserved[3]; +} __packed; + +/* + * WMI_GET_PCP_CHANNEL_EVENTID + */ +struct wmi_get_pcp_channel_event { + u8 channel; + u8 reserved[3]; +} __packed; + +/* + * WMI_SW_TX_COMPLETE_EVENTID + */ +enum wmi_sw_tx_status { + WMI_TX_SW_STATUS_SUCCESS = 0, + WMI_TX_SW_STATUS_FAILED_NO_RESOURCES = 1, + WMI_TX_SW_STATUS_FAILED_TX = 2, +}; + +struct wmi_sw_tx_complete_event { + u8 status; /* enum wmi_sw_tx_status */ + u8 reserved[3]; +} __packed; + +/* + * WMI_GET_SSID_EVENTID + */ +struct wmi_get_ssid_event { + __le32 ssid_len; + u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_RX_MGMT_PACKET_EVENTID + */ +struct wmi_rx_mgmt_info { + u8 mcs; + s8 snr; + __le16 range; + __le16 stype; + __le16 status; + __le32 len; + u8 qid; + u8 mid; + u8 cid; + u8 channel; /* From Radio MNGR */ +} __packed; + +struct wmi_rx_mgmt_packet_event { + struct wmi_rx_mgmt_info info; + u8 payload[0]; +} __packed; + +/* + * WMI_ECHO_RSP_EVENTID + */ +struct wmi_echo_event { + __le32 echoed_value; +} __packed; + +#endif /* __WILOCITY_WMI_H__ */ From 111b72a2f09e1958a6078a8b1c3feded6a3b450e Mon Sep 17 00:00:00 2001 From: Julian Wollrath Date: Wed, 2 Jan 2013 15:21:56 +0100 Subject: [PATCH 179/478] rtlwifi: Fix typo in debug output of rtl8192c and rtl8723ae Signed-off-by: Julian Wollrath Acked-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c | 2 +- drivers/net/wireless/rtlwifi/rtl8723ae/phy.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 1d5d3604e3e0..246e5352f2e1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c @@ -692,7 +692,7 @@ u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw) if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { rtl92c_phy_sw_chnl_callback(hw); RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, - "sw_chnl_inprogress false schdule workitem\n"); + "sw_chnl_inprogress false schedule workitem\n"); rtlphy->sw_chnl_inprogress = false; } else { RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c index 39cc7938eedf..3d8536bb0d2b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c @@ -1106,7 +1106,7 @@ u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw) if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { rtl8723ae_phy_sw_chnl_callback(hw); RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, - "sw_chnl_inprogress false schdule workitem\n"); + "sw_chnl_inprogress false schedule workitem\n"); rtlphy->sw_chnl_inprogress = false; } else { RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, From c4f74d35cac7cbc44877313b69550e2f5aeae77d Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Sun, 6 Jan 2013 20:27:22 -0500 Subject: [PATCH 180/478] drivers/net/wireless/mwl8k.c: avoid use-after-free Do not dereference p->station_id after kfree(cmd) because p points into the cmd data structure. Signed-off-by: Nickolai Zeldovich Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f221b95b90b3..83564d36e801 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4250,9 +4250,11 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, p->amsdu_enabled = 0; rc = mwl8k_post_cmd(hw, &cmd->header); + if (!rc) + rc = p->station_id; kfree(cmd); - return rc ? rc : p->station_id; + return rc; } static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, From 407ee23725bba0f273963bb744fea6cb3cf78bf4 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 7 Jan 2013 12:42:46 +0800 Subject: [PATCH 181/478] drivers/net/wireless/iwlegacy: use strlcpy instead of strncpy The fields must be null-terminated, or simple_strtoul will cause issue. Signed-off-by: Chen Gang Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/3945-mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index d604b4036a76..3726cd6fcd75 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3273,7 +3273,7 @@ il3945_store_measurement(struct device *d, struct device_attribute *attr, if (count) { char *p = buffer; - strncpy(buffer, buf, min(sizeof(buffer), count)); + strlcpy(buffer, buf, sizeof(buffer)); channel = simple_strtoul(p, NULL, 0); if (channel) params.channel = channel; From 5e20a4b53094651d80f856ff55a916b999dbb57a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 20 Dec 2012 15:55:01 -0600 Subject: [PATCH 182/478] b43: Fix firmware loading when driver is built into the kernel Recent versions of udev cause synchronous firmware loading from the probe routine to fail because the request to user space would time out. The original fix for b43 (commit 6b6fa58) moved the firmware load from the probe routine to a work queue, but it still used synchronous firmware loading. This method is OK when b43 is built as a module; however, it fails when the driver is compiled into the kernel. This version changes the code to load the initial firmware file using request_firmware_nowait(). A completion event is used to hold the work queue until that file is available. This driver reads several firmware files - the remainder can be read synchronously. On some test systems, the async read fails; however, a following synch read works, thus the async failure falls through to the sync try. Reported-and-Tested by: Felix Janda Signed-off-by: Larry Finger Cc: Stable (V3.4+) Signed-off-by: John W. Linville --- drivers/net/wireless/b43/b43.h | 5 +++ drivers/net/wireless/b43/main.c | 54 +++++++++++++++++++++++++-------- drivers/net/wireless/b43/main.h | 5 ++- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index b298e5d68be2..10e288d470e7 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "debugfs.h" @@ -722,6 +723,10 @@ enum b43_firmware_file_type { struct b43_request_fw_context { /* The device we are requesting the fw for. */ struct b43_wldev *dev; + /* a completion event structure needed if this call is asynchronous */ + struct completion fw_load_complete; + /* a pointer to the firmware object */ + const struct firmware *blob; /* The type of firmware to request. */ enum b43_firmware_file_type req_type; /* Error messages for each firmware type. */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 16ab280359bd..806e34c19281 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2088,11 +2088,18 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error) b43warn(wl, text); } +static void b43_fw_cb(const struct firmware *firmware, void *context) +{ + struct b43_request_fw_context *ctx = context; + + ctx->blob = firmware; + complete(&ctx->fw_load_complete); +} + int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, - struct b43_firmware_file *fw) + struct b43_firmware_file *fw, bool async) { - const struct firmware *blob; struct b43_fw_header *hdr; u32 size; int err; @@ -2131,11 +2138,31 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, B43_WARN_ON(1); return -ENOSYS; } - err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev); + if (async) { + /* do this part asynchronously */ + init_completion(&ctx->fw_load_complete); + err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname, + ctx->dev->dev->dev, GFP_KERNEL, + ctx, b43_fw_cb); + if (err < 0) { + pr_err("Unable to load firmware\n"); + return err; + } + /* stall here until fw ready */ + wait_for_completion(&ctx->fw_load_complete); + if (ctx->blob) + goto fw_ready; + /* On some ARM systems, the async request will fail, but the next sync + * request works. For this reason, we dall through here + */ + } + err = request_firmware(&ctx->blob, ctx->fwname, + ctx->dev->dev->dev); if (err == -ENOENT) { snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), - "Firmware file \"%s\" not found\n", ctx->fwname); + "Firmware file \"%s\" not found\n", + ctx->fwname); return err; } else if (err) { snprintf(ctx->errors[ctx->req_type], @@ -2144,14 +2171,15 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, ctx->fwname, err); return err; } - if (blob->size < sizeof(struct b43_fw_header)) +fw_ready: + if (ctx->blob->size < sizeof(struct b43_fw_header)) goto err_format; - hdr = (struct b43_fw_header *)(blob->data); + hdr = (struct b43_fw_header *)(ctx->blob->data); switch (hdr->type) { case B43_FW_TYPE_UCODE: case B43_FW_TYPE_PCM: size = be32_to_cpu(hdr->size); - if (size != blob->size - sizeof(struct b43_fw_header)) + if (size != ctx->blob->size - sizeof(struct b43_fw_header)) goto err_format; /* fallthrough */ case B43_FW_TYPE_IV: @@ -2162,7 +2190,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, goto err_format; } - fw->data = blob; + fw->data = ctx->blob; fw->filename = name; fw->type = ctx->req_type; @@ -2172,7 +2200,7 @@ err_format: snprintf(ctx->errors[ctx->req_type], sizeof(ctx->errors[ctx->req_type]), "Firmware file \"%s\" format error.\n", ctx->fwname); - release_firmware(blob); + release_firmware(ctx->blob); return -EPROTO; } @@ -2223,7 +2251,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) goto err_no_ucode; } } - err = b43_do_request_fw(ctx, filename, &fw->ucode); + err = b43_do_request_fw(ctx, filename, &fw->ucode, true); if (err) goto err_load; @@ -2235,7 +2263,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else goto err_no_pcm; fw->pcm_request_failed = false; - err = b43_do_request_fw(ctx, filename, &fw->pcm); + err = b43_do_request_fw(ctx, filename, &fw->pcm, false); if (err == -ENOENT) { /* We did not find a PCM file? Not fatal, but * core rev <= 10 must do without hwcrypto then. */ @@ -2296,7 +2324,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) default: goto err_no_initvals; } - err = b43_do_request_fw(ctx, filename, &fw->initvals); + err = b43_do_request_fw(ctx, filename, &fw->initvals, false); if (err) goto err_load; @@ -2355,7 +2383,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) default: goto err_no_initvals; } - err = b43_do_request_fw(ctx, filename, &fw->initvals_band); + err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false); if (err) goto err_load; diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h index 8c684cd33529..abac25ee958d 100644 --- a/drivers/net/wireless/b43/main.h +++ b/drivers/net/wireless/b43/main.h @@ -137,9 +137,8 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); struct b43_request_fw_context; -int b43_do_request_fw(struct b43_request_fw_context *ctx, - const char *name, - struct b43_firmware_file *fw); +int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, + struct b43_firmware_file *fw, bool async); void b43_do_release_fw(struct b43_firmware_file *fw); #endif /* B43_MAIN_H_ */ From d5374ef13ebda6ec93f0b4af6b30682ea4b14782 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 12 Dec 2012 06:30:55 +0100 Subject: [PATCH 183/478] rt2800: refactor RFCSR initialization rt2800_init_rfcsr() is too big, split RF initialization into per chip functions. Code will change, but we should setup the same values onto RF registers and in the same order as before. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 793 +++++++++++++----------- 1 file changed, 422 insertions(+), 371 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 12f93e42e160..0ece3537106d 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3866,6 +3866,400 @@ static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev, return rfcsr24; } +static void rt2800_init_rfcsr_305x_soc(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rfcsr_write(rt2x00dev, 0, 0x50); + rt2800_rfcsr_write(rt2x00dev, 1, 0x01); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf7); + rt2800_rfcsr_write(rt2x00dev, 3, 0x75); + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x03); + rt2800_rfcsr_write(rt2x00dev, 6, 0x02); + rt2800_rfcsr_write(rt2x00dev, 7, 0x50); + rt2800_rfcsr_write(rt2x00dev, 8, 0x39); + rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 10, 0x60); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x75); + rt2800_rfcsr_write(rt2x00dev, 13, 0x75); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x58); + rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 17, 0x92); + rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0xba); + rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x31); + rt2800_rfcsr_write(rt2x00dev, 24, 0x08); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + rt2800_rfcsr_write(rt2x00dev, 26, 0x25); + rt2800_rfcsr_write(rt2x00dev, 27, 0x23); + rt2800_rfcsr_write(rt2x00dev, 28, 0x13); + rt2800_rfcsr_write(rt2x00dev, 29, 0x83); + rt2800_rfcsr_write(rt2x00dev, 30, 0x00); + rt2800_rfcsr_write(rt2x00dev, 31, 0x00); +} + +static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x03); + rt2800_rfcsr_write(rt2x00dev, 6, 0x02); + rt2800_rfcsr_write(rt2x00dev, 7, 0x60); + rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 10, 0x41); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x58); + rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 17, 0x92); + rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0xba); + rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 24, 0x16); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); +} + +static void rt2800_init_rfcsr_3290(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + rt2800_rfcsr_write(rt2x00dev, 3, 0x08); + rt2800_rfcsr_write(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf3); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 18, 0x02); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 25, 0x83); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x05); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + rt2800_rfcsr_write(rt2x00dev, 47, 0x00); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x98); + rt2800_rfcsr_write(rt2x00dev, 52, 0x38); + rt2800_rfcsr_write(rt2x00dev, 53, 0x00); + rt2800_rfcsr_write(rt2x00dev, 54, 0x78); + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + rt2800_rfcsr_write(rt2x00dev, 56, 0x02); + rt2800_rfcsr_write(rt2x00dev, 57, 0x80); + rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 59, 0x09); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0xc1); +} + +static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rfcsr_write(rt2x00dev, 0, 0xf0); + rt2800_rfcsr_write(rt2x00dev, 1, 0x23); + rt2800_rfcsr_write(rt2x00dev, 2, 0x50); + rt2800_rfcsr_write(rt2x00dev, 3, 0x18); + rt2800_rfcsr_write(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0x33); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0xd2); + rt2800_rfcsr_write(rt2x00dev, 11, 0x42); + rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); + rt2800_rfcsr_write(rt2x00dev, 13, 0x00); + rt2800_rfcsr_write(rt2x00dev, 14, 0x5a); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x01); + rt2800_rfcsr_write(rt2x00dev, 18, 0x45); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x00); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x00); + rt2800_rfcsr_write(rt2x00dev, 24, 0x00); + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + rt2800_rfcsr_write(rt2x00dev, 26, 0x00); + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + rt2800_rfcsr_write(rt2x00dev, 28, 0x03); + rt2800_rfcsr_write(rt2x00dev, 29, 0x00); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x01); + rt2800_rfcsr_write(rt2x00dev, 35, 0x03); + rt2800_rfcsr_write(rt2x00dev, 36, 0xbd); + rt2800_rfcsr_write(rt2x00dev, 37, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 38, 0x5f); + rt2800_rfcsr_write(rt2x00dev, 39, 0xc5); + rt2800_rfcsr_write(rt2x00dev, 40, 0x33); + rt2800_rfcsr_write(rt2x00dev, 41, 0x5b); + rt2800_rfcsr_write(rt2x00dev, 42, 0x5b); + rt2800_rfcsr_write(rt2x00dev, 43, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 44, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 45, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 46, 0xdd); + rt2800_rfcsr_write(rt2x00dev, 47, 0x0d); + rt2800_rfcsr_write(rt2x00dev, 48, 0x14); + rt2800_rfcsr_write(rt2x00dev, 49, 0x00); + rt2800_rfcsr_write(rt2x00dev, 50, 0x2d); + rt2800_rfcsr_write(rt2x00dev, 51, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 52, 0x00); + rt2800_rfcsr_write(rt2x00dev, 53, 0x52); + rt2800_rfcsr_write(rt2x00dev, 54, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 55, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 56, 0x00); + rt2800_rfcsr_write(rt2x00dev, 57, 0x52); + rt2800_rfcsr_write(rt2x00dev, 58, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 59, 0x00); + rt2800_rfcsr_write(rt2x00dev, 60, 0x00); + rt2800_rfcsr_write(rt2x00dev, 61, 0x00); + rt2800_rfcsr_write(rt2x00dev, 62, 0x00); + rt2800_rfcsr_write(rt2x00dev, 63, 0x00); +} + +static void rt2800_init_rfcsr_3390(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rfcsr_write(rt2x00dev, 0, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 1, 0xe1); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 3, 0x62); + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x8b); + rt2800_rfcsr_write(rt2x00dev, 6, 0x42); + rt2800_rfcsr_write(rt2x00dev, 7, 0x34); + rt2800_rfcsr_write(rt2x00dev, 8, 0x00); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); + rt2800_rfcsr_write(rt2x00dev, 10, 0x61); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x3b); + rt2800_rfcsr_write(rt2x00dev, 13, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x53); + rt2800_rfcsr_write(rt2x00dev, 16, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 17, 0x94); + rt2800_rfcsr_write(rt2x00dev, 18, 0x5c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 20, 0xb2); + rt2800_rfcsr_write(rt2x00dev, 21, 0xf6); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x14); + rt2800_rfcsr_write(rt2x00dev, 24, 0x08); + rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); + rt2800_rfcsr_write(rt2x00dev, 26, 0x85); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 28, 0x41); + rt2800_rfcsr_write(rt2x00dev, 29, 0x8f); + rt2800_rfcsr_write(rt2x00dev, 30, 0x20); + rt2800_rfcsr_write(rt2x00dev, 31, 0x0f); +} + +static void rt2800_init_rfcsr_3572(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rfcsr_write(rt2x00dev, 0, 0x70); + rt2800_rfcsr_write(rt2x00dev, 1, 0x81); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 3, 0x02); + rt2800_rfcsr_write(rt2x00dev, 4, 0x4c); + rt2800_rfcsr_write(rt2x00dev, 5, 0x05); + rt2800_rfcsr_write(rt2x00dev, 6, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); + rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); + rt2800_rfcsr_write(rt2x00dev, 12, 0x70); + rt2800_rfcsr_write(rt2x00dev, 13, 0x65); + rt2800_rfcsr_write(rt2x00dev, 14, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 15, 0x53); + rt2800_rfcsr_write(rt2x00dev, 16, 0x4c); + rt2800_rfcsr_write(rt2x00dev, 17, 0x23); + rt2800_rfcsr_write(rt2x00dev, 18, 0xac); + rt2800_rfcsr_write(rt2x00dev, 19, 0x93); + rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 21, 0xd0); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 24, 0x16); + rt2800_rfcsr_write(rt2x00dev, 25, 0x15); + rt2800_rfcsr_write(rt2x00dev, 26, 0x85); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 30, 0x09); + rt2800_rfcsr_write(rt2x00dev, 31, 0x10); +} + +static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + rt2800_rfcsr_write(rt2x00dev, 3, 0x88); + rt2800_rfcsr_write(rt2x00dev, 5, 0x10); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); + else + rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0xc6); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 14, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x03); + rt2800_rfcsr_write(rt2x00dev, 19, 0x00); + + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x00); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 23, 0x00); + rt2800_rfcsr_write(rt2x00dev, 24, 0x00); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + else + rt2800_rfcsr_write(rt2x00dev, 25, 0xc0); + rt2800_rfcsr_write(rt2x00dev, 26, 0x00); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + + rt2800_rfcsr_write(rt2x00dev, 30, 0x00); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x07); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x08); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); + else + rt2800_rfcsr_write(rt2x00dev, 40, 0x4b); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd2); + rt2800_rfcsr_write(rt2x00dev, 43, 0x9a); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + else + rt2800_rfcsr_write(rt2x00dev, 46, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 47, 0x00); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x94); + + rt2800_rfcsr_write(rt2x00dev, 52, 0x38); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 53, 0x00); + else + rt2800_rfcsr_write(rt2x00dev, 53, 0x84); + rt2800_rfcsr_write(rt2x00dev, 54, 0x78); + rt2800_rfcsr_write(rt2x00dev, 55, 0x44); + rt2800_rfcsr_write(rt2x00dev, 56, 0x22); + rt2800_rfcsr_write(rt2x00dev, 57, 0x80); + rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 59, 0x63); + + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 61, 0xd1); + else + rt2800_rfcsr_write(rt2x00dev, 61, 0xdd); + rt2800_rfcsr_write(rt2x00dev, 62, 0x00); + rt2800_rfcsr_write(rt2x00dev, 63, 0x00); +} + +static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rfcsr_write(rt2x00dev, 1, 0x17); + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + rt2800_rfcsr_write(rt2x00dev, 3, 0x88); + rt2800_rfcsr_write(rt2x00dev, 5, 0x10); + rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 14, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x03); + rt2800_rfcsr_write(rt2x00dev, 19, 0x4d); + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x8d); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 23, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 24, 0x44); + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x20); + rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); + rt2800_rfcsr_write(rt2x00dev, 34, 0x07); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x08); + rt2800_rfcsr_write(rt2x00dev, 38, 0x89); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + rt2800_rfcsr_write(rt2x00dev, 47, 0x0c); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x94); + rt2800_rfcsr_write(rt2x00dev, 50, 0x94); + rt2800_rfcsr_write(rt2x00dev, 51, 0x3a); + rt2800_rfcsr_write(rt2x00dev, 52, 0x48); + rt2800_rfcsr_write(rt2x00dev, 53, 0x44); + rt2800_rfcsr_write(rt2x00dev, 54, 0x38); + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + rt2800_rfcsr_write(rt2x00dev, 56, 0xa1); + rt2800_rfcsr_write(rt2x00dev, 57, 0x00); + rt2800_rfcsr_write(rt2x00dev, 58, 0x39); + rt2800_rfcsr_write(rt2x00dev, 59, 0x07); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0x91); + rt2800_rfcsr_write(rt2x00dev, 62, 0x39); + rt2800_rfcsr_write(rt2x00dev, 63, 0x07); +} + static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) { struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; @@ -3889,6 +4283,7 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) /* * Init RF calibration. */ + if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { @@ -3907,379 +4302,35 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); } - if (rt2x00_rt(rt2x00dev, RT3070) || - rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090)) { - rt2800_rfcsr_write(rt2x00dev, 4, 0x40); - rt2800_rfcsr_write(rt2x00dev, 5, 0x03); - rt2800_rfcsr_write(rt2x00dev, 6, 0x02); - rt2800_rfcsr_write(rt2x00dev, 7, 0x60); - rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 10, 0x41); - rt2800_rfcsr_write(rt2x00dev, 11, 0x21); - rt2800_rfcsr_write(rt2x00dev, 12, 0x7b); - rt2800_rfcsr_write(rt2x00dev, 14, 0x90); - rt2800_rfcsr_write(rt2x00dev, 15, 0x58); - rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); - rt2800_rfcsr_write(rt2x00dev, 17, 0x92); - rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); - rt2800_rfcsr_write(rt2x00dev, 19, 0x02); - rt2800_rfcsr_write(rt2x00dev, 20, 0xba); - rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 24, 0x16); - rt2800_rfcsr_write(rt2x00dev, 25, 0x01); - rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); - } else if (rt2x00_rt(rt2x00dev, RT3290)) { - rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 2, 0x80); - rt2800_rfcsr_write(rt2x00dev, 3, 0x08); - rt2800_rfcsr_write(rt2x00dev, 4, 0x00); - rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); - rt2800_rfcsr_write(rt2x00dev, 8, 0xf3); - rt2800_rfcsr_write(rt2x00dev, 9, 0x02); - rt2800_rfcsr_write(rt2x00dev, 10, 0x53); - rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 12, 0x46); - rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); - rt2800_rfcsr_write(rt2x00dev, 18, 0x02); - rt2800_rfcsr_write(rt2x00dev, 22, 0x20); - rt2800_rfcsr_write(rt2x00dev, 25, 0x83); - rt2800_rfcsr_write(rt2x00dev, 26, 0x82); - rt2800_rfcsr_write(rt2x00dev, 27, 0x09); - rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x80); - rt2800_rfcsr_write(rt2x00dev, 33, 0x00); - rt2800_rfcsr_write(rt2x00dev, 34, 0x05); - rt2800_rfcsr_write(rt2x00dev, 35, 0x12); - rt2800_rfcsr_write(rt2x00dev, 36, 0x00); - rt2800_rfcsr_write(rt2x00dev, 38, 0x85); - rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); - rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); - rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); - rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); - rt2800_rfcsr_write(rt2x00dev, 43, 0x7b); - rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); - rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); - rt2800_rfcsr_write(rt2x00dev, 46, 0x73); - rt2800_rfcsr_write(rt2x00dev, 47, 0x00); - rt2800_rfcsr_write(rt2x00dev, 48, 0x10); - rt2800_rfcsr_write(rt2x00dev, 49, 0x98); - rt2800_rfcsr_write(rt2x00dev, 52, 0x38); - rt2800_rfcsr_write(rt2x00dev, 53, 0x00); - rt2800_rfcsr_write(rt2x00dev, 54, 0x78); - rt2800_rfcsr_write(rt2x00dev, 55, 0x43); - rt2800_rfcsr_write(rt2x00dev, 56, 0x02); - rt2800_rfcsr_write(rt2x00dev, 57, 0x80); - rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 59, 0x09); - rt2800_rfcsr_write(rt2x00dev, 60, 0x45); - rt2800_rfcsr_write(rt2x00dev, 61, 0xc1); - } else if (rt2x00_rt(rt2x00dev, RT3390)) { - rt2800_rfcsr_write(rt2x00dev, 0, 0xa0); - rt2800_rfcsr_write(rt2x00dev, 1, 0xe1); - rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 3, 0x62); - rt2800_rfcsr_write(rt2x00dev, 4, 0x40); - rt2800_rfcsr_write(rt2x00dev, 5, 0x8b); - rt2800_rfcsr_write(rt2x00dev, 6, 0x42); - rt2800_rfcsr_write(rt2x00dev, 7, 0x34); - rt2800_rfcsr_write(rt2x00dev, 8, 0x00); - rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); - rt2800_rfcsr_write(rt2x00dev, 10, 0x61); - rt2800_rfcsr_write(rt2x00dev, 11, 0x21); - rt2800_rfcsr_write(rt2x00dev, 12, 0x3b); - rt2800_rfcsr_write(rt2x00dev, 13, 0xe0); - rt2800_rfcsr_write(rt2x00dev, 14, 0x90); - rt2800_rfcsr_write(rt2x00dev, 15, 0x53); - rt2800_rfcsr_write(rt2x00dev, 16, 0xe0); - rt2800_rfcsr_write(rt2x00dev, 17, 0x94); - rt2800_rfcsr_write(rt2x00dev, 18, 0x5c); - rt2800_rfcsr_write(rt2x00dev, 19, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 20, 0xb2); - rt2800_rfcsr_write(rt2x00dev, 21, 0xf6); - rt2800_rfcsr_write(rt2x00dev, 22, 0x00); - rt2800_rfcsr_write(rt2x00dev, 23, 0x14); - rt2800_rfcsr_write(rt2x00dev, 24, 0x08); - rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); - rt2800_rfcsr_write(rt2x00dev, 26, 0x85); - rt2800_rfcsr_write(rt2x00dev, 27, 0x00); - rt2800_rfcsr_write(rt2x00dev, 28, 0x41); - rt2800_rfcsr_write(rt2x00dev, 29, 0x8f); - rt2800_rfcsr_write(rt2x00dev, 30, 0x20); - rt2800_rfcsr_write(rt2x00dev, 31, 0x0f); - } else if (rt2x00_rt(rt2x00dev, RT3572)) { - rt2800_rfcsr_write(rt2x00dev, 0, 0x70); - rt2800_rfcsr_write(rt2x00dev, 1, 0x81); - rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 3, 0x02); - rt2800_rfcsr_write(rt2x00dev, 4, 0x4c); - rt2800_rfcsr_write(rt2x00dev, 5, 0x05); - rt2800_rfcsr_write(rt2x00dev, 6, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); - rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); - rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); - rt2800_rfcsr_write(rt2x00dev, 12, 0x70); - rt2800_rfcsr_write(rt2x00dev, 13, 0x65); - rt2800_rfcsr_write(rt2x00dev, 14, 0xa0); - rt2800_rfcsr_write(rt2x00dev, 15, 0x53); - rt2800_rfcsr_write(rt2x00dev, 16, 0x4c); - rt2800_rfcsr_write(rt2x00dev, 17, 0x23); - rt2800_rfcsr_write(rt2x00dev, 18, 0xac); - rt2800_rfcsr_write(rt2x00dev, 19, 0x93); - rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); - rt2800_rfcsr_write(rt2x00dev, 21, 0xd0); - rt2800_rfcsr_write(rt2x00dev, 22, 0x00); - rt2800_rfcsr_write(rt2x00dev, 23, 0x3c); - rt2800_rfcsr_write(rt2x00dev, 24, 0x16); - rt2800_rfcsr_write(rt2x00dev, 25, 0x15); - rt2800_rfcsr_write(rt2x00dev, 26, 0x85); - rt2800_rfcsr_write(rt2x00dev, 27, 0x00); - rt2800_rfcsr_write(rt2x00dev, 28, 0x00); - rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); - rt2800_rfcsr_write(rt2x00dev, 30, 0x09); - rt2800_rfcsr_write(rt2x00dev, 31, 0x10); - } else if (rt2800_is_305x_soc(rt2x00dev)) { - rt2800_rfcsr_write(rt2x00dev, 0, 0x50); - rt2800_rfcsr_write(rt2x00dev, 1, 0x01); - rt2800_rfcsr_write(rt2x00dev, 2, 0xf7); - rt2800_rfcsr_write(rt2x00dev, 3, 0x75); - rt2800_rfcsr_write(rt2x00dev, 4, 0x40); - rt2800_rfcsr_write(rt2x00dev, 5, 0x03); - rt2800_rfcsr_write(rt2x00dev, 6, 0x02); - rt2800_rfcsr_write(rt2x00dev, 7, 0x50); - rt2800_rfcsr_write(rt2x00dev, 8, 0x39); - rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 10, 0x60); - rt2800_rfcsr_write(rt2x00dev, 11, 0x21); - rt2800_rfcsr_write(rt2x00dev, 12, 0x75); - rt2800_rfcsr_write(rt2x00dev, 13, 0x75); - rt2800_rfcsr_write(rt2x00dev, 14, 0x90); - rt2800_rfcsr_write(rt2x00dev, 15, 0x58); - rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); - rt2800_rfcsr_write(rt2x00dev, 17, 0x92); - rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); - rt2800_rfcsr_write(rt2x00dev, 19, 0x02); - rt2800_rfcsr_write(rt2x00dev, 20, 0xba); - rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 22, 0x00); - rt2800_rfcsr_write(rt2x00dev, 23, 0x31); - rt2800_rfcsr_write(rt2x00dev, 24, 0x08); - rt2800_rfcsr_write(rt2x00dev, 25, 0x01); - rt2800_rfcsr_write(rt2x00dev, 26, 0x25); - rt2800_rfcsr_write(rt2x00dev, 27, 0x23); - rt2800_rfcsr_write(rt2x00dev, 28, 0x13); - rt2800_rfcsr_write(rt2x00dev, 29, 0x83); - rt2800_rfcsr_write(rt2x00dev, 30, 0x00); - rt2800_rfcsr_write(rt2x00dev, 31, 0x00); + if (rt2800_is_305x_soc(rt2x00dev)) { + rt2800_init_rfcsr_305x_soc(rt2x00dev); return 0; - } else if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_rfcsr_write(rt2x00dev, 0, 0xf0); - rt2800_rfcsr_write(rt2x00dev, 1, 0x23); - rt2800_rfcsr_write(rt2x00dev, 2, 0x50); - rt2800_rfcsr_write(rt2x00dev, 3, 0x18); - rt2800_rfcsr_write(rt2x00dev, 4, 0x00); - rt2800_rfcsr_write(rt2x00dev, 5, 0x00); - rt2800_rfcsr_write(rt2x00dev, 6, 0x33); - rt2800_rfcsr_write(rt2x00dev, 7, 0x00); - rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 9, 0x02); - rt2800_rfcsr_write(rt2x00dev, 10, 0xd2); - rt2800_rfcsr_write(rt2x00dev, 11, 0x42); - rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); - rt2800_rfcsr_write(rt2x00dev, 13, 0x00); - rt2800_rfcsr_write(rt2x00dev, 14, 0x5a); - rt2800_rfcsr_write(rt2x00dev, 15, 0x00); - rt2800_rfcsr_write(rt2x00dev, 16, 0x01); - rt2800_rfcsr_write(rt2x00dev, 18, 0x45); - rt2800_rfcsr_write(rt2x00dev, 19, 0x02); - rt2800_rfcsr_write(rt2x00dev, 20, 0x00); - rt2800_rfcsr_write(rt2x00dev, 21, 0x00); - rt2800_rfcsr_write(rt2x00dev, 22, 0x00); - rt2800_rfcsr_write(rt2x00dev, 23, 0x00); - rt2800_rfcsr_write(rt2x00dev, 24, 0x00); - rt2800_rfcsr_write(rt2x00dev, 25, 0x80); - rt2800_rfcsr_write(rt2x00dev, 26, 0x00); - rt2800_rfcsr_write(rt2x00dev, 27, 0x03); - rt2800_rfcsr_write(rt2x00dev, 28, 0x03); - rt2800_rfcsr_write(rt2x00dev, 29, 0x00); - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x80); - rt2800_rfcsr_write(rt2x00dev, 33, 0x00); - rt2800_rfcsr_write(rt2x00dev, 34, 0x01); - rt2800_rfcsr_write(rt2x00dev, 35, 0x03); - rt2800_rfcsr_write(rt2x00dev, 36, 0xbd); - rt2800_rfcsr_write(rt2x00dev, 37, 0x3c); - rt2800_rfcsr_write(rt2x00dev, 38, 0x5f); - rt2800_rfcsr_write(rt2x00dev, 39, 0xc5); - rt2800_rfcsr_write(rt2x00dev, 40, 0x33); - rt2800_rfcsr_write(rt2x00dev, 41, 0x5b); - rt2800_rfcsr_write(rt2x00dev, 42, 0x5b); - rt2800_rfcsr_write(rt2x00dev, 43, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 44, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 45, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 46, 0xdd); - rt2800_rfcsr_write(rt2x00dev, 47, 0x0d); - rt2800_rfcsr_write(rt2x00dev, 48, 0x14); - rt2800_rfcsr_write(rt2x00dev, 49, 0x00); - rt2800_rfcsr_write(rt2x00dev, 50, 0x2d); - rt2800_rfcsr_write(rt2x00dev, 51, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 52, 0x00); - rt2800_rfcsr_write(rt2x00dev, 53, 0x52); - rt2800_rfcsr_write(rt2x00dev, 54, 0x1b); - rt2800_rfcsr_write(rt2x00dev, 55, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 56, 0x00); - rt2800_rfcsr_write(rt2x00dev, 57, 0x52); - rt2800_rfcsr_write(rt2x00dev, 58, 0x1b); - rt2800_rfcsr_write(rt2x00dev, 59, 0x00); - rt2800_rfcsr_write(rt2x00dev, 60, 0x00); - rt2800_rfcsr_write(rt2x00dev, 61, 0x00); - rt2800_rfcsr_write(rt2x00dev, 62, 0x00); - rt2800_rfcsr_write(rt2x00dev, 63, 0x00); - } else if (rt2x00_rt(rt2x00dev, RT5390)) { - rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 2, 0x80); - rt2800_rfcsr_write(rt2x00dev, 3, 0x88); - rt2800_rfcsr_write(rt2x00dev, 5, 0x10); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); - else - rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); - rt2800_rfcsr_write(rt2x00dev, 7, 0x00); - rt2800_rfcsr_write(rt2x00dev, 10, 0x53); - rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 12, 0xc6); - rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); - rt2800_rfcsr_write(rt2x00dev, 14, 0x00); - rt2800_rfcsr_write(rt2x00dev, 15, 0x00); - rt2800_rfcsr_write(rt2x00dev, 16, 0x00); - rt2800_rfcsr_write(rt2x00dev, 18, 0x03); - rt2800_rfcsr_write(rt2x00dev, 19, 0x00); + } - rt2800_rfcsr_write(rt2x00dev, 20, 0x00); - rt2800_rfcsr_write(rt2x00dev, 21, 0x00); - rt2800_rfcsr_write(rt2x00dev, 22, 0x20); - rt2800_rfcsr_write(rt2x00dev, 23, 0x00); - rt2800_rfcsr_write(rt2x00dev, 24, 0x00); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 25, 0x80); - else - rt2800_rfcsr_write(rt2x00dev, 25, 0xc0); - rt2800_rfcsr_write(rt2x00dev, 26, 0x00); - rt2800_rfcsr_write(rt2x00dev, 27, 0x09); - rt2800_rfcsr_write(rt2x00dev, 28, 0x00); - rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - - rt2800_rfcsr_write(rt2x00dev, 30, 0x00); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x80); - rt2800_rfcsr_write(rt2x00dev, 33, 0x00); - rt2800_rfcsr_write(rt2x00dev, 34, 0x07); - rt2800_rfcsr_write(rt2x00dev, 35, 0x12); - rt2800_rfcsr_write(rt2x00dev, 36, 0x00); - rt2800_rfcsr_write(rt2x00dev, 37, 0x08); - rt2800_rfcsr_write(rt2x00dev, 38, 0x85); - rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); - - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); - else - rt2800_rfcsr_write(rt2x00dev, 40, 0x4b); - rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); - rt2800_rfcsr_write(rt2x00dev, 42, 0xd2); - rt2800_rfcsr_write(rt2x00dev, 43, 0x9a); - rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); - rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 46, 0x73); - else - rt2800_rfcsr_write(rt2x00dev, 46, 0x7b); - rt2800_rfcsr_write(rt2x00dev, 47, 0x00); - rt2800_rfcsr_write(rt2x00dev, 48, 0x10); - rt2800_rfcsr_write(rt2x00dev, 49, 0x94); - - rt2800_rfcsr_write(rt2x00dev, 52, 0x38); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 53, 0x00); - else - rt2800_rfcsr_write(rt2x00dev, 53, 0x84); - rt2800_rfcsr_write(rt2x00dev, 54, 0x78); - rt2800_rfcsr_write(rt2x00dev, 55, 0x44); - rt2800_rfcsr_write(rt2x00dev, 56, 0x22); - rt2800_rfcsr_write(rt2x00dev, 57, 0x80); - rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 59, 0x63); - - rt2800_rfcsr_write(rt2x00dev, 60, 0x45); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 61, 0xd1); - else - rt2800_rfcsr_write(rt2x00dev, 61, 0xdd); - rt2800_rfcsr_write(rt2x00dev, 62, 0x00); - rt2800_rfcsr_write(rt2x00dev, 63, 0x00); - } else if (rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_rfcsr_write(rt2x00dev, 1, 0x17); - rt2800_rfcsr_write(rt2x00dev, 2, 0x80); - rt2800_rfcsr_write(rt2x00dev, 3, 0x88); - rt2800_rfcsr_write(rt2x00dev, 5, 0x10); - rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); - rt2800_rfcsr_write(rt2x00dev, 7, 0x00); - rt2800_rfcsr_write(rt2x00dev, 10, 0x53); - rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 12, 0x46); - rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); - rt2800_rfcsr_write(rt2x00dev, 14, 0x00); - rt2800_rfcsr_write(rt2x00dev, 15, 0x00); - rt2800_rfcsr_write(rt2x00dev, 16, 0x00); - rt2800_rfcsr_write(rt2x00dev, 18, 0x03); - rt2800_rfcsr_write(rt2x00dev, 19, 0x4d); - rt2800_rfcsr_write(rt2x00dev, 20, 0x00); - rt2800_rfcsr_write(rt2x00dev, 21, 0x8d); - rt2800_rfcsr_write(rt2x00dev, 22, 0x20); - rt2800_rfcsr_write(rt2x00dev, 23, 0x0b); - rt2800_rfcsr_write(rt2x00dev, 24, 0x44); - rt2800_rfcsr_write(rt2x00dev, 25, 0x80); - rt2800_rfcsr_write(rt2x00dev, 26, 0x82); - rt2800_rfcsr_write(rt2x00dev, 27, 0x09); - rt2800_rfcsr_write(rt2x00dev, 28, 0x00); - rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x20); - rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); - rt2800_rfcsr_write(rt2x00dev, 34, 0x07); - rt2800_rfcsr_write(rt2x00dev, 35, 0x12); - rt2800_rfcsr_write(rt2x00dev, 36, 0x00); - rt2800_rfcsr_write(rt2x00dev, 37, 0x08); - rt2800_rfcsr_write(rt2x00dev, 38, 0x89); - rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); - rt2800_rfcsr_write(rt2x00dev, 40, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); - rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); - rt2800_rfcsr_write(rt2x00dev, 43, 0x9b); - rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); - rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); - rt2800_rfcsr_write(rt2x00dev, 46, 0x73); - rt2800_rfcsr_write(rt2x00dev, 47, 0x0c); - rt2800_rfcsr_write(rt2x00dev, 48, 0x10); - rt2800_rfcsr_write(rt2x00dev, 49, 0x94); - rt2800_rfcsr_write(rt2x00dev, 50, 0x94); - rt2800_rfcsr_write(rt2x00dev, 51, 0x3a); - rt2800_rfcsr_write(rt2x00dev, 52, 0x48); - rt2800_rfcsr_write(rt2x00dev, 53, 0x44); - rt2800_rfcsr_write(rt2x00dev, 54, 0x38); - rt2800_rfcsr_write(rt2x00dev, 55, 0x43); - rt2800_rfcsr_write(rt2x00dev, 56, 0xa1); - rt2800_rfcsr_write(rt2x00dev, 57, 0x00); - rt2800_rfcsr_write(rt2x00dev, 58, 0x39); - rt2800_rfcsr_write(rt2x00dev, 59, 0x07); - rt2800_rfcsr_write(rt2x00dev, 60, 0x45); - rt2800_rfcsr_write(rt2x00dev, 61, 0x91); - rt2800_rfcsr_write(rt2x00dev, 62, 0x39); - rt2800_rfcsr_write(rt2x00dev, 63, 0x07); + switch (rt2x00dev->chip.rt) { + case RT3070: + case RT3071: + case RT3090: + rt2800_init_rfcsr_30xx(rt2x00dev); + break; + case RT3290: + rt2800_init_rfcsr_3290(rt2x00dev); + break; + case RT3352: + rt2800_init_rfcsr_3352(rt2x00dev); + break; + case RT3390: + rt2800_init_rfcsr_3390(rt2x00dev); + break; + case RT3572: + rt2800_init_rfcsr_3572(rt2x00dev); + break; + case RT5390: + rt2800_init_rfcsr_5390(rt2x00dev); + break; + case RT5392: + rt2800_init_rfcsr_5392(rt2x00dev); + break; } if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { From b81950b165ff71d826fcac851153f9265a83d9ab Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 12 Dec 2012 13:14:22 +0100 Subject: [PATCH 184/478] ath9k: use the devres API for allocations/mappings Signed-off-by: Felix Fietkau Acked-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ahb.c | 22 ++---- drivers/net/wireless/ath/ath9k/ath9k.h | 5 -- drivers/net/wireless/ath/ath9k/init.c | 100 ++++++++----------------- drivers/net/wireless/ath/ath9k/mci.c | 7 +- drivers/net/wireless/ath/ath9k/pci.c | 39 ++-------- drivers/net/wireless/ath/ath9k/recv.c | 11 +-- drivers/net/wireless/ath/ath9k/xmit.c | 37 ++------- 7 files changed, 51 insertions(+), 170 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 3a69804f4c16..d1ff3c246a12 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -86,29 +86,25 @@ static int ath_ahb_probe(struct platform_device *pdev) if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "no platform data specified\n"); - ret = -EINVAL; - goto err_out; + return -EINVAL; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "no memory resource found\n"); - ret = -ENXIO; - goto err_out; + return -ENXIO; } - mem = ioremap_nocache(res->start, resource_size(res)); + mem = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); if (mem == NULL) { dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENOMEM; - goto err_out; + return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { dev_err(&pdev->dev, "no IRQ resource found\n"); - ret = -ENXIO; - goto err_iounmap; + return -ENXIO; } irq = res->start; @@ -116,8 +112,7 @@ static int ath_ahb_probe(struct platform_device *pdev) hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (hw == NULL) { dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); - ret = -ENOMEM; - goto err_iounmap; + return -ENOMEM; } SET_IEEE80211_DEV(hw, &pdev->dev); @@ -156,9 +151,6 @@ static int ath_ahb_probe(struct platform_device *pdev) err_free_hw: ieee80211_free_hw(hw); platform_set_drvdata(pdev, NULL); - err_iounmap: - iounmap(mem); - err_out: return ret; } @@ -168,12 +160,10 @@ static int ath_ahb_remove(struct platform_device *pdev) if (hw) { struct ath_softc *sc = hw->priv; - void __iomem *mem = sc->mem; ath9k_deinit_device(sc); free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); - iounmap(mem); platform_set_drvdata(pdev, NULL); } diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 86e26a19efda..72501073b499 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -109,14 +109,11 @@ struct ath_descdma { void *dd_desc; dma_addr_t dd_desc_paddr; u32 dd_desc_len; - struct ath_buf *dd_bufptr; }; int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, struct list_head *head, const char *name, int nbuf, int ndesc, bool is_tx); -void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, - struct list_head *head); /***********/ /* RX / TX */ @@ -320,7 +317,6 @@ struct ath_rx { spinlock_t rxbuflock; struct list_head rxbuf; struct ath_descdma rxdma; - struct ath_buf *rx_bufptr; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; struct sk_buff *frag; @@ -345,7 +341,6 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an); void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an); void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); int ath_tx_init(struct ath_softc *sc, int nbufs); -void ath_tx_cleanup(struct ath_softc *sc); int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_tx_queue_info *q); void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index f69ef5d48c7b..0f00a1306ac0 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -337,7 +337,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 *ds; struct ath_buf *bf; - int i, bsize, error, desc_len; + int i, bsize, desc_len; ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n", name, nbuf, ndesc); @@ -353,8 +353,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, if ((desc_len % 4) != 0) { ath_err(common, "ath_desc not DWORD aligned\n"); BUG_ON((desc_len % 4) != 0); - error = -ENOMEM; - goto fail; + return -ENOMEM; } dd->dd_desc_len = desc_len * nbuf * ndesc; @@ -378,12 +377,11 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, } /* allocate descriptors */ - dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, - &dd->dd_desc_paddr, GFP_KERNEL); - if (dd->dd_desc == NULL) { - error = -ENOMEM; - goto fail; - } + dd->dd_desc = dmam_alloc_coherent(sc->dev, dd->dd_desc_len, + &dd->dd_desc_paddr, GFP_KERNEL); + if (!dd->dd_desc) + return -ENOMEM; + ds = (u8 *) dd->dd_desc; ath_dbg(common, CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", name, ds, (u32) dd->dd_desc_len, @@ -391,12 +389,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, /* allocate buffers */ bsize = sizeof(struct ath_buf) * nbuf; - bf = kzalloc(bsize, GFP_KERNEL); - if (bf == NULL) { - error = -ENOMEM; - goto fail2; - } - dd->dd_bufptr = bf; + bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); + if (!bf) + return -ENOMEM; for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { bf->bf_desc = ds; @@ -422,12 +417,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, list_add_tail(&bf->list, head); } return 0; -fail2: - dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, - dd->dd_desc_paddr); -fail: - memset(dd, 0, sizeof(*dd)); - return error; } static int ath9k_init_queues(struct ath_softc *sc) @@ -457,11 +446,13 @@ static int ath9k_init_channels_rates(struct ath_softc *sc) ATH9K_NUM_CHANNELS); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { - channels = kmemdup(ath9k_2ghz_chantable, + channels = devm_kzalloc(sc->dev, sizeof(ath9k_2ghz_chantable), GFP_KERNEL); if (!channels) return -ENOMEM; + memcpy(channels, ath9k_2ghz_chantable, + sizeof(ath9k_2ghz_chantable)); sc->sbands[IEEE80211_BAND_2GHZ].channels = channels; sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; sc->sbands[IEEE80211_BAND_2GHZ].n_channels = @@ -472,14 +463,13 @@ static int ath9k_init_channels_rates(struct ath_softc *sc) } if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { - channels = kmemdup(ath9k_5ghz_chantable, + channels = devm_kzalloc(sc->dev, sizeof(ath9k_5ghz_chantable), GFP_KERNEL); - if (!channels) { - if (sc->sbands[IEEE80211_BAND_2GHZ].channels) - kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels); + if (!channels) return -ENOMEM; - } + memcpy(channels, ath9k_5ghz_chantable, + sizeof(ath9k_5ghz_chantable)); sc->sbands[IEEE80211_BAND_5GHZ].channels = channels; sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; sc->sbands[IEEE80211_BAND_5GHZ].n_channels = @@ -565,7 +555,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, int ret = 0, i; int csz = 0; - ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); + ah = devm_kzalloc(sc->dev, sizeof(struct ath_hw), GFP_KERNEL); if (!ah) return -ENOMEM; @@ -636,7 +626,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, if (pdata && pdata->eeprom_name) { ret = ath9k_eeprom_request(sc, pdata->eeprom_name); if (ret) - goto err_eeprom; + return ret; } /* Initializes the hardware for all supported chipsets */ @@ -676,10 +666,6 @@ err_queues: ath9k_hw_deinit(ah); err_hw: ath9k_eeprom_release(sc); -err_eeprom: - kfree(ah); - sc->sc_ah = NULL; - return ret; } @@ -844,8 +830,8 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, /* Bring up device */ error = ath9k_init_softc(devid, sc, bus_ops); - if (error != 0) - goto error_init; + if (error) + return error; ah = sc->sc_ah; common = ath9k_hw_common(ah); @@ -855,19 +841,19 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, error = ath_regd_init(&common->regulatory, sc->hw->wiphy, ath9k_reg_notifier); if (error) - goto error_regd; + goto deinit; reg = &common->regulatory; /* Setup TX DMA */ error = ath_tx_init(sc, ATH_TXBUF); if (error != 0) - goto error_tx; + goto deinit; /* Setup RX DMA */ error = ath_rx_init(sc, ATH_RXBUF); if (error != 0) - goto error_rx; + goto deinit; ath9k_init_txpower_limits(sc); @@ -881,19 +867,19 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) - goto error_register; + goto rx_cleanup; error = ath9k_init_debug(ah); if (error) { ath_err(common, "Unable to create debugfs files\n"); - goto error_world; + goto unregister; } /* Handle world regulatory */ if (!ath_is_world_regd(reg)) { error = regulatory_hint(hw->wiphy, reg->alpha2); if (error) - goto error_world; + goto unregister; } ath_init_leds(sc); @@ -901,17 +887,12 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, return 0; -error_world: +unregister: ieee80211_unregister_hw(hw); -error_register: +rx_cleanup: ath_rx_cleanup(sc); -error_rx: - ath_tx_cleanup(sc); -error_tx: - /* Nothing */ -error_regd: +deinit: ath9k_deinit_softc(sc); -error_init: return error; } @@ -923,12 +904,6 @@ static void ath9k_deinit_softc(struct ath_softc *sc) { int i = 0; - if (sc->sbands[IEEE80211_BAND_2GHZ].channels) - kfree(sc->sbands[IEEE80211_BAND_2GHZ].channels); - - if (sc->sbands[IEEE80211_BAND_5GHZ].channels) - kfree(sc->sbands[IEEE80211_BAND_5GHZ].channels); - ath9k_deinit_btcoex(sc); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) @@ -940,8 +915,6 @@ static void ath9k_deinit_softc(struct ath_softc *sc) sc->dfs_detector->exit(sc->dfs_detector); ath9k_eeprom_release(sc); - kfree(sc->sc_ah); - sc->sc_ah = NULL; } void ath9k_deinit_device(struct ath_softc *sc) @@ -957,22 +930,9 @@ void ath9k_deinit_device(struct ath_softc *sc) ieee80211_unregister_hw(hw); ath_rx_cleanup(sc); - ath_tx_cleanup(sc); ath9k_deinit_softc(sc); } -void ath_descdma_cleanup(struct ath_softc *sc, - struct ath_descdma *dd, - struct list_head *head) -{ - dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, - dd->dd_desc_paddr); - - INIT_LIST_HEAD(head); - kfree(dd->dd_bufptr); - memset(dd, 0, sizeof(*dd)); -} - /************************/ /* Module Hooks */ /************************/ diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 5c02702f21e7..d2074334ec9b 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -438,7 +438,7 @@ int ath_mci_setup(struct ath_softc *sc) struct ath_mci_buf *buf = &mci->sched_buf; int ret; - buf->bf_addr = dma_alloc_coherent(sc->dev, + buf->bf_addr = dmam_alloc_coherent(sc->dev, ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, &buf->bf_paddr, GFP_KERNEL); @@ -477,11 +477,6 @@ void ath_mci_cleanup(struct ath_softc *sc) struct ath_mci_coex *mci = &sc->mci_coex; struct ath_mci_buf *buf = &mci->sched_buf; - if (buf->bf_addr) - dma_free_coherent(sc->dev, - ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, - buf->bf_addr, buf->bf_paddr); - ar9003_mci_cleanup(ah); ath_dbg(common, MCI, "MCI De-Initialized\n"); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 7ae73fbd9136..0e0d39583837 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -147,7 +147,6 @@ static const struct ath_bus_ops ath_pci_bus_ops = { static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - void __iomem *mem; struct ath_softc *sc; struct ieee80211_hw *hw; u8 csz; @@ -155,19 +154,19 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) int ret = 0; char hw_name[64]; - if (pci_enable_device(pdev)) + if (pcim_enable_device(pdev)) return -EIO; ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { pr_err("32-bit DMA not available\n"); - goto err_dma; + return ret; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { pr_err("32-bit DMA consistent DMA enable failed\n"); - goto err_dma; + return ret; } /* @@ -203,25 +202,16 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); - ret = pci_request_region(pdev, 0, "ath9k"); + ret = pcim_iomap_regions(pdev, BIT(0), "ath9k"); if (ret) { dev_err(&pdev->dev, "PCI memory region reserve error\n"); - ret = -ENODEV; - goto err_region; - } - - mem = pci_iomap(pdev, 0, 0); - if (!mem) { - pr_err("PCI memory map error\n") ; - ret = -EIO; - goto err_iomap; + return -ENODEV; } hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (!hw) { dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); - ret = -ENOMEM; - goto err_alloc_hw; + return -ENOMEM; } SET_IEEE80211_DEV(hw, &pdev->dev); @@ -230,7 +220,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc = hw->priv; sc->hw = hw; sc->dev = &pdev->dev; - sc->mem = mem; + sc->mem = pcim_iomap_table(pdev)[0]; /* Will be cleared in ath9k_start() */ set_bit(SC_OP_INVALID, &sc->sc_flags); @@ -251,7 +241,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name)); wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n", - hw_name, (unsigned long)mem, pdev->irq); + hw_name, (unsigned long)sc->mem, pdev->irq); return 0; @@ -259,14 +249,6 @@ err_init: free_irq(sc->irq, sc); err_irq: ieee80211_free_hw(hw); -err_alloc_hw: - pci_iounmap(pdev, mem); -err_iomap: - pci_release_region(pdev, 0); -err_region: - /* Nothing */ -err_dma: - pci_disable_device(pdev); return ret; } @@ -274,17 +256,12 @@ static void ath_pci_remove(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_softc *sc = hw->priv; - void __iomem *mem = sc->mem; if (!is_ath9k_unloaded) sc->sc_ah->ah_flags |= AH_UNPLUGGED; ath9k_deinit_device(sc); free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); - - pci_iounmap(pdev, mem); - pci_disable_device(pdev); - pci_release_region(pdev, 0); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d4df98a938bf..3d236aebf588 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -180,11 +180,6 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc) bf->bf_mpdu = NULL; } } - - INIT_LIST_HEAD(&sc->rx.rxbuf); - - kfree(sc->rx.rx_bufptr); - sc->rx.rx_bufptr = NULL; } static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size) @@ -211,12 +206,11 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) ah->caps.rx_hp_qdepth); size = sizeof(struct ath_buf) * nbufs; - bf = kzalloc(size, GFP_KERNEL); + bf = devm_kzalloc(sc->dev, size, GFP_KERNEL); if (!bf) return -ENOMEM; INIT_LIST_HEAD(&sc->rx.rxbuf); - sc->rx.rx_bufptr = bf; for (i = 0; i < nbufs; i++, bf++) { skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_KERNEL); @@ -363,9 +357,6 @@ void ath_rx_cleanup(struct ath_softc *sc) bf->bf_mpdu = NULL; } } - - if (sc->rx.rxdma.dd_desc_len != 0) - ath_descdma_cleanup(sc, &sc->rx.rxdma, &sc->rx.rxbuf); } } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 90e48a0fafe5..ca4a0341294f 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2361,8 +2361,8 @@ static int ath_txstatus_setup(struct ath_softc *sc, int size) u8 txs_len = sc->sc_ah->caps.txs_len; dd->dd_desc_len = size * txs_len; - dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, - &dd->dd_desc_paddr, GFP_KERNEL); + dd->dd_desc = dmam_alloc_coherent(sc->dev, dd->dd_desc_len, + &dd->dd_desc_paddr, GFP_KERNEL); if (!dd->dd_desc) return -ENOMEM; @@ -2382,14 +2382,6 @@ static int ath_tx_edma_init(struct ath_softc *sc) return err; } -static void ath_tx_edma_cleanup(struct ath_softc *sc) -{ - struct ath_descdma *dd = &sc->txsdma; - - dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, - dd->dd_desc_paddr); -} - int ath_tx_init(struct ath_softc *sc, int nbufs) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -2402,7 +2394,7 @@ int ath_tx_init(struct ath_softc *sc, int nbufs) if (error != 0) { ath_err(common, "Failed to allocate tx descriptors: %d\n", error); - goto err; + return error; } error = ath_descdma_setup(sc, &sc->beacon.bdma, &sc->beacon.bbuf, @@ -2410,36 +2402,17 @@ int ath_tx_init(struct ath_softc *sc, int nbufs) if (error != 0) { ath_err(common, "Failed to allocate beacon descriptors: %d\n", error); - goto err; + return error; } INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work); - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) error = ath_tx_edma_init(sc); - if (error) - goto err; - } - -err: - if (error != 0) - ath_tx_cleanup(sc); return error; } -void ath_tx_cleanup(struct ath_softc *sc) -{ - if (sc->beacon.bdma.dd_desc_len != 0) - ath_descdma_cleanup(sc, &sc->beacon.bdma, &sc->beacon.bbuf); - - if (sc->tx.txdma.dd_desc_len != 0) - ath_descdma_cleanup(sc, &sc->tx.txdma, &sc->tx.txbuf); - - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) - ath_tx_edma_cleanup(sc); -} - void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) { struct ath_atx_tid *tid; From c1b976d2fcb3b308906be7c6bda1390251dec32d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 12 Dec 2012 13:14:23 +0100 Subject: [PATCH 185/478] ath9k_hw: use the devres API for allocations Signed-off-by: Felix Fietkau Acked-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 50 +++++++-------------- drivers/net/wireless/ath/ath9k/ar9002_hw.c | 11 +++-- drivers/net/wireless/ath/ath9k/ar9002_phy.c | 2 - drivers/net/wireless/ath/ath9k/hw-ops.h | 16 ------- drivers/net/wireless/ath/ath9k/hw.c | 28 +++++------- drivers/net/wireless/ath/ath9k/hw.h | 9 ++-- drivers/net/wireless/ath/ath9k/init.c | 1 + 7 files changed, 37 insertions(+), 80 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 874186bfda41..fd69376ecc83 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -470,16 +470,15 @@ static void ar5008_hw_spur_mitigate(struct ath_hw *ah, static int ar5008_hw_rf_alloc_ext_banks(struct ath_hw *ah) { #define ATH_ALLOC_BANK(bank, size) do { \ - bank = kzalloc((sizeof(u32) * size), GFP_KERNEL); \ - if (!bank) { \ - ath_err(common, "Cannot allocate RF banks\n"); \ - return -ENOMEM; \ - } \ + bank = devm_kzalloc(ah->dev, sizeof(u32) * size, GFP_KERNEL); \ + if (!bank) \ + goto error; \ } while (0); struct ath_common *common = ath9k_hw_common(ah); - BUG_ON(AR_SREV_9280_20_OR_LATER(ah)); + if (AR_SREV_9280_20_OR_LATER(ah)) + return 0; ATH_ALLOC_BANK(ah->analogBank0Data, ah->iniBank0.ia_rows); ATH_ALLOC_BANK(ah->analogBank1Data, ah->iniBank1.ia_rows); @@ -492,35 +491,12 @@ static int ar5008_hw_rf_alloc_ext_banks(struct ath_hw *ah) return 0; #undef ATH_ALLOC_BANK +error: + ath_err(common, "Cannot allocate RF banks\n"); + return -ENOMEM; } -/** - * ar5008_hw_rf_free_ext_banks - Free memory for analog bank scratch buffers - * @ah: atheros hardware struture - * For the external AR2133/AR5133 radios banks. - */ -static void ar5008_hw_rf_free_ext_banks(struct ath_hw *ah) -{ -#define ATH_FREE_BANK(bank) do { \ - kfree(bank); \ - bank = NULL; \ - } while (0); - - BUG_ON(AR_SREV_9280_20_OR_LATER(ah)); - - ATH_FREE_BANK(ah->analogBank0Data); - ATH_FREE_BANK(ah->analogBank1Data); - ATH_FREE_BANK(ah->analogBank2Data); - ATH_FREE_BANK(ah->analogBank3Data); - ATH_FREE_BANK(ah->analogBank6Data); - ATH_FREE_BANK(ah->analogBank6TPCData); - ATH_FREE_BANK(ah->analogBank7Data); - ATH_FREE_BANK(ah->bank6Temp); - -#undef ATH_FREE_BANK -} - /* * * ar5008_hw_set_rf_regs - programs rf registers based on EEPROM * @ah: atheros hardware structure @@ -1380,7 +1356,7 @@ static void ar5008_hw_set_radar_conf(struct ath_hw *ah) conf->radar_inband = 8; } -void ar5008_hw_attach_phy_ops(struct ath_hw *ah) +int ar5008_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); static const u32 ar5416_cca_regs[6] = { @@ -1391,12 +1367,15 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah) AR_PHY_CH1_EXT_CCA, AR_PHY_CH2_EXT_CCA }; + int ret; + + ret = ar5008_hw_rf_alloc_ext_banks(ah); + if (ret) + return ret; priv_ops->rf_set_freq = ar5008_hw_set_channel; priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate; - priv_ops->rf_alloc_ext_banks = ar5008_hw_rf_alloc_ext_banks; - priv_ops->rf_free_ext_banks = ar5008_hw_rf_free_ext_banks; priv_ops->set_rf_regs = ar5008_hw_set_rf_regs; priv_ops->set_channel_regs = ar5008_hw_set_channel_regs; priv_ops->init_bb = ar5008_hw_init_bb; @@ -1421,4 +1400,5 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah) ar5008_hw_set_nf_limits(ah); ar5008_hw_set_radar_conf(ah); memcpy(ah->nf_regs, ar5416_cca_regs, sizeof(ah->nf_regs)); + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 648da3e885e9..54da026f058c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -102,7 +102,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns; u32 *data; - data = kmalloc(size, GFP_KERNEL); + data = devm_kzalloc(ah->dev, size, GFP_KERNEL); if (!data) return; @@ -409,22 +409,27 @@ void ar9002_hw_enable_async_fifo(struct ath_hw *ah) } /* Sets up the AR5008/AR9001/AR9002 hardware familiy callbacks */ -void ar9002_hw_attach_ops(struct ath_hw *ah) +int ar9002_hw_attach_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); + int ret; priv_ops->init_mode_regs = ar9002_hw_init_mode_regs; priv_ops->init_mode_gain_regs = ar9002_hw_init_mode_gain_regs; ops->config_pci_powersave = ar9002_hw_configpcipowersave; - ar5008_hw_attach_phy_ops(ah); + ret = ar5008_hw_attach_phy_ops(ah); + if (ret) + return ret; + if (AR_SREV_9280_20_OR_LATER(ah)) ar9002_hw_attach_phy_ops(ah); ar9002_hw_attach_calib_ops(ah); ar9002_hw_attach_mac_ops(ah); + return 0; } void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 846dd7974eb8..8b119811b2d9 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -561,8 +561,6 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah) struct ath_hw_ops *ops = ath9k_hw_ops(ah); priv_ops->set_rf_regs = NULL; - priv_ops->rf_alloc_ext_banks = NULL; - priv_ops->rf_free_ext_banks = NULL; priv_ops->rf_set_freq = ar9002_hw_set_channel; priv_ops->spur_mitigate_freq = ar9002_hw_spur_mitigate; priv_ops->olc_init = ar9002_olc_init; diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 0f2b97f6b739..14b701140b49 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -101,22 +101,6 @@ static inline void ath9k_hw_spur_mitigate_freq(struct ath_hw *ah, ath9k_hw_private_ops(ah)->spur_mitigate_freq(ah, chan); } -static inline int ath9k_hw_rf_alloc_ext_banks(struct ath_hw *ah) -{ - if (!ath9k_hw_private_ops(ah)->rf_alloc_ext_banks) - return 0; - - return ath9k_hw_private_ops(ah)->rf_alloc_ext_banks(ah); -} - -static inline void ath9k_hw_rf_free_ext_banks(struct ath_hw *ah) -{ - if (!ath9k_hw_private_ops(ah)->rf_free_ext_banks) - return; - - ath9k_hw_private_ops(ah)->rf_free_ext_banks(ah); -} - static inline bool ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, u16 modesIndex) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 7cb787065913..ecf1c08a03c1 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -554,14 +554,6 @@ static int ath9k_hw_post_init(struct ath_hw *ah) ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); - ecode = ath9k_hw_rf_alloc_ext_banks(ah); - if (ecode) { - ath_err(ath9k_hw_common(ah), - "Failed allocating banks for external radio\n"); - ath9k_hw_rf_free_ext_banks(ah); - return ecode; - } - if (ah->config.enable_ani) { ath9k_hw_ani_setup(ah); ath9k_hw_ani_init(ah); @@ -570,12 +562,13 @@ static int ath9k_hw_post_init(struct ath_hw *ah) return 0; } -static void ath9k_hw_attach_ops(struct ath_hw *ah) +static int ath9k_hw_attach_ops(struct ath_hw *ah) { - if (AR_SREV_9300_20_OR_LATER(ah)) - ar9003_hw_attach_ops(ah); - else - ar9002_hw_attach_ops(ah); + if (!AR_SREV_9300_20_OR_LATER(ah)) + return ar9002_hw_attach_ops(ah); + + ar9003_hw_attach_ops(ah); + return 0; } /* Called for all hardware families */ @@ -611,7 +604,9 @@ static int __ath9k_hw_init(struct ath_hw *ah) ath9k_hw_init_defaults(ah); ath9k_hw_init_config(ah); - ath9k_hw_attach_ops(ah); + r = ath9k_hw_attach_ops(ah); + if (r) + return r; if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) { ath_err(common, "Couldn't wakeup chip\n"); @@ -1153,12 +1148,9 @@ void ath9k_hw_deinit(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); if (common->state < ATH_HW_INITIALIZED) - goto free_hw; + return; ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); - -free_hw: - ath9k_hw_rf_free_ext_banks(ah); } EXPORT_SYMBOL(ath9k_hw_deinit); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 7f1a8e91c908..d2b5229474b6 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -604,8 +604,6 @@ struct ath_hw_radar_conf { * * @rf_set_freq: change frequency * @spur_mitigate_freq: spur mitigation - * @rf_alloc_ext_banks: - * @rf_free_ext_banks: * @set_rf_regs: * @compute_pll_control: compute the PLL control value to use for * AR_RTC_PLL_CONTROL for a given channel @@ -630,8 +628,6 @@ struct ath_hw_private_ops { struct ath9k_channel *chan); void (*spur_mitigate_freq)(struct ath_hw *ah, struct ath9k_channel *chan); - int (*rf_alloc_ext_banks)(struct ath_hw *ah); - void (*rf_free_ext_banks)(struct ath_hw *ah); bool (*set_rf_regs)(struct ath_hw *ah, struct ath9k_channel *chan, u16 modesIndex); @@ -710,6 +706,7 @@ enum ath_cal_list { struct ath_hw { struct ath_ops reg_ops; + struct device *dev; struct ieee80211_hw *hw; struct ath_common common; struct ath9k_hw_version hw_version; @@ -1068,14 +1065,14 @@ bool ar9003_paprd_is_done(struct ath_hw *ah); bool ar9003_is_paprd_enabled(struct ath_hw *ah); /* Hardware family op attach helpers */ -void ar5008_hw_attach_phy_ops(struct ath_hw *ah); +int ar5008_hw_attach_phy_ops(struct ath_hw *ah); void ar9002_hw_attach_phy_ops(struct ath_hw *ah); void ar9003_hw_attach_phy_ops(struct ath_hw *ah); void ar9002_hw_attach_calib_ops(struct ath_hw *ah); void ar9003_hw_attach_calib_ops(struct ath_hw *ah); -void ar9002_hw_attach_ops(struct ath_hw *ah); +int ar9002_hw_attach_ops(struct ath_hw *ah); void ar9003_hw_attach_ops(struct ath_hw *ah); void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 0f00a1306ac0..5c01f43c32b0 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -559,6 +559,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, if (!ah) return -ENOMEM; + ah->dev = sc->dev; ah->hw = sc->hw; ah->hw_version.devid = devid; ah->reg_ops.read = ath9k_ioread32; From d1f3de71a584a82ee4e84505fce9acccf40215ff Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 15 Dec 2012 23:18:06 +0100 Subject: [PATCH 186/478] carl9170: advertise support for TDLS Based on a quick test [ath9k and carl9170], TDLS seemed to be working fine. And while we are at it, let's move the wiphy feature flag set from carl9170_alloc into a single place in carl9170_fw. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/fw.c | 6 +++++- drivers/net/wireless/ath/carl9170/main.c | 4 ---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index aaebecd19e59..5a8a9f891dd8 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -354,7 +354,11 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) ar->hw->wiphy->interface_modes |= if_comb_types; - ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + ar->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + /* As IBSS Encryption is software-based, IBSS RSN is supported. */ + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_SUPPORTS_TDLS; #undef SUPPORTED return carl9170_fw_tx_sequence(ar); diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 9d2051aeb782..ce8ae1e6bd3b 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1807,10 +1807,6 @@ void *carl9170_alloc(size_t priv_size) for (i = 0; i < ARRAY_SIZE(ar->noise); i++) ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */ - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - /* As IBSS Encryption is software-based, IBSS RSN is supported. */ - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; return ar; err_nomem: From 5ae994d0984ddc2f1e79c4f8de7189faecfc0af9 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 15 Dec 2012 23:21:32 +0100 Subject: [PATCH 187/478] carl9170: import 1.9.7 firmware headers Import new headers from my firmware branch: visit our wiki at: Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/fwcmd.h | 8 ++++++++ drivers/net/wireless/ath/carl9170/hw.h | 2 +- drivers/net/wireless/ath/carl9170/version.h | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h index 9443c802b25b..9111d4ffc1b3 100644 --- a/drivers/net/wireless/ath/carl9170/fwcmd.h +++ b/drivers/net/wireless/ath/carl9170/fwcmd.h @@ -156,6 +156,14 @@ struct carl9170_psm { } __packed; #define CARL9170_PSM_SIZE 4 +/* + * Note: If a bit in rx_filter is set, then it + * means that the particular frames which matches + * the condition are FILTERED/REMOVED/DISCARDED! + * (This is can be a bit confusing, especially + * because someone people think it's the exact + * opposite way, so watch out!) + */ struct carl9170_rx_filter_cmd { __le32 rx_filter; } __packed; diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h index fa834c1460f0..0db874abde50 100644 --- a/drivers/net/wireless/ath/carl9170/hw.h +++ b/drivers/net/wireless/ath/carl9170/hw.h @@ -384,7 +384,7 @@ #define AR9170_MAC_REG_BCN_ADDR (AR9170_MAC_REG_BASE + 0xd84) #define AR9170_MAC_REG_BCN_LENGTH (AR9170_MAC_REG_BASE + 0xd88) -#define AR9170_MAC_BCN_LENGTH_MAX 256 +#define AR9170_MAC_BCN_LENGTH_MAX (512 - 32) #define AR9170_MAC_REG_BCN_STATUS (AR9170_MAC_REG_BASE + 0xd8c) diff --git a/drivers/net/wireless/ath/carl9170/version.h b/drivers/net/wireless/ath/carl9170/version.h index 2ec3e9191e4d..2282847d4bb8 100644 --- a/drivers/net/wireless/ath/carl9170/version.h +++ b/drivers/net/wireless/ath/carl9170/version.h @@ -1,7 +1,7 @@ #ifndef __CARL9170_SHARED_VERSION_H #define __CARL9170_SHARED_VERSION_H #define CARL9170FW_VERSION_YEAR 12 -#define CARL9170FW_VERSION_MONTH 7 -#define CARL9170FW_VERSION_DAY 7 -#define CARL9170FW_VERSION_GIT "1.9.6" +#define CARL9170FW_VERSION_MONTH 12 +#define CARL9170FW_VERSION_DAY 15 +#define CARL9170FW_VERSION_GIT "1.9.7" #endif /* __CARL9170_SHARED_VERSION_H */ From 7f878b0d96e6b3bd27d736fb2fb7e3cc94b16b26 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sun, 16 Dec 2012 01:41:37 +0100 Subject: [PATCH 188/478] carl9170: allow P2P_GO interface creation after P2P_CLIENT Janusz Dziedzic reported that after a change in wpa_supplicant ["nl80211: Automatically use concurrent P2P if possible"], carl9170 was no longer able to host a P2P network. This patch tackles the problem by allowing GO interfaces to be registered, long after the P2P_CLIENT interface is brought up. Reported-by: Janusz Dziedzic Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/main.c | 54 ++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index ce8ae1e6bd3b..95e4bf05e559 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -580,7 +580,7 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct carl9170_vif_info *vif_priv = (void *) vif->drv_priv; - struct ieee80211_vif *main_vif; + struct ieee80211_vif *main_vif, *old_main = NULL; struct ar9170 *ar = hw->priv; int vif_id = -1, err = 0; @@ -602,6 +602,15 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw, goto init; } + /* Because the AR9170 HW's MAC doesn't provide full support for + * multiple, independent interfaces [of different operation modes]. + * We have to select ONE main interface [main mode of HW], but we + * can have multiple slaves [AKA: entry in the ACK-table]. + * + * The first (from HEAD/TOP) interface in the ar->vif_list is + * always the main intf. All following intfs in this list + * are considered to be slave intfs. + */ main_vif = carl9170_get_main_vif(ar); if (main_vif) { @@ -610,6 +619,18 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw, if (vif->type == NL80211_IFTYPE_STATION) break; + /* P2P GO [master] use-case + * Because the P2P GO station is selected dynamically + * by all participating peers of a WIFI Direct network, + * the driver has be able to change the main interface + * operating mode on the fly. + */ + if (main_vif->p2p && vif->p2p && + vif->type == NL80211_IFTYPE_AP) { + old_main = main_vif; + break; + } + err = -EBUSY; rcu_read_unlock(); @@ -648,14 +669,41 @@ static int carl9170_op_add_interface(struct ieee80211_hw *hw, vif_priv->id = vif_id; vif_priv->enable_beacon = false; ar->vifs++; - list_add_tail_rcu(&vif_priv->list, &ar->vif_list); + if (old_main) { + /* We end up in here, if the main interface is being replaced. + * Put the new main interface at the HEAD of the list and the + * previous inteface will automatically become second in line. + */ + list_add_rcu(&vif_priv->list, &ar->vif_list); + } else { + /* Add new inteface. If the list is empty, it will become the + * main inteface, otherwise it will be slave. + */ + list_add_tail_rcu(&vif_priv->list, &ar->vif_list); + } rcu_assign_pointer(ar->vif_priv[vif_id].vif, vif); init: - if (carl9170_get_main_vif(ar) == vif) { + main_vif = carl9170_get_main_vif(ar); + + if (main_vif == vif) { rcu_assign_pointer(ar->beacon_iter, vif_priv); rcu_read_unlock(); + if (old_main) { + struct carl9170_vif_info *old_main_priv = + (void *) old_main->drv_priv; + /* downgrade old main intf to slave intf. + * NOTE: We are no longer under rcu_read_lock. + * But we are still holding ar->mutex, so the + * vif data [id, addr] is safe. + */ + err = carl9170_mod_virtual_mac(ar, old_main_priv->id, + old_main->addr); + if (err) + goto unlock; + } + err = carl9170_init_interface(ar, vif); if (err) goto unlock; From 17f658ac8c7e0af0c7aa12587477f9bbc093229e Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 17 Dec 2012 14:54:19 +0100 Subject: [PATCH 189/478] carl9170: Only specify interface combinations if more than one interface is possible Otherwise carl9170 triggers a warning in cfg80211, from net/wireless/core.c /* Combinations with just one interface aren't real */ if (WARN_ON(c->max_interfaces < 2)) Note: The number of supported interfaces is set by the carl9170 firmware. The default number of supported interfaces for all current firmwares is 2. Therefore this warning can only be observed with custom firmwares. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/fw.c | 29 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index 5a8a9f891dd8..401a080a30f1 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -215,6 +215,24 @@ static int carl9170_fw_tx_sequence(struct ar9170 *ar) return 0; } +static void carl9170_fw_set_if_combinations(struct ar9170 *ar, + u16 if_comb_types) +{ + if (ar->fw.vif_num < 2) + return; + + ar->if_comb_limits[0].max = ar->fw.vif_num; + ar->if_comb_limits[0].types = if_comb_types; + + ar->if_combs[0].num_different_channels = 1; + ar->if_combs[0].max_interfaces = ar->fw.vif_num; + ar->if_combs[0].limits = ar->if_comb_limits; + ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits); + + ar->hw->wiphy->iface_combinations = ar->if_combs; + ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs); +} + static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) { const struct carl9170fw_otus_desc *otus_desc; @@ -341,16 +359,7 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) } } - ar->if_comb_limits[0].max = ar->fw.vif_num; - ar->if_comb_limits[0].types = if_comb_types; - - ar->if_combs[0].num_different_channels = 1; - ar->if_combs[0].max_interfaces = ar->fw.vif_num; - ar->if_combs[0].limits = ar->if_comb_limits; - ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits); - - ar->hw->wiphy->iface_combinations = ar->if_combs; - ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs); + carl9170_fw_set_if_combinations(ar, if_comb_types); ar->hw->wiphy->interface_modes |= if_comb_types; From 7a5c7307736cdc26146581aed24cc46a4c7e6f35 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 17 Dec 2012 16:30:05 +0100 Subject: [PATCH 190/478] carl9170: don't enable hw crypto offload, if the fw doesn't support it Previously, op_start would set disable_offload always to false, even if it was set to true by the fw parser. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/carl9170.h | 1 + drivers/net/wireless/ath/carl9170/fw.c | 2 +- drivers/net/wireless/ath/carl9170/main.c | 33 +++++++++++++------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 2df17f1e49ef..c77874c4f4ca 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -290,6 +290,7 @@ struct ar9170 { unsigned int rx_size; unsigned int tx_seq_table; bool ba_filter; + bool disable_offload_fw; } fw; /* interface configuration combinations */ diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index 401a080a30f1..6e76179f1a79 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -282,7 +282,7 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) if (!SUPP(CARL9170FW_COMMAND_CAM)) { dev_info(&ar->udev->dev, "crypto offloading is disabled " "by firmware.\n"); - ar->disable_offload = true; + ar->fw.disable_offload_fw = true; } if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM)) diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 95e4bf05e559..6ca5dd068275 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -358,8 +358,13 @@ static int carl9170_op_start(struct ieee80211_hw *hw) ar->ps.last_action = jiffies; ar->ps.last_slept = jiffies; ar->erp_mode = CARL9170_ERP_AUTO; - ar->rx_software_decryption = false; - ar->disable_offload = false; + + /* Set "disable hw crypto offload" whenever the module parameter + * nohwcrypt is true or if the firmware does not support it. + */ + ar->disable_offload = modparam_nohwcrypt | + ar->fw.disable_offload_fw; + ar->rx_software_decryption = ar->disable_offload; for (i = 0; i < ar->hw->queues; i++) { ar->queue_stop_timeout[i] = jiffies; @@ -565,12 +570,20 @@ static int carl9170_init_interface(struct ar9170 *ar, memcpy(common->macaddr, vif->addr, ETH_ALEN); - if (modparam_nohwcrypt || - ((vif->type != NL80211_IFTYPE_STATION) && - (vif->type != NL80211_IFTYPE_AP))) { - ar->rx_software_decryption = true; - ar->disable_offload = true; - } + /* We have to fall back to software crypto, whenever + * the user choose to participates in an IBSS. HW + * offload for IBSS RSN is not supported by this driver. + * + * NOTE: If the previous main interface has already + * disabled hw crypto offload, we have to keep this + * previous disable_offload setting as it was. + * Altough ideally, we should notify mac80211 and tell + * it to forget about any HW crypto offload for now. + */ + ar->disable_offload |= ((vif->type != NL80211_IFTYPE_STATION) && + (vif->type != NL80211_IFTYPE_AP)); + + ar->rx_software_decryption = ar->disable_offload; err = carl9170_set_operating_mode(ar); return err; @@ -1160,9 +1173,7 @@ static int carl9170_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (ar->disable_offload || !vif) return -EOPNOTSUPP; - /* - * We have to fall back to software encryption, whenever - * the user choose to participates in an IBSS or is connected + /* Fall back to software encryption whenever the driver is connected * to more than one network. * * This is very unfortunate, because some machines cannot handle From 55fa645e3a3f4c0b5da744eb0e1fec4e51dfc75e Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 17 Dec 2012 17:05:55 +0100 Subject: [PATCH 191/478] carl9170: disable hw crypto for p2p networks While the driver supports HW offload in a single P2P client configuration, it doesn't support HW offload in the concurrent P2P GO+CLIENT configuration. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 6ca5dd068275..0abf005aaa4e 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -583,6 +583,14 @@ static int carl9170_init_interface(struct ar9170 *ar, ar->disable_offload |= ((vif->type != NL80211_IFTYPE_STATION) && (vif->type != NL80211_IFTYPE_AP)); + /* While the driver supports HW offload in a single + * P2P client configuration, it doesn't support HW + * offload in the favourit, concurrent P2P GO+CLIENT + * configuration. Hence, HW offload will always be + * disabled for P2P. + */ + ar->disable_offload |= vif->p2p; + ar->rx_software_decryption = ar->disable_offload; err = carl9170_set_operating_mode(ar); From 558925f3135731d67c5e65436ecff1a4b1c9450f Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 20 Dec 2012 02:44:29 +0100 Subject: [PATCH 192/478] carl9170: remove custom NUM_TID and friends The commit: "mac80211: introduce IEEE80211_NUM_TIDS and use it" introduced a generic NUM_TID definitions for all everyone. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/carl9170.h | 18 ++++++------------ drivers/net/wireless/ath/carl9170/main.c | 4 ++-- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index c77874c4f4ca..25599741cd8a 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -85,20 +85,14 @@ enum carl9170_device_state { CARL9170_STARTED, }; -#define CARL9170_NUM_TID 16 #define WME_BA_BMP_SIZE 64 #define CARL9170_TX_USER_RATE_TRIES 3 -#define WME_AC_BE 2 -#define WME_AC_BK 3 -#define WME_AC_VI 1 -#define WME_AC_VO 0 - #define TID_TO_WME_AC(_tid) \ - ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ - (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \ - (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ - WME_AC_VO) + ((((_tid) == 0) || ((_tid) == 3)) ? IEEE80211_AC_BE : \ + (((_tid) == 1) || ((_tid) == 2)) ? IEEE80211_AC_BK : \ + (((_tid) == 4) || ((_tid) == 5)) ? IEEE80211_AC_VI : \ + IEEE80211_AC_VO) #define SEQ_DIFF(_start, _seq) \ (((_start) - (_seq)) & 0x0fff) @@ -494,8 +488,8 @@ struct carl9170_sta_info { bool sleeping; atomic_t pending_frames; unsigned int ampdu_max_len; - struct carl9170_sta_tid __rcu *agg[CARL9170_NUM_TID]; - struct carl9170_ba_stats stats[CARL9170_NUM_TID]; + struct carl9170_sta_tid __rcu *agg[IEEE80211_NUM_TIDS]; + struct carl9170_ba_stats stats[IEEE80211_NUM_TIDS]; }; struct carl9170_tx_info { diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 0abf005aaa4e..f22da264fd93 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1330,7 +1330,7 @@ static int carl9170_op_sta_add(struct ieee80211_hw *hw, return 0; } - for (i = 0; i < CARL9170_NUM_TID; i++) + for (i = 0; i < ARRAY_SIZE(sta_info->agg); i++) RCU_INIT_POINTER(sta_info->agg[i], NULL); sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor); @@ -1354,7 +1354,7 @@ static int carl9170_op_sta_remove(struct ieee80211_hw *hw, sta_info->ht_sta = false; rcu_read_lock(); - for (i = 0; i < CARL9170_NUM_TID; i++) { + for (i = 0; i < ARRAY_SIZE(sta_info->agg); i++) { struct carl9170_sta_tid *tid_info; tid_info = rcu_dereference(sta_info->agg[i]); From 1f1d9654e183c31df4d168591165beed869ea098 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 22 Dec 2012 15:05:26 +0100 Subject: [PATCH 193/478] carl9170: refactor carl9170_update_beacon This patch moves parts of carl9170_update_beacon into separate subroutines, so the parts become more manageable. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/carl9170/tx.c | 135 +++++++++++++++---------- 1 file changed, 81 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index ef4ec0da6e49..9c0b150d5b8e 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -1520,36 +1520,93 @@ void carl9170_tx_scheduler(struct ar9170 *ar) carl9170_tx(ar); } +/* caller has to take rcu_read_lock */ +static struct carl9170_vif_info *carl9170_pick_beaconing_vif(struct ar9170 *ar) +{ + struct carl9170_vif_info *cvif; + int i = 1; + + /* The AR9170 hardware has no fancy beacon queue or some + * other scheduling mechanism. So, the driver has to make + * due by setting the two beacon timers (pretbtt and tbtt) + * once and then swapping the beacon address in the HW's + * register file each time the pretbtt fires. + */ + + cvif = rcu_dereference(ar->beacon_iter); + if (ar->vifs > 0 && cvif) { + do { + list_for_each_entry_continue_rcu(cvif, &ar->vif_list, + list) { + if (cvif->active && cvif->enable_beacon) + goto out; + } + } while (ar->beacon_enabled && i--); + } + +out: + rcu_assign_pointer(ar->beacon_iter, cvif); + return cvif; +} + +static bool carl9170_tx_beacon_physet(struct ar9170 *ar, struct sk_buff *skb, + u32 *ht1, u32 *plcp) +{ + struct ieee80211_tx_info *txinfo; + struct ieee80211_tx_rate *rate; + unsigned int power, chains; + bool ht_rate; + + txinfo = IEEE80211_SKB_CB(skb); + rate = &txinfo->control.rates[0]; + ht_rate = !!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS); + carl9170_tx_rate_tpc_chains(ar, txinfo, rate, plcp, &power, &chains); + + *ht1 = AR9170_MAC_BCN_HT1_TX_ANT0; + if (chains == AR9170_TX_PHY_TXCHAIN_2) + *ht1 |= AR9170_MAC_BCN_HT1_TX_ANT1; + SET_VAL(AR9170_MAC_BCN_HT1_PWR_CTRL, *ht1, 7); + SET_VAL(AR9170_MAC_BCN_HT1_TPC, *ht1, power); + SET_VAL(AR9170_MAC_BCN_HT1_CHAIN_MASK, *ht1, chains); + + if (ht_rate) { + *ht1 |= AR9170_MAC_BCN_HT1_HT_EN; + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + *plcp |= AR9170_MAC_BCN_HT2_SGI; + + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + *ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED; + *plcp |= AR9170_MAC_BCN_HT2_BW40; + } else if (rate->flags & IEEE80211_TX_RC_DUP_DATA) { + *ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_DUP; + *plcp |= AR9170_MAC_BCN_HT2_BW40; + } + + SET_VAL(AR9170_MAC_BCN_HT2_LEN, *plcp, skb->len + FCS_LEN); + } else { + if (*plcp <= AR9170_TX_PHY_RATE_CCK_11M) + *plcp |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400; + else + *plcp |= ((skb->len + FCS_LEN) << 16) + 0x0010; + } + + return ht_rate; +} + int carl9170_update_beacon(struct ar9170 *ar, const bool submit) { struct sk_buff *skb = NULL; struct carl9170_vif_info *cvif; - struct ieee80211_tx_info *txinfo; - struct ieee80211_tx_rate *rate; __le32 *data, *old = NULL; - unsigned int plcp, power, chains; - u32 word, ht1, off, addr, len; + u32 word, ht1, plcp, off, addr, len; int i = 0, err = 0; + bool ht_rate; rcu_read_lock(); - cvif = rcu_dereference(ar->beacon_iter); -retry: - if (ar->vifs == 0 || !cvif) + cvif = carl9170_pick_beaconing_vif(ar); + if (!cvif) goto out_unlock; - list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) { - if (cvif->active && cvif->enable_beacon) - goto found; - } - - if (!ar->beacon_enabled || i++) - goto out_unlock; - - goto retry; - -found: - rcu_assign_pointer(ar->beacon_iter, cvif); - skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif), NULL, NULL); @@ -1558,7 +1615,6 @@ found: goto err_free; } - txinfo = IEEE80211_SKB_CB(skb); spin_lock_bh(&ar->beacon_lock); data = (__le32 *)skb->data; if (cvif->beacon) @@ -1588,43 +1644,14 @@ found: goto err_unlock; } - ht1 = AR9170_MAC_BCN_HT1_TX_ANT0; - rate = &txinfo->control.rates[0]; - carl9170_tx_rate_tpc_chains(ar, txinfo, rate, &plcp, &power, &chains); - if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) { - if (plcp <= AR9170_TX_PHY_RATE_CCK_11M) - plcp |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400; - else - plcp |= ((skb->len + FCS_LEN) << 16) + 0x0010; - } else { - ht1 |= AR9170_MAC_BCN_HT1_HT_EN; - if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - plcp |= AR9170_MAC_BCN_HT2_SGI; - - if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { - ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED; - plcp |= AR9170_MAC_BCN_HT2_BW40; - } - if (rate->flags & IEEE80211_TX_RC_DUP_DATA) { - ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_DUP; - plcp |= AR9170_MAC_BCN_HT2_BW40; - } - - SET_VAL(AR9170_MAC_BCN_HT2_LEN, plcp, skb->len + FCS_LEN); - } - - SET_VAL(AR9170_MAC_BCN_HT1_PWR_CTRL, ht1, 7); - SET_VAL(AR9170_MAC_BCN_HT1_TPC, ht1, power); - SET_VAL(AR9170_MAC_BCN_HT1_CHAIN_MASK, ht1, chains); - if (chains == AR9170_TX_PHY_TXCHAIN_2) - ht1 |= AR9170_MAC_BCN_HT1_TX_ANT1; + ht_rate = carl9170_tx_beacon_physet(ar, skb, &ht1, &plcp); carl9170_async_regwrite_begin(ar); carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT1, ht1); - if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) - carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, plcp); - else + if (ht_rate) carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT2, plcp); + else + carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, plcp); for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) { /* From 93476b1e685036e8513be8df34ca442096af6b4c Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 17 Dec 2012 22:06:08 +0100 Subject: [PATCH 194/478] ath9k: fix column header comments for some initval arrays Some 3-column initval arrays have wrong comments. The column of these arrays is indexed by the 'freqIndex' variable in 'ar5008_hw_process_ini' which only depends on the actual band. The 'initvals' tool from 'qca-swiss-army-knife' prints the correct comment lines for these arrays, since commit 'atheros-initvals: fix comments for non-fastclock 3-column tables' however the comments were not refreshed in ath9k. The patch contains no functional changes. Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar5008_initvals.h | 8 ++++---- drivers/net/wireless/ath/ath9k/ar9001_initvals.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar5008_initvals.h b/drivers/net/wireless/ath/ath9k/ar5008_initvals.h index f81e7fc60a36..467ccfae2cee 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar5008_initvals.h @@ -466,7 +466,7 @@ static const u32 ar5416Bank0[][2] = { }; static const u32 ar5416BB_RfGain[][3] = { - /* Addr 5G_HT20 5G_HT40 */ + /* Addr 5G 2G */ {0x00009a00, 0x00000000, 0x00000000}, {0x00009a04, 0x00000040, 0x00000040}, {0x00009a08, 0x00000080, 0x00000080}, @@ -546,12 +546,12 @@ static const u32 ar5416Bank2[][2] = { }; static const u32 ar5416Bank3[][3] = { - /* Addr 5G_HT20 5G_HT40 */ + /* Addr 5G 2G */ {0x000098f0, 0x01400018, 0x01c00018}, }; static const u32 ar5416Bank6[][3] = { - /* Addr 5G_HT20 5G_HT40 */ + /* Addr 5G 2G */ {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, @@ -588,7 +588,7 @@ static const u32 ar5416Bank6[][3] = { }; static const u32 ar5416Bank6TPC[][3] = { - /* Addr 5G_HT20 5G_HT40 */ + /* Addr 5G 2G */ {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9001_initvals.h b/drivers/net/wireless/ath/ath9k/ar9001_initvals.h index ea4a230997ac..59524e1d4678 100644 --- a/drivers/net/wireless/ath/ath9k/ar9001_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9001_initvals.h @@ -460,7 +460,7 @@ static const u32 ar5416Common_9100[][2] = { }; static const u32 ar5416Bank6_9100[][3] = { - /* Addr 5G_HT20 5G_HT40 */ + /* Addr 5G 2G */ {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, @@ -497,7 +497,7 @@ static const u32 ar5416Bank6_9100[][3] = { }; static const u32 ar5416Bank6TPC_9100[][3] = { - /* Addr 5G_HT20 5G_HT40 */ + /* Addr 5G 2G */ {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, {0x0000989c, 0x00000000, 0x00000000}, From 70277f47b58b174a6b0b891dcd06ae5125afb73b Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 20 Dec 2012 14:31:51 +0100 Subject: [PATCH 195/478] iwlegacy: add flush callback Dump implementation of flush, which just wait until all TX queues become empty. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/3945-mac.c | 1 + drivers/net/wireless/iwlegacy/4965-mac.c | 1 + drivers/net/wireless/iwlegacy/common.c | 36 ++++++++++++++++++++++++ drivers/net/wireless/iwlegacy/common.h | 1 + 4 files changed, 39 insertions(+) diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index d604b4036a76..962400a20161 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3474,6 +3474,7 @@ struct ieee80211_ops il3945_mac_ops = { .sta_add = il3945_mac_sta_add, .sta_remove = il_mac_sta_remove, .tx_last_beacon = il_mac_tx_last_beacon, + .flush = il_mac_flush, }; static int diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 6a86ed45835d..c40020c8b273 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -6308,6 +6308,7 @@ const struct ieee80211_ops il4965_mac_ops = { .sta_remove = il_mac_sta_remove, .channel_switch = il4965_mac_channel_switch, .tx_last_beacon = il_mac_tx_last_beacon, + .flush = il_mac_flush, }; static int diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index 7e16d10a7f14..56b80219d695 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -4707,6 +4707,42 @@ out: } EXPORT_SYMBOL(il_mac_change_interface); +void +il_mac_flush(struct ieee80211_hw *hw, bool drop) +{ + struct il_priv *il = hw->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + int i; + + mutex_lock(&il->mutex); + D_MAC80211("enter\n"); + + if (il->txq == NULL) + goto out; + + for (i = 0; i < il->hw_params.max_txq_num; i++) { + struct il_queue *q; + + if (i == il->cmd_queue) + continue; + + q = &il->txq[i].q; + if (q->read_ptr == q->write_ptr) + continue; + + if (time_after(jiffies, timeout)) { + IL_ERR("Failed to flush queue %d\n", q->id); + break; + } + + msleep(20); + } +out: + D_MAC80211("leave\n"); + mutex_unlock(&il->mutex); +} +EXPORT_SYMBOL(il_mac_flush); + /* * On every watchdog tick we check (latest) time stamp. If it does not * change during timeout period and queue is not empty we reset firmware. diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index a9a569f432fb..37fe553b25e0 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1723,6 +1723,7 @@ void il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p); +void il_mac_flush(struct ieee80211_hw *hw, bool drop); int il_alloc_txq_mem(struct il_priv *il); void il_free_txq_mem(struct il_priv *il); From 07db8f8fe9889a2db44d7692e5514a41d20e32c6 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 20 Dec 2012 14:31:53 +0100 Subject: [PATCH 196/478] iwlegacy: allow to enable PS Power save support was removed from iwlegacy due to possible firmware crashes problems it cause. I use to plan first inspect code to find reason of problems, fix them and then allow to enable PS. But realistically - code inspection will not happen, so let's do it, and wait for eventual bug reports. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/3945-mac.c | 5 ++++- drivers/net/wireless/iwlegacy/4965-mac.c | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index 962400a20161..3d76a3f595b7 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3549,7 +3549,8 @@ il3945_setup_mac(struct il_priv *il) hw->vif_data_size = sizeof(struct il_vif_priv); /* Tell mac80211 our characteristics */ - hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SPECTRUM_MGMT; + hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); @@ -3558,6 +3559,8 @@ il3945_setup_mac(struct il_priv *il) WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945; /* we create the 802.11 header and a zero-length SSID element */ hw->wiphy->max_scan_ie_len = IL3945_MAX_PROBE_REQUEST - 24 - 2; diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index c40020c8b273..10fc2493f415 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5712,8 +5712,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_NEED_DTIM_PERIOD | IEEE80211_HW_SPECTRUM_MGMT | - IEEE80211_HW_REPORTS_TX_ACK_STATUS; - + IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; if (il->cfg->sku & IL_SKU_N) hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | From c2397bb0c55495d8474c36143fdaced5c435a5e4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 22 Dec 2012 22:07:14 +0100 Subject: [PATCH 197/478] brcmsmac: initialize morepending in brcms_b_recv() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/net/wireless/brcm80211/brcmsmac/main.c: In function ‘brcms_b_recv’: drivers/net/wireless/brcm80211/brcmsmac/main.c:7636: warning: ‘morepending’ may be used uninitialized in this function Signed-off-by: Geert Uytterhoeven Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 17594de4199e..5f0f444f85a3 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -7633,7 +7633,7 @@ brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) uint n = 0; uint bound_limit = bound ? RXBND : -1; - bool morepending; + bool morepending = false; skb_queue_head_init(&recv_frames); From e1b97c9bc5902ec8b36247bcb7c3552895611485 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 26 Dec 2012 12:27:38 +0530 Subject: [PATCH 198/478] ath9k_hw: Remove AR9485 1.0 macro Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/reg.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index ad3c82c09177..0ac205e4cd04 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -884,9 +884,6 @@ #define AR_SREV_9485(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485)) -#define AR_SREV_9485_10(_ah) \ - (AR_SREV_9485(_ah) && \ - ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_10)) #define AR_SREV_9485_11(_ah) \ (AR_SREV_9485(_ah) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_11)) From a02308e931ad0bba19803779bec491c4b2d67b47 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sat, 29 Dec 2012 14:51:51 +0100 Subject: [PATCH 199/478] rt2x00: rt2800: convert read_eeprom functions to return an int value Both the rtt2x00usb_eeprom_read and the ioremap functions are allowed to fail, however their return values are not checked in the read_eeprom functions in the rt2800{pci,usb} drivers. The patch adds the missing checks, and converts all read_eeprom functions to return an int value, so the error values can be propagated up to the 'rt2800_validate_eeprom' function. Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 9 +++++-- drivers/net/wireless/rt2x00/rt2800lib.h | 8 +++--- drivers/net/wireless/rt2x00/rt2800pci.c | 35 +++++++++++++++++-------- drivers/net/wireless/rt2x00/rt2800usb.c | 12 ++++++--- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 0ece3537106d..f139a913c25a 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4671,12 +4671,14 @@ static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i) mutex_unlock(&rt2x00dev->csr_mutex); } -void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) { unsigned int i; for (i = 0; i < EEPROM_SIZE / sizeof(u16); i += 8) rt2800_efuse_read(rt2x00dev, i); + + return 0; } EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse); @@ -4686,11 +4688,14 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) u16 word; u8 *mac; u8 default_lna_gain; + int retval; /* * Read the EEPROM. */ - rt2800_read_eeprom(rt2x00dev); + retval = rt2800_read_eeprom(rt2x00dev); + if (retval) + return retval; /* * Start validation of the data that has been read. diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index a128ceadcb3e..6ec739466db4 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -43,7 +43,7 @@ struct rt2800_ops { const unsigned int offset, const struct rt2x00_field32 field, u32 *reg); - void (*read_eeprom)(struct rt2x00_dev *rt2x00dev); + int (*read_eeprom)(struct rt2x00_dev *rt2x00dev); bool (*hwcrypt_disabled)(struct rt2x00_dev *rt2x00dev); int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev, @@ -117,11 +117,11 @@ static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev, return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg); } -static inline void rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev) +static inline int rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev) { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - rt2800ops->read_eeprom(rt2x00dev); + return rt2800ops->read_eeprom(rt2x00dev); } static inline bool rt2800_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) @@ -207,7 +207,7 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev); void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev); int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev); -void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); +int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 9224d874bf24..0e8d1705e368 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -90,17 +90,22 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) } #if defined(CONFIG_RALINK_RT288X) || defined(CONFIG_RALINK_RT305X) -static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) +static int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) { void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); + if (!base_addr) + return -ENOMEM; + memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); iounmap(base_addr); + return 0; } #else -static inline void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) +static inline int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) { + return -ENOMEM; } #endif /* CONFIG_RALINK_RT288X || CONFIG_RALINK_RT305X */ @@ -135,7 +140,7 @@ static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom) rt2x00pci_register_write(rt2x00dev, E2PROM_CSR, reg); } -static void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) +static int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) { struct eeprom_93cx6 eeprom; u32 reg; @@ -164,6 +169,8 @@ static void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, EEPROM_SIZE / sizeof(u16)); + + return 0; } static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) @@ -171,13 +178,14 @@ static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) return rt2800_efuse_detect(rt2x00dev); } -static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) { - rt2800_read_eeprom_efuse(rt2x00dev); + return rt2800_read_eeprom_efuse(rt2x00dev); } #else -static inline void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) +static inline int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) { + return -EOPNOTSUPP; } static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) @@ -185,8 +193,9 @@ static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) return 0; } -static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) { + return -EOPNOTSUPP; } #endif /* CONFIG_PCI */ @@ -970,14 +979,18 @@ static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) /* * Device probe functions. */ -static void rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) +static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) { + int retval; + if (rt2x00_is_soc(rt2x00dev)) - rt2800pci_read_eeprom_soc(rt2x00dev); + retval = rt2800pci_read_eeprom_soc(rt2x00dev); else if (rt2800pci_efuse_detect(rt2x00dev)) - rt2800pci_read_eeprom_efuse(rt2x00dev); + retval = rt2800pci_read_eeprom_efuse(rt2x00dev); else - rt2800pci_read_eeprom_pci(rt2x00dev); + retval = rt2800pci_read_eeprom_pci(rt2x00dev); + + return retval; } static const struct ieee80211_ops rt2800pci_mac80211_ops = { diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 5c149b58ab46..4721cada1591 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -735,13 +735,17 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, /* * Device probe functions. */ -static void rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) +static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) { + int retval; + if (rt2800_efuse_detect(rt2x00dev)) - rt2800_read_eeprom_efuse(rt2x00dev); + retval = rt2800_read_eeprom_efuse(rt2x00dev); else - rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, - EEPROM_SIZE); + retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, + EEPROM_SIZE); + + return retval; } static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) From 01486c5ad388c8a38f2e075666e8c2ee32bda4b6 Mon Sep 17 00:00:00 2001 From: Piotr Haber Date: Wed, 2 Jan 2013 15:22:34 +0100 Subject: [PATCH 200/478] brcmsmac: increase timer reference count for new timers only On hardware reintialization reference count of already existing timers would be increased again. This leads to problems on module unloading. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Reviewed-by: Arend van Spriel Signed-off-by: Piotr Haber Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index f0fc8cd4d5df..f8e2591961ab 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -1409,9 +1409,10 @@ void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic) #endif t->ms = ms; t->periodic = (bool) periodic; - t->set = true; - - atomic_inc(&t->wl->callbacks); + if (!t->set) { + t->set = true; + atomic_inc(&t->wl->callbacks); + } ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms)); } From 66578c0a8cb8c34ef284acf34e05f2d8e7416ee1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:22:35 +0100 Subject: [PATCH 201/478] brcmsmac: use perimeter lock in add_interface() callback All callbacks that access driver functions should do that under perimeter lock protection. The add_interface() callback was lacking this lock. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Piotr Haber Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index f8e2591961ab..b5b4487006f3 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -362,8 +362,10 @@ brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) return -EOPNOTSUPP; } + spin_lock_bh(&wl->lock); wl->mute_tx = false; brcms_c_mute(wl->wlc, false); + spin_unlock_bh(&wl->lock); return 0; } From b180b10056e204fa1f65f0494215d73eb8253c87 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:22:36 +0100 Subject: [PATCH 202/478] brcmsmac: allow user-space setting of interface address The interface address of the wireless device is determined by the permanent address stored in the device. This patch allows it to be overridden from user-space. Reported-by: Maximilian Engelhardt Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Piotr Haber Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 1 + drivers/net/wireless/brcm80211/brcmsmac/main.c | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index b5b4487006f3..7fc49ca3f597 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -363,6 +363,7 @@ brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } spin_lock_bh(&wl->lock); + memcpy(wl->pub->cur_etheraddr, vif->addr, sizeof(vif->addr)); wl->mute_tx = false; brcms_c_mute(wl->wlc, false); spin_unlock_bh(&wl->lock); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 5f0f444f85a3..c26992a60e6c 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -2473,6 +2473,7 @@ static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw, static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) { static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; + u8 *ethaddr = wlc_hw->wlc->pub->cur_etheraddr; if (mute_tx) { /* suspend tx fifos */ @@ -2482,8 +2483,7 @@ static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO); /* zero the address match register so we do not send ACKs */ - brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, - null_ether_addr); + brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, null_ether_addr); } else { /* resume tx fifos */ brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO); @@ -2492,8 +2492,7 @@ static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO); /* Restore address */ - brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, - wlc_hw->etheraddr); + brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, ethaddr); } wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0); From 637ccc27f46f2232267e689b6f8eb744f78f4810 Mon Sep 17 00:00:00 2001 From: Piotr Haber Date: Wed, 2 Jan 2013 15:22:37 +0100 Subject: [PATCH 203/478] brcmsmac: remove dead code Dead code after AMPDU restructure. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Reviewed-by: Arend van Spriel Signed-off-by: Piotr Haber Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/ampdu.c | 7 ++----- drivers/net/wireless/brcm80211/brcmsmac/scb.h | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c index 1de94f30564f..1585cc5bf866 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c @@ -961,7 +961,6 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, /* if acked then clear bit and free packet */ if ((bindex < AMPDU_TX_BA_MAX_WSIZE) && isset(bitmap, bindex)) { - ini->tx_in_transit--; ini->txretry[index] = 0; /* @@ -990,7 +989,6 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, if (retry && (ini->txretry[index] < (int)retry_limit)) { int ret; ini->txretry[index]++; - ini->tx_in_transit--; ret = brcms_c_txfifo(wlc, queue, p); /* * We shouldn't be out of space in the DMA @@ -1000,7 +998,6 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, WARN_ONCE(ret, "queue %d out of txds\n", queue); } else { /* Retry timeout */ - ini->tx_in_transit--; ieee80211_tx_info_clear_status(tx_info); tx_info->status.ampdu_ack_len = 0; tx_info->status.ampdu_len = 1; @@ -1009,8 +1006,8 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, skb_pull(p, D11_PHY_HDR_LEN); skb_pull(p, D11_TXH_LEN); brcms_dbg_ht(wlc->hw->d11core, - "BA Timeout, seq %d, in_transit %d\n", - seq, ini->tx_in_transit); + "BA Timeout, seq %d\n", + seq); ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/scb.h b/drivers/net/wireless/brcm80211/brcmsmac/scb.h index 51c79c7239b7..3a3d73699f83 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/scb.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/scb.h @@ -36,7 +36,6 @@ /* structure to store per-tid state for the ampdu initiator */ struct scb_ampdu_tid_ini { - u8 tx_in_transit; /* number of pending mpdus in transit in driver */ u8 tid; /* initiator tid for easy lookup */ /* tx retry count; indexed by seq modulo */ u8 txretry[AMPDU_TX_BA_MAX_WSIZE]; From 0429a6fa6be9489bf451e9fcc0f97341e4a356b6 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 2 Jan 2013 15:22:38 +0100 Subject: [PATCH 204/478] brcmfmac: do not reconfigure refill rx on 0-length packet. When USB device gets removed rx complete comes with 0-length packets. Do not refill those packets. In some rare cases it can cause infinite loop. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 914c56fe6c5f..22eae57d9546 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -467,7 +467,11 @@ static void brcmf_usb_rx_complete(struct urb *urb) devinfo->bus_pub.bus->dstats.rx_errors++; } else brcmf_rx_packet(devinfo->dev, ifidx, skb); - brcmf_usb_rx_refill(devinfo, req); + /* zero lenght packets indicate usb "failure". Do not refill */ + if (urb->actual_length) + brcmf_usb_rx_refill(devinfo, req); + else + brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); } else { brcmu_pkt_buf_free_skb(skb); brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); From ac744395ba0a0491f8524a8371ad6f7df67bf673 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 2 Jan 2013 15:22:39 +0100 Subject: [PATCH 205/478] brcmfmac: removed deprecated set_bitrate_mask support Set bitrate_mask is not desired anymore. The firmware will determine the correct rates to be used. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 1261a9b84e04..ee4eb4446892 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2011,67 +2011,6 @@ done: return err; } -static s32 -brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev, - const u8 *addr, - const struct cfg80211_bitrate_mask *mask) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcm_rateset_le rateset_le; - s32 rate; - s32 val; - s32 err_bg; - s32 err_a; - u32 legacy; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - /* addr param is always NULL. ignore it */ - /* Get current rateset */ - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CURR_RATESET, - &rateset_le, sizeof(rateset_le)); - if (err) { - brcmf_err("could not get current rateset (%d)\n", err); - goto done; - } - - legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF); - if (!legacy) - legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy & - 0xFFFF); - - val = wl_g_rates[legacy - 1].bitrate * 100000; - - if (val < le32_to_cpu(rateset_le.count)) - /* Select rate by rateset index */ - rate = rateset_le.rates[val] & 0x7f; - else - /* Specified rate in bps */ - rate = val / 500000; - - brcmf_dbg(CONN, "rate %d mbps\n", rate / 2); - - /* - * - * Set rate override, - * Since the is a/b/g-blind, both a/bg_rate are enforced. - */ - err_bg = brcmf_fil_iovar_int_set(ifp, "bg_rate", rate); - err_a = brcmf_fil_iovar_int_set(ifp, "a_rate", rate); - if (err_bg && err_a) { - brcmf_err("could not set fixed rate (%d) (%d)\n", err_bg, - err_a); - err = err_bg | err_a; - } - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, struct brcmf_bss_info_le *bi) { @@ -3703,7 +3642,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { .set_default_key = brcmf_cfg80211_config_default_key, .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, - .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask, .connect = brcmf_cfg80211_connect, .disconnect = brcmf_cfg80211_disconnect, .suspend = brcmf_cfg80211_suspend, From 81d5f1bbc47961b2347d2b92b2aec56e6bad8779 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:22:40 +0100 Subject: [PATCH 206/478] brcmfmac: assure USB dongle firmware is reset upon module unload Upon unloading the brcmfmac module the USB firmware should be reset as the device remains powered. The reset assures a known device state when a new brcmfmac driver load is being done. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 1 + drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 3 ++- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 11 +++++++++++ drivers/net/wireless/brcm80211/brcmfmac/usb.c | 13 +++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index fd672bf53867..7245b171190a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -39,6 +39,7 @@ #define BRCMF_C_GET_BSSID 23 #define BRCMF_C_GET_SSID 25 #define BRCMF_C_SET_SSID 26 +#define BRCMF_C_TERMINATED 28 #define BRCMF_C_GET_CHANNEL 29 #define BRCMF_C_SET_CHANNEL 30 #define BRCMF_C_GET_SRL 31 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index dd38b78a9726..358b54fff795 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -154,7 +154,8 @@ static inline void brcmf_rx_packet(struct device *dev, int ifidx, extern int brcmf_attach(uint bus_hdrlen, struct device *dev); /* Indication from bus module regarding removal/absence of dongle */ extern void brcmf_detach(struct device *dev); - +/* Indication from bus module that dongle should be reset */ +extern void brcmf_dev_reset(struct device *dev); /* Indication from bus module to change flow-control state */ extern void brcmf_txflowblock(struct device *dev, bool state); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 74a616b4de8e..16efcb48dfbc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -845,6 +845,17 @@ static void brcmf_bus_detach(struct brcmf_pub *drvr) } } +void brcmf_dev_reset(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + if (drvr == NULL) + return; + + brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1); +} + void brcmf_detach(struct device *dev) { int i; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 22eae57d9546..1df85955ad93 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1524,10 +1524,23 @@ static void brcmf_release_fw(struct list_head *q) } } +static int brcmf_usb_reset_device(struct device *dev, void *notused) +{ + /* device past is the usb interface so we + * need to use parent here. + */ + brcmf_dev_reset(dev->parent); + return 0; +} void brcmf_usb_exit(void) { + struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver; + int ret; + brcmf_dbg(USB, "Enter\n"); + ret = driver_for_each_device(drv, NULL, NULL, + brcmf_usb_reset_device); usb_deregister(&brcmf_usbdrvr); brcmf_release_fw(&fw_image_list); } From 79d7c4e8da4ac3cfa98e8e622a17baaeb3c9d29d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 21:20:10 +0100 Subject: [PATCH 207/478] brcmfmac: define pr_fmt in one place Several source files (but not all) define the pr_fmt() macro in exactly the same way. Instead this commit defines it in a header file so driver logging is consistent. Cc: Joe Perches Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 -- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c | 2 -- drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c | 2 -- drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c | 2 -- drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 4 ++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 -- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 -- drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c | 2 -- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 -- 9 files changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index be35a2f99b1c..11fd1c735589 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -15,8 +15,6 @@ */ /* ****************** SDIO CARD Interface Functions **************************/ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index d33e5598611b..d92d373733d7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -14,8 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index 83923553f1ac..0c83998af181 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -19,8 +19,6 @@ * For certain dcmd codes, the dongle interprets string data from the host. ******************************************************************************/ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index f8b52e5b941a..4544342a0428 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -14,8 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index f2ab01cd7966..ea3f5f5a6ab9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -34,6 +34,10 @@ #define BRCMF_SCAN_VAL 0x4000 #define BRCMF_CONN_VAL 0x8000 +/* set default print format */ +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /* Macro for error messages. net_ratelimit() is used when driver * debugging is not selected. When debugging the driver error * messages are as important as other tracing or even more so. diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 16efcb48dfbc..6520b587d4c8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -14,8 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index cf857f1edf8c..651474bc1ce3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -14,8 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index b1bb46c49799..14be2d5530ce 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -15,8 +15,6 @@ */ /* ***** SDIO interface chip backplane handle functions ***** */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index ee4eb4446892..1ec2d573abc2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -16,8 +16,6 @@ /* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include #include #include From 3aa7aad2b2da32dd897900ab3eb62348a54d2dc1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:22:42 +0100 Subject: [PATCH 208/478] brcmfmac: remove rx helper function from bus interface The bus interface provided a wrapper function to pass a single packet to the common driver part filling a skb queue with one packet. For clarity the caller now sets up the skb queue and call the rx bus interface function. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 13 ++----------- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 4 ++-- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 7 +++++-- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 5 ++++- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 358b54fff795..639bc2a2d03c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -138,17 +138,8 @@ extern bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec); /* Receive frame for delivery to OS. Callee disposes of rxp. */ -extern void brcmf_rx_frame(struct device *dev, u8 ifidx, - struct sk_buff_head *rxlist); -static inline void brcmf_rx_packet(struct device *dev, int ifidx, - struct sk_buff *pkt) -{ - struct sk_buff_head q; - - skb_queue_head_init(&q); - skb_queue_tail(&q, pkt); - brcmf_rx_frame(dev, ifidx, &q); -} +extern void brcmf_rx_frames(struct device *dev, u8 ifidx, + struct sk_buff_head *rxlist); /* Indication from bus module regarding presence/insertion of dongle. */ extern int brcmf_attach(uint bus_hdrlen, struct device *dev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 6520b587d4c8..a820f58100b7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -248,8 +248,8 @@ void brcmf_txflowblock(struct device *dev, bool state) } } -void brcmf_rx_frame(struct device *dev, u8 ifidx, - struct sk_buff_head *skb_list) +void brcmf_rx_frames(struct device *dev, u8 ifidx, + struct sk_buff_head *skb_list) { unsigned char *eth; uint len; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 651474bc1ce3..13ea9b48acdb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -1405,7 +1405,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } /* sent any remaining packets up */ if (bus->glom.qlen) - brcmf_rx_frame(bus->sdiodev->dev, ifidx, &bus->glom); + brcmf_rx_frames(bus->sdiodev->dev, ifidx, &bus->glom); bus->sdcnt.rxglomframes++; bus->sdcnt.rxglompkts += bus->glom.qlen; @@ -1556,6 +1556,7 @@ static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) { struct sk_buff *pkt; /* Packet for event or data frames */ + struct sk_buff_head pktlist; /* needed for bus interface */ u16 pad; /* Number of pad bytes to read */ uint rxleft = 0; /* Remaining number of frames allowed */ int sdret; /* Return code from calls */ @@ -1766,7 +1767,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) continue; } - brcmf_rx_packet(bus->sdiodev->dev, ifidx, pkt); + skb_queue_head_init(&pktlist); + skb_queue_tail(&pktlist, pkt); + brcmf_rx_frames(bus->sdiodev->dev, ifidx, &pktlist); } rxcount = maxframes - rxleft; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 1df85955ad93..34342ff718db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -443,6 +443,7 @@ static void brcmf_usb_rx_complete(struct urb *urb) struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; struct brcmf_usbdev_info *devinfo = req->devinfo; struct sk_buff *skb; + struct sk_buff_head skbq; int ifidx = 0; brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); @@ -460,13 +461,15 @@ static void brcmf_usb_rx_complete(struct urb *urb) } if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { + skb_queue_head_init(&skbq); + skb_queue_tail(&skbq, skb); skb_put(skb, urb->actual_length); if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) { brcmf_err("rx protocol error\n"); brcmu_pkt_buf_free_skb(skb); devinfo->bus_pub.bus->dstats.rx_errors++; } else - brcmf_rx_packet(devinfo->dev, ifidx, skb); + brcmf_rx_frames(devinfo->dev, ifidx, &skbq); /* zero lenght packets indicate usb "failure". Do not refill */ if (urb->actual_length) brcmf_usb_rx_refill(devinfo, req); From a43af515f6252e62604a1bfc139d43fa43ef5b6f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:22:43 +0100 Subject: [PATCH 209/478] brcmfmac: remove brcmf_proto_hdrpull() from bus interface The use of the function brcmf_proto_hdrpull() is moved to the common part of the driver and consequently it can be removed from the bus interface. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 4 ++++ .../net/wireless/brcm80211/brcmfmac/dhd_bus.h | 7 +------ .../net/wireless/brcm80211/brcmfmac/dhd_cdc.c | 6 +++--- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 19 +++++++++++++++---- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 19 ++----------------- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 17 ++++------------- 6 files changed, 29 insertions(+), 43 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 7245b171190a..4937bf4d5506 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -577,6 +577,10 @@ extern int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); +/* Remove any protocol-specific data header. */ +extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, + struct sk_buff *rxp); + extern int brcmf_net_attach(struct brcmf_if *ifp); extern struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, int ifidx, s32 bssidx, char *name, u8 *mac_addr); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 639bc2a2d03c..64c38f4226a3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -130,16 +130,11 @@ int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len) * interface functions from common layer */ -/* Remove any protocol-specific data header. */ -extern int brcmf_proto_hdrpull(struct device *dev, int *ifidx, - struct sk_buff *rxp); - extern bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec); /* Receive frame for delivery to OS. Callee disposes of rxp. */ -extern void brcmf_rx_frames(struct device *dev, u8 ifidx, - struct sk_buff_head *rxlist); +extern void brcmf_rx_frames(struct device *dev, struct sk_buff_head *rxlist); /* Indication from bus module regarding presence/insertion of dongle. */ extern int brcmf_attach(uint bus_hdrlen, struct device *dev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index 0c83998af181..aa37f3e614b6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -303,12 +303,10 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, BDC_SET_IF_IDX(h, ifidx); } -int brcmf_proto_hdrpull(struct device *dev, int *ifidx, +int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, struct sk_buff *pktbuf) { struct brcmf_proto_bdc_header *h; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; brcmf_dbg(TRACE, "Enter\n"); @@ -346,6 +344,8 @@ int brcmf_proto_hdrpull(struct device *dev, int *ifidx, skb_pull(pktbuf, BDC_HEADER_LEN); skb_pull(pktbuf, h->data_offset << 2); + if (pktbuf->len == 0) + return -ENODATA; return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index a820f58100b7..682cb02a4674 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -248,8 +248,7 @@ void brcmf_txflowblock(struct device *dev, bool state) } } -void brcmf_rx_frames(struct device *dev, u8 ifidx, - struct sk_buff_head *skb_list) +void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) { unsigned char *eth; uint len; @@ -257,12 +256,24 @@ void brcmf_rx_frames(struct device *dev, u8 ifidx, struct brcmf_if *ifp; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; + u8 ifidx; + int ret; brcmf_dbg(TRACE, "Enter\n"); skb_queue_walk_safe(skb_list, skb, pnext) { skb_unlink(skb, skb_list); + /* process and remove protocol-specific header + */ + ret = brcmf_proto_hdrpull(drvr, &ifidx, skb); + if (ret < 0) { + if (ret != -ENODATA) + bus_if->dstats.rx_errors++; + brcmu_pkt_buf_free_skb(skb); + continue; + } + /* Get the protocol, maintain skb around eth_type_trans() * The main reason for this hack is for the limitation of * Linux 2.4 where 'eth_type_trans' uses the @@ -326,13 +337,13 @@ void brcmf_rx_frames(struct device *dev, u8 ifidx, void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) { - uint ifidx; + u8 ifidx; struct ethhdr *eh; u16 type; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; - brcmf_proto_hdrpull(dev, &ifidx, txp); + brcmf_proto_hdrpull(drvr, &ifidx, txp); eh = (struct ethhdr *)(txp->data); type = ntohs(eh->h_proto); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 13ea9b48acdb..7fef9b5ba003 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -1167,7 +1167,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) int errcode; u8 doff, sfdoff; - int ifidx = 0; bool usechain = bus->use_rxchain; struct brcmf_sdio_read rd_new; @@ -1386,13 +1385,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) skb_unlink(pfirst, &bus->glom); brcmu_pkt_buf_free_skb(pfirst); continue; - } else if (brcmf_proto_hdrpull(bus->sdiodev->dev, - &ifidx, pfirst) != 0) { - brcmf_err("rx protocol error\n"); - bus->sdiodev->bus_if->dstats.rx_errors++; - skb_unlink(pfirst, &bus->glom); - brcmu_pkt_buf_free_skb(pfirst); - continue; } brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), @@ -1405,7 +1397,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } /* sent any remaining packets up */ if (bus->glom.qlen) - brcmf_rx_frames(bus->sdiodev->dev, ifidx, &bus->glom); + brcmf_rx_frames(bus->sdiodev->dev, &bus->glom); bus->sdcnt.rxglomframes++; bus->sdcnt.rxglompkts += bus->glom.qlen; @@ -1560,7 +1552,6 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) u16 pad; /* Number of pad bytes to read */ uint rxleft = 0; /* Remaining number of frames allowed */ int sdret; /* Return code from calls */ - int ifidx = 0; uint rxcount = 0; /* Total frames read */ struct brcmf_sdio_read *rd = &bus->cur_read, rd_new; u8 head_read = 0; @@ -1759,17 +1750,11 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) if (pkt->len == 0) { brcmu_pkt_buf_free_skb(pkt); continue; - } else if (brcmf_proto_hdrpull(bus->sdiodev->dev, &ifidx, - pkt) != 0) { - brcmf_err("rx protocol error\n"); - brcmu_pkt_buf_free_skb(pkt); - bus->sdiodev->bus_if->dstats.rx_errors++; - continue; } skb_queue_head_init(&pktlist); skb_queue_tail(&pktlist, pkt); - brcmf_rx_frames(bus->sdiodev->dev, ifidx, &pktlist); + brcmf_rx_frames(bus->sdiodev->dev, &pktlist); } rxcount = maxframes - rxleft; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 34342ff718db..e15630cc3889 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -444,14 +444,14 @@ static void brcmf_usb_rx_complete(struct urb *urb) struct brcmf_usbdev_info *devinfo = req->devinfo; struct sk_buff *skb; struct sk_buff_head skbq; - int ifidx = 0; brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); brcmf_usb_del_fromq(devinfo, req); skb = req->skb; req->skb = NULL; - if (urb->status == 0) { + /* zero lenght packets indicate usb "failure". Do not refill */ + if (urb->status == 0 && urb->actual_length) { devinfo->bus_pub.bus->dstats.rx_packets++; } else { devinfo->bus_pub.bus->dstats.rx_errors++; @@ -464,17 +464,8 @@ static void brcmf_usb_rx_complete(struct urb *urb) skb_queue_head_init(&skbq); skb_queue_tail(&skbq, skb); skb_put(skb, urb->actual_length); - if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) { - brcmf_err("rx protocol error\n"); - brcmu_pkt_buf_free_skb(skb); - devinfo->bus_pub.bus->dstats.rx_errors++; - } else - brcmf_rx_frames(devinfo->dev, ifidx, &skbq); - /* zero lenght packets indicate usb "failure". Do not refill */ - if (urb->actual_length) - brcmf_usb_rx_refill(devinfo, req); - else - brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); + brcmf_rx_frames(devinfo->dev, &skbq); + brcmf_usb_rx_refill(devinfo, req); } else { brcmu_pkt_buf_free_skb(skb); brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); From a6cfb1477d23aa251a1a4b0285fb385d94cdde51 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 2 Jan 2013 15:22:44 +0100 Subject: [PATCH 210/478] brcmfmac: Use dedicated trace level for CDC. CDC debug is partly done with TRACE and partly with CTL, however CDC hardly ever needs debugging. Use dedicated level CDC and replace TRACE and CTL in dhd_cdc with that. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_cdc.c | 16 +++++----- .../net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 31 ++++++++++--------- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index aa37f3e614b6..db3bc2e99902 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -105,7 +105,7 @@ static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr) int len = le32_to_cpu(prot->msg.len) + sizeof(struct brcmf_proto_cdc_dcmd); - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CDC, "Enter\n"); /* NOTE : cdc->msg.len holds the desired length of the buffer to be * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area @@ -123,7 +123,7 @@ static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) int ret; struct brcmf_proto *prot = drvr->prot; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CDC, "Enter\n"); len += sizeof(struct brcmf_proto_cdc_dcmd); do { ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&prot->msg, @@ -145,8 +145,7 @@ brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, int ret = 0, retries = 0; u32 id, flags; - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len); + brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); /* Respond "bcmerror" and "bcmerrorstr" with local cache */ if (cmd == BRCMF_C_GET_VAR && buf) { @@ -226,8 +225,7 @@ int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, int ret = 0; u32 flags, id; - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len); + brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); @@ -285,7 +283,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, { struct brcmf_proto_bdc_header *h; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CDC, "Enter\n"); /* Push BDC header used to convey priority for buses that don't */ @@ -308,7 +306,7 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, { struct brcmf_proto_bdc_header *h; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CDC, "Enter\n"); /* Pop BDC header used to convey priority for buses that don't */ @@ -334,7 +332,7 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, u8 *ifidx, } if (h->flags & BDC_FLAG_SUM_GOOD) { - brcmf_dbg(INFO, "%s: BDC packet received with good rx-csum, flags 0x%x\n", + brcmf_dbg(CDC, "%s: BDC rcv, good checksum, flags 0x%x\n", brcmf_ifname(drvr, *ifidx), h->flags); pkt_set_sum_good(pktbuf, true); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index ea3f5f5a6ab9..bc013cbe06f6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -18,21 +18,22 @@ #define _BRCMF_DBG_H_ /* message levels */ -#define BRCMF_TRACE_VAL 0x0002 -#define BRCMF_INFO_VAL 0x0004 -#define BRCMF_DATA_VAL 0x0008 -#define BRCMF_CTL_VAL 0x0010 -#define BRCMF_TIMER_VAL 0x0020 -#define BRCMF_HDRS_VAL 0x0040 -#define BRCMF_BYTES_VAL 0x0080 -#define BRCMF_INTR_VAL 0x0100 -#define BRCMF_GLOM_VAL 0x0200 -#define BRCMF_EVENT_VAL 0x0400 -#define BRCMF_BTA_VAL 0x0800 -#define BRCMF_FIL_VAL 0x1000 -#define BRCMF_USB_VAL 0x2000 -#define BRCMF_SCAN_VAL 0x4000 -#define BRCMF_CONN_VAL 0x8000 +#define BRCMF_TRACE_VAL 0x00000002 +#define BRCMF_INFO_VAL 0x00000004 +#define BRCMF_DATA_VAL 0x00000008 +#define BRCMF_CTL_VAL 0x00000010 +#define BRCMF_TIMER_VAL 0x00000020 +#define BRCMF_HDRS_VAL 0x00000040 +#define BRCMF_BYTES_VAL 0x00000080 +#define BRCMF_INTR_VAL 0x00000100 +#define BRCMF_GLOM_VAL 0x00000200 +#define BRCMF_EVENT_VAL 0x00000400 +#define BRCMF_BTA_VAL 0x00000800 +#define BRCMF_FIL_VAL 0x00001000 +#define BRCMF_USB_VAL 0x00002000 +#define BRCMF_SCAN_VAL 0x00004000 +#define BRCMF_CONN_VAL 0x00008000 +#define BRCMF_CDC_VAL 0x00010000 /* set default print format */ #undef pr_fmt diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 682cb02a4674..a1ca6912ef28 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -459,7 +459,7 @@ static int brcmf_ethtool(struct brcmf_if *ifp, void __user *uaddr) sprintf(info.version, "%lu", drvr->drv_version); if (copy_to_user(uaddr, &info, sizeof(info))) return -EFAULT; - brcmf_dbg(CTL, "given %*s, returning %s\n", + brcmf_dbg(TRACE, "given %*s, returning %s\n", (int)sizeof(drvname), drvname, info.driver); break; From 7833b5799fb4dd201d6808c164712ea9d0c3dec0 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:22:45 +0100 Subject: [PATCH 211/478] brcmfmac: remove unused event related definitions The driver had some global definitions in dhd.h to map event identifiers to event names. With redesign of firmware event processing this has all moved to fweh module so these definitions can be removed. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 4937bf4d5506..0388599f6be7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -520,11 +520,6 @@ struct brcmf_pub { #endif }; -struct bcmevent_name { - uint event; - const char *name; -}; - struct brcmf_if_event { u8 ifidx; u8 action; @@ -564,8 +559,6 @@ static inline s32 brcmf_ndev_bssidx(struct net_device *ndev) return ifp->bssidx; } -extern const struct bcmevent_name bcmevent_names[]; - extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev); /* Return pointer to interface name */ From 12b33dacb4bb3780512d3c6a4d064d9ed1172c0e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:22:46 +0100 Subject: [PATCH 212/478] brcmfmac: cleanup netdev transmit callback The header of the ethernet packet is processed conditionally, but the check is wrong as it checks skb length is at least ETH_ALEN. It should check it is at least sizeof struct ethhdr instead. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index a1ca6912ef28..9ca74118e131 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -165,6 +165,7 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) int ret; struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; + struct ethhdr *eh; brcmf_dbg(TRACE, "Enter\n"); @@ -202,17 +203,20 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) } } - /* Update multicast statistic */ - if (skb->len >= ETH_ALEN) { - u8 *pktdata = (u8 *)(skb->data); - struct ethhdr *eh = (struct ethhdr *)pktdata; - - if (is_multicast_ether_addr(eh->h_dest)) - drvr->tx_multicast++; - if (ntohs(eh->h_proto) == ETH_P_PAE) - atomic_inc(&drvr->pend_8021x_cnt); + /* validate length for ether packet */ + if (skb->len < sizeof(*eh)) { + ret = -EINVAL; + dev_kfree_skb(skb); + goto done; } + /* handle ethernet header */ + eh = (struct ethhdr *)(skb->data); + if (is_multicast_ether_addr(eh->h_dest)) + drvr->tx_multicast++; + if (ntohs(eh->h_proto) == ETH_P_PAE) + atomic_inc(&drvr->pend_8021x_cnt); + /* If the protocol uses a data header, apply it */ brcmf_proto_hdrpush(drvr, ifp->idx, skb); From b84e3112ecc73482a7e07cd88bf2feaa60e42634 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 2 Jan 2013 15:22:47 +0100 Subject: [PATCH 213/478] brcmfmac: remove unnecessary curly braces in dhd_attach() Stumbled into a curly braces used for if statement with only one conditional statement. Removing them. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 9ca74118e131..285a70ee9350 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -890,9 +890,8 @@ void brcmf_detach(struct device *dev) brcmf_bus_detach(drvr); - if (drvr->prot) { + if (drvr->prot) brcmf_proto_detach(drvr); - } brcmf_debugfs_detach(drvr); bus_if->drvr = NULL; From 03abad08bb837650feb2bd7581ef14c8a1c51964 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 2 Jan 2013 15:22:48 +0100 Subject: [PATCH 214/478] brcmfmac: Remove unused caching dongle error. CDC errors are retained. However, it is never used so it can be removed. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 3 --- .../net/wireless/brcm80211/brcmfmac/dhd_cdc.c | 22 ++----------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 0388599f6be7..bbefaf38b4df 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -496,9 +496,6 @@ struct brcmf_pub { /* Last error return */ int bcmerror; - /* Last error from dongle */ - int dongle_error; - /* Suspend disable flag flag */ int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index db3bc2e99902..4557709d9655 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -147,18 +147,6 @@ brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); - /* Respond "bcmerror" and "bcmerrorstr" with local cache */ - if (cmd == BRCMF_C_GET_VAR && buf) { - if (!strcmp((char *)buf, "bcmerrorstr")) { - strncpy((char *)buf, "bcm_error", - BCME_STRLEN); - goto done; - } else if (!strcmp((char *)buf, "bcmerror")) { - *(int *)buf = drvr->dongle_error; - goto done; - } - } - memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); msg->cmd = cpu_to_le32(cmd); @@ -207,11 +195,8 @@ retry: } /* Check the ERROR flag */ - if (flags & CDC_DCMD_ERROR) { + if (flags & CDC_DCMD_ERROR) ret = le32_to_cpu(msg->status); - /* Cache error from dongle */ - drvr->dongle_error = ret; - } done: return ret; @@ -258,11 +243,8 @@ int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, } /* Check the ERROR flag */ - if (flags & CDC_DCMD_ERROR) { + if (flags & CDC_DCMD_ERROR) ret = le32_to_cpu(msg->status); - /* Cache error from dongle */ - drvr->dongle_error = ret; - } done: return ret; From 80fd2dbee00edcd19fb8c04a12dbeefc55533f2e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 2 Jan 2013 15:22:49 +0100 Subject: [PATCH 215/478] brcmfmac: Return correct error on netdev xmit. Netdev xmit routine brcfm_netdev_start_xmit was defined incorrectly and used wrong return codes. Always eat the packet and return ok. Remove drvr_up check since it is not relevant. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 285a70ee9350..d655501305b0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -160,7 +160,8 @@ static void brcmf_netdev_set_multicast_list(struct net_device *ndev) schedule_work(&ifp->multicast_work); } -static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) +static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, + struct net_device *ndev) { int ret; struct brcmf_if *ifp = netdev_priv(ndev); @@ -169,20 +170,21 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) brcmf_dbg(TRACE, "Enter\n"); - /* Reject if down */ - if (!drvr->bus_if->drvr_up || - (drvr->bus_if->state != BRCMF_BUS_DATA)) { - brcmf_err("xmit rejected drvup=%d state=%d\n", - drvr->bus_if->drvr_up, - drvr->bus_if->state); + /* Can the device send data? */ + if (drvr->bus_if->state != BRCMF_BUS_DATA) { + brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state); netif_stop_queue(ndev); - return -ENODEV; + dev_kfree_skb(skb); + ret = -ENODEV; + goto done; } if (!drvr->iflist[ifp->idx]) { brcmf_err("bad ifidx %d\n", ifp->idx); netif_stop_queue(ndev); - return -ENODEV; + dev_kfree_skb(skb); + ret = -ENODEV; + goto done; } /* Make sure there's enough room for any header */ @@ -230,7 +232,7 @@ done: drvr->bus_if->dstats.tx_packets++; /* Return ok: we always eat the packet */ - return 0; + return NETDEV_TX_OK; } void brcmf_txflowblock(struct device *dev, bool state) From 88d1239a1b9713cd505314fe9a1031c775b3eb3c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 2 Jan 2013 15:22:50 +0100 Subject: [PATCH 216/478] brcmfmac: Removing obsolete variables and inline functions. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 24 ------------------- .../net/wireless/brcm80211/brcmfmac/dhd_cdc.c | 2 -- 2 files changed, 26 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index bbefaf38b4df..a2f32fb990fa 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -481,33 +481,14 @@ struct brcmf_pub { unsigned long drv_version; /* Version of dongle-resident driver */ u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */ - /* Additional stats for the bus level */ - /* Multicast data packets sent to dongle */ unsigned long tx_multicast; - /* Packets flushed due to unscheduled sendup thread */ - unsigned long rx_flushed; - /* Number of times dpc scheduled by watchdog timer */ - unsigned long wd_dpc_sched; - - /* Number of flow control pkts recvd */ - unsigned long fc_packets; - - /* Last error return */ - int bcmerror; - - /* Suspend disable flag flag */ - int suspend_disable_flag; /* "1" to disable all extra powersaving - during suspend */ - int in_suspend; /* flag set to 1 when early suspend called */ - int dtim_skip; /* dtim skip , default 0 means wake each dtim */ struct brcmf_if *iflist[BRCMF_MAX_IFS]; struct mutex proto_block; unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; - u8 macvalue[ETH_ALEN]; atomic_t pend_8021x_cnt; wait_queue_head_t pend_8021x_wait; @@ -550,11 +531,6 @@ struct brcmf_if { u8 mac_addr[ETH_ALEN]; }; -static inline s32 brcmf_ndev_bssidx(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - return ifp->bssidx; -} extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index 4557709d9655..bb454cdab29d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -92,8 +92,6 @@ struct brcmf_proto_bdc_header { struct brcmf_proto { u16 reqid; - u8 pending; - u32 lastcmd; u8 bus_header[BUS_HEADER_LEN]; struct brcmf_proto_cdc_dcmd msg; unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN]; From 40a23296854dded596fda33e0df4a7373229d75e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 2 Jan 2013 15:22:51 +0100 Subject: [PATCH 217/478] brcmfmac: Update init code routines from interface up. On interface up dongle gets inialized. Move UP command to common routine and update these common init routines using ifp. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 9 ++---- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 30 ++++++++----------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index d655501305b0..e3326a58bdb1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -585,14 +585,9 @@ static int brcmf_netdev_open(struct net_device *ndev) /* Get current TOE mode from dongle */ if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) - drvr->iflist[ifp->idx]->ndev->features |= - NETIF_F_IP_CSUM; + ndev->features |= NETIF_F_IP_CSUM; else - drvr->iflist[ifp->idx]->ndev->features &= - ~NETIF_F_IP_CSUM; - - /* make sure RF is ready for work */ - brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); + ndev->features &= ~NETIF_F_IP_CSUM; /* Allow transmit calls */ netif_start_queue(ndev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 1ec2d573abc2..730da84edb01 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4265,9 +4265,8 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) } static s32 -brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) +brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) { - struct brcmf_if *ifp = netdev_priv(ndev); s32 err = 0; __le32 roamtrigger[2]; __le32 roam_delta[2]; @@ -4318,10 +4317,9 @@ dongle_rom_out: } static s32 -brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, +brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time, s32 scan_unassoc_time, s32 scan_passive_time) { - struct brcmf_if *ifp = netdev_priv(ndev); s32 err = 0; err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, @@ -4391,6 +4389,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) { struct net_device *ndev; struct wireless_dev *wdev; + struct brcmf_if *ifp; s32 power_mode; s32 err = 0; @@ -4399,35 +4398,34 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) ndev = cfg_to_ndev(cfg); wdev = ndev->ieee80211_ptr; + ifp = netdev_priv(ndev); - brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME, - WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); + /* make sure RF is ready for work */ + brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); + + brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME, + WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; - err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PM, - power_mode); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); if (err) goto default_conf_out; brcmf_dbg(INFO, "power save set to %s\n", (power_mode ? "enabled" : "disabled")); - err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1), - WL_BEACON_TIMEOUT); + err = brcmf_dongle_roam(ifp, (cfg->roam_on ? 0 : 1), WL_BEACON_TIMEOUT); if (err) goto default_conf_out; err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, NULL, NULL); - if (err && err != -EINPROGRESS) + if (err) goto default_conf_out; err = brcmf_dongle_probecap(cfg); if (err) goto default_conf_out; - /* -EINPROGRESS: Call commit handler */ - -default_conf_out: - cfg->dongle_up = true; +default_conf_out: return err; @@ -4436,8 +4434,6 @@ default_conf_out: static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp) { set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); - if (ifp->idx) - return 0; return brcmf_config_dongle(ifp->drvr->config); } From 6960af6dce1f0b2d9d1b4ddf75952d54e633b923 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Thu, 3 Jan 2013 00:06:47 +0400 Subject: [PATCH 218/478] p54pci: don't return zero on failure paths in p54p_probe() If pci_set_dma_mask() or pci_set_consistent_dma_mask() fails in p54p_probe(), it breaks off initialization, deallocates all resources, but returns zero. Similar issue is if check for returned value of pci_resource_len() fails. The patch implements proper error code propagation. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Acked-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54pci.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index 933e5d941937..57e3af8ebb4b 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -559,6 +559,7 @@ static int p54p_probe(struct pci_dev *pdev, mem_len = pci_resource_len(pdev, 0); if (mem_len < sizeof(struct p54p_csr)) { dev_err(&pdev->dev, "Too short PCI resources\n"); + err = -ENODEV; goto err_disable_dev; } @@ -568,8 +569,10 @@ static int p54p_probe(struct pci_dev *pdev, goto err_disable_dev; } - if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || - pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { dev_err(&pdev->dev, "No suitable DMA available\n"); goto err_free_reg; } From fc40ca92344a4e4a45bf916cf067e92085b881ea Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 2 Jan 2013 16:23:01 -0800 Subject: [PATCH 219/478] wireless: mwifiex: remove unreachable paths We know 'firmware' is non-NULL from the beginning of mwifiex_prog_fw_w_helper, remove all !firmware paths from the rest of the function. Signed-off-by: Sasha Levin Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/usb.c | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 63ac9f2d11ae..5d4a10a8a005 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -786,21 +786,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) return 0; } -/* This function reads one block of firmware data. */ -static int mwifiex_get_fw_data(struct mwifiex_adapter *adapter, - u32 offset, u32 len, u8 *buf) -{ - if (!buf || !len) - return -1; - - if (offset + len > adapter->firmware->size) - return -1; - - memcpy(buf, adapter->firmware->data + offset, len); - - return 0; -} - static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw) { @@ -836,23 +821,14 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, dlen = 0; } else { /* copy the header of the fw_data to get the length */ - if (firmware) - memcpy(&fwdata->fw_hdr, &firmware[tlen], - sizeof(struct fw_header)); - else - mwifiex_get_fw_data(adapter, tlen, - sizeof(struct fw_header), - (u8 *)&fwdata->fw_hdr); + memcpy(&fwdata->fw_hdr, &firmware[tlen], + sizeof(struct fw_header)); dlen = le32_to_cpu(fwdata->fw_hdr.data_len); dnld_cmd = le32_to_cpu(fwdata->fw_hdr.dnld_cmd); tlen += sizeof(struct fw_header); - if (firmware) - memcpy(fwdata->data, &firmware[tlen], dlen); - else - mwifiex_get_fw_data(adapter, tlen, dlen, - (u8 *)fwdata->data); + memcpy(fwdata->data, &firmware[tlen], dlen); fwdata->seq_num = cpu_to_le32(fw_seqnum); tlen += dlen; From d35f1035b53e92167a8231e15121dd10f4aa8edd Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 2 Jan 2013 16:56:00 -0800 Subject: [PATCH 220/478] mwifiex: use correct htcapinfo for HT20 ibss network It is observed that same htcapinfo ie is included in beacon for HT20, HT40+ and HT40- ibss networks. This patch makes sure that we will not advertise 40Mhz flags while creating/joining ibss network in HT20 mode. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n.c | 4 +++- drivers/net/wireless/mwifiex/join.c | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 245a371f1a43..9cd6216c61e6 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -53,7 +53,9 @@ mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, sizeof(sband->ht_cap.mcs)); if (priv->bss_mode == NL80211_IFTYPE_STATION || - sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) + (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + (priv->adapter->sec_chan_offset != + IEEE80211_HT_PARAM_CHA_SEC_NONE))) /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask); diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 88664ae667ba..3c7cabeddf76 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -969,6 +969,16 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, priv->adapter->config_bands); mwifiex_fill_cap_info(priv, radio_type, ht_cap); + if (adapter->sec_chan_offset == + IEEE80211_HT_PARAM_CHA_SEC_NONE) { + u16 tmp_ht_cap; + + tmp_ht_cap = le16_to_cpu(ht_cap->ht_cap.cap_info); + tmp_ht_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + tmp_ht_cap &= ~IEEE80211_HT_CAP_SGI_40; + ht_cap->ht_cap.cap_info = cpu_to_le16(tmp_ht_cap); + } + pos += sizeof(struct mwifiex_ie_types_htcap); cmd_append_size += sizeof(struct mwifiex_ie_types_htcap); From 54428c57b6d7a9dc172ecfa80b07f17431015d09 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Wed, 2 Jan 2013 16:56:01 -0800 Subject: [PATCH 221/478] mwifiex: parse WMM IEs from hostapd for mwifiex AP This patch adds support for parsing WMM IEs from hostapd and setting them to FW via sys configure command. Patch also sets wiphy flag to advertise AP uAPSD support. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 2 ++ drivers/net/wireless/mwifiex/decl.h | 18 ++++++++++ drivers/net/wireless/mwifiex/fw.h | 11 +++---- drivers/net/wireless/mwifiex/ioctl.h | 3 +- drivers/net/wireless/mwifiex/main.h | 4 +++ drivers/net/wireless/mwifiex/uap_cmd.c | 44 +++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index a875499f8945..c4edb28cce08 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1327,6 +1327,7 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, } mwifiex_set_ht_params(priv, bss_cfg, params); + mwifiex_set_wmm_params(priv, bss_cfg, params); if (params->inactivity_timeout > 0) { /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ @@ -2261,6 +2262,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index e9357d87d327..e8a569aaa2e8 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -26,6 +26,7 @@ #include #include #include +#include #define MWIFIEX_MAX_BSS_NUM (3) @@ -58,6 +59,8 @@ #define MWIFIEX_RTS_MAX_VALUE (2347) #define MWIFIEX_FRAG_MIN_VALUE (256) #define MWIFIEX_FRAG_MAX_VALUE (2346) +#define MWIFIEX_WMM_VERSION 0x01 +#define MWIFIEX_WMM_SUBTYPE 0x01 #define MWIFIEX_RETRY_LIMIT 14 #define MWIFIEX_SDIO_BLOCK_SIZE 256 @@ -126,4 +129,19 @@ enum mwifiex_wmm_ac_e { WMM_AC_VI, WMM_AC_VO } __packed; + +struct ieee_types_wmm_ac_parameters { + u8 aci_aifsn_bitmap; + u8 ecw_bitmap; + __le16 tx_op_limit; +} __packed; + +struct mwifiex_types_wmm_info { + u8 oui[4]; + u8 subtype; + u8 version; + u8 qos_info; + u8 reserved; + struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; +} __packed; #endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 4dc8e2e9a889..41c85dd78084 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -1131,12 +1131,6 @@ struct ieee_types_vendor_header { u8 version; } __packed; -struct ieee_types_wmm_ac_parameters { - u8 aci_aifsn_bitmap; - u8 ecw_bitmap; - __le16 tx_op_limit; -} __packed; - struct ieee_types_wmm_parameter { /* * WMM Parameter IE - Vendor Specific Header: @@ -1186,6 +1180,11 @@ struct mwifiex_ie_types_htcap { struct ieee80211_ht_cap ht_cap; } __packed; +struct mwifiex_ie_types_wmmcap { + struct mwifiex_ie_types_header header; + struct mwifiex_types_wmm_info wmm_info; +} __packed; + struct mwifiex_ie_types_htinfo { struct mwifiex_ie_types_header header; struct ieee80211_ht_operation ht_oper; diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 4e31c6013ebe..6095b3e53f4e 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -20,7 +20,6 @@ #ifndef _MWIFIEX_IOCTL_H_ #define _MWIFIEX_IOCTL_H_ -#include #include enum { @@ -107,6 +106,8 @@ struct mwifiex_uap_bss_param { u8 rates[MWIFIEX_SUPPORTED_RATES]; u32 sta_ao_timer; u32 ps_sta_ao_timer; + u8 qos_info; + struct mwifiex_types_wmm_info wmm_info; }; enum { diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 1b3cfc821940..d717c9859fb9 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -890,6 +890,10 @@ void mwifiex_set_ht_params(struct mwifiex_private *priv, struct cfg80211_ap_settings *params); void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, struct cfg80211_ap_settings *params); +void +mwifiex_set_wmm_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); /* * This function checks if the queuing is RA based or not. diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 8dd72240f162..6e76a15a8950 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -219,6 +219,7 @@ void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config) config->rts_threshold = 0x7FFF; config->frag_threshold = 0x7FFF; config->retry_limit = 0x7F; + config->qos_info = 0xFF; } /* This function parses BSS related parameters from structure @@ -297,6 +298,38 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) return; } +/* This function parses WMM related parameters from cfg80211_ap_settings + * structure and updates bss_config structure. + */ +void +mwifiex_set_wmm_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *vendor_ie; + struct ieee_types_header *wmm_ie; + u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; + + vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + params->beacon.tail, + params->beacon.tail_len); + if (vendor_ie) { + wmm_ie = (struct ieee_types_header *)vendor_ie; + memcpy(&bss_cfg->wmm_info, wmm_ie + 1, + sizeof(bss_cfg->wmm_info)); + priv->wmm_enabled = 1; + } else { + memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info)); + memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui)); + bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE; + bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION; + priv->wmm_enabled = 0; + } + + bss_cfg->qos_info = 0x00; + return; +} /* This function parses BSS related parameters from structure * and prepares TLVs specific to WEP encryption. * These TLVs are appended to command buffer. @@ -354,6 +387,7 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) struct host_cmd_tlv_rates *tlv_rates; struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer; struct mwifiex_ie_types_htcap *htcap; + struct mwifiex_ie_types_wmmcap *wmm_cap; struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; int i; u16 cmd_size = *param_size; @@ -507,6 +541,16 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) tlv += sizeof(struct mwifiex_ie_types_htcap); } + if (bss_cfg->wmm_info.qos_info != 0xFF) { + wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv; + wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC); + wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info)); + memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info, + sizeof(wmm_cap->wmm_info)); + cmd_size += sizeof(struct mwifiex_ie_types_wmmcap); + tlv += sizeof(struct mwifiex_ie_types_wmmcap); + } + if (bss_cfg->sta_ao_timer) { ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); From b686929c544be6d2d30b82dfb7a61a7766885177 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Thu, 3 Jan 2013 12:21:21 +0530 Subject: [PATCH 222/478] ath9k_hw: Enable calibration types in init_cal_settings Doing this in ath9k_hw_fill_cap_info() is odd and it's cleaner to do this in the init function for calibration. Also, setup the supported calibration type in init_cal_settings. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 17 +++++++++++------ drivers/net/wireless/ath/ath9k/hw.c | 7 ------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 8b0d8dcd7625..598b847e4e5a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -323,6 +323,14 @@ static const struct ath9k_percal_data iq_cal_single_sample = { static void ar9003_hw_init_cal_settings(struct ath_hw *ah) { ah->iq_caldata.calData = &iq_cal_single_sample; + + if (AR_SREV_9300_20_OR_LATER(ah)) { + ah->enabled_cals |= TX_IQ_CAL; + if (AR_SREV_9485_OR_LATER(ah)) + ah->enabled_cals |= TX_IQ_ON_AGC_CAL; + } + + ah->supp_cals = IQ_MISMATCH_CAL; } /* @@ -1131,13 +1139,10 @@ skip_tx_iqcal: /* Initialize list pointers */ ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; - ah->supp_cals = IQ_MISMATCH_CAL; - if (ah->supp_cals & IQ_MISMATCH_CAL) { - INIT_CAL(&ah->iq_caldata); - INSERT_CAL(ah, &ah->iq_caldata); - ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n"); - } + INIT_CAL(&ah->iq_caldata); + INSERT_CAL(ah, &ah->iq_caldata); + ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n"); if (ah->supp_cals & TEMP_COMP_CAL) { INIT_CAL(&ah->tempCompCalData); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index ecf1c08a03c1..caf0626eeaa5 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2568,12 +2568,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) rx_chainmask >>= 1; } - if (AR_SREV_9300_20_OR_LATER(ah)) { - ah->enabled_cals |= TX_IQ_CAL; - if (AR_SREV_9485_OR_LATER(ah)) - ah->enabled_cals |= TX_IQ_ON_AGC_CAL; - } - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { if (!(ah->ent_mode & AR_ENT_OTP_49GHZ_DISABLE)) pCap->hw_caps |= ATH9K_HW_CAP_MCI; @@ -2582,7 +2576,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->hw_caps |= ATH9K_HW_CAP_RTT; } - if (AR_SREV_9280_20_OR_LATER(ah)) { pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE | ATH9K_HW_WOW_PATTERN_MATCH_EXACT; From 1a6e5d7c96c247ee0d9f446248679cac1bd65dd1 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Thu, 3 Jan 2013 12:21:23 +0530 Subject: [PATCH 223/478] ath9k_hw: Remove TEMP_COMP_CAL This is not enabled for any chip and is unused. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 20 +++---------------- drivers/net/wireless/ath/ath9k/hw.h | 1 - 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 598b847e4e5a..a562a5d47eab 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -32,7 +32,6 @@ struct coeff { enum ar9003_cal_types { IQ_MISMATCH_CAL = BIT(0), - TEMP_COMP_CAL = BIT(1), }; static void ar9003_hw_setup_calibration(struct ath_hw *ah, @@ -49,7 +48,7 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah, */ REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, - currCal->calData->calCountMax); + currCal->calData->calCountMax); REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); ath_dbg(common, CALIBRATE, @@ -58,14 +57,8 @@ static void ar9003_hw_setup_calibration(struct ath_hw *ah, /* Kick-off cal */ REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); break; - case TEMP_COMP_CAL: - REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, - AR_PHY_65NM_CH0_THERM_LOCAL, 1); - REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, - AR_PHY_65NM_CH0_THERM_START, 1); - - ath_dbg(common, CALIBRATE, - "starting Temperature Compensation Calibration\n"); + default: + ath_err(common, "Invalid calibration type\n"); break; } } @@ -1144,13 +1137,6 @@ skip_tx_iqcal: INSERT_CAL(ah, &ah->iq_caldata); ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n"); - if (ah->supp_cals & TEMP_COMP_CAL) { - INIT_CAL(&ah->tempCompCalData); - INSERT_CAL(ah, &ah->tempCompCalData); - ath_dbg(common, CALIBRATE, - "enabling Temperature Compensation Calibration\n"); - } - /* Initialize current pointer to first element in list */ ah->cal_list_curr = ah->cal_list; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index d2b5229474b6..38c5a8702fb2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -768,7 +768,6 @@ struct ath_hw { struct ath9k_cal_list iq_caldata; struct ath9k_cal_list adcgain_caldata; struct ath9k_cal_list adcdc_caldata; - struct ath9k_cal_list tempCompCalData; struct ath9k_cal_list *cal_list; struct ath9k_cal_list *cal_list_last; struct ath9k_cal_list *cal_list_curr; From 96769caf8a755c9767b1a5c56fe71e062c2ad7da Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 3 Jan 2013 13:20:47 +0530 Subject: [PATCH 224/478] mwl8k: Remove redundant txq_idx Ampdu stream index can be derived from stream->idx. So we no longer need stream->txq_idx, hence removing it. Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 19b46fdf9f0f..a13f690f1851 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -160,7 +160,6 @@ struct mwl8k_ampdu_stream { u8 tid; u8 state; u8 idx; - u8 txq_idx; /* index of this stream in priv->txq */ }; struct mwl8k_priv { @@ -1741,7 +1740,6 @@ mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid) stream->state = AMPDU_STREAM_NEW; stream->tid = tid; stream->idx = i; - stream->txq_idx = MWL8K_TX_WMM_QUEUES + i; wiphy_debug(hw->wiphy, "Added a new stream for %pM %d", sta->addr, tid); return stream; @@ -1928,8 +1926,8 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, stream = mwl8k_lookup_stream(hw, sta->addr, tid); if (stream != NULL) { if (stream->state == AMPDU_STREAM_ACTIVE) { - txpriority = stream->txq_idx; - index = stream->txq_idx; + txpriority = stream->idx + MWL8K_TX_WMM_QUEUES; + index = stream->idx + MWL8K_TX_WMM_QUEUES; } else if (stream->state == AMPDU_STREAM_NEW) { /* We get here if the driver sends us packets * after we've initiated a stream, but before From 5f2a14940db23350612071a3c906c8960e3ceb9a Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 3 Jan 2013 13:21:25 +0530 Subject: [PATCH 225/478] mwl8k: set ack policy to normal Set ACK policy to NORMAL when its not going to be an AMPDU packet. Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a13f690f1851..7df18faae1b6 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1926,6 +1926,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, stream = mwl8k_lookup_stream(hw, sta->addr, tid); if (stream != NULL) { if (stream->state == AMPDU_STREAM_ACTIVE) { + WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK)); txpriority = stream->idx + MWL8K_TX_WMM_QUEUES; index = stream->idx + MWL8K_TX_WMM_QUEUES; } else if (stream->state == AMPDU_STREAM_NEW) { @@ -1969,6 +1970,9 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, } } spin_unlock(&priv->stream_lock); + } else { + qos &= ~MWL8K_QOS_ACK_POLICY_MASK; + qos |= MWL8K_QOS_ACK_POLICY_NORMAL; } dma = pci_map_single(priv->pdev, skb->data, From 7fb978b7e93b5c4a378eba5767c7513540b56642 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 3 Jan 2013 13:22:56 +0530 Subject: [PATCH 226/478] mwl8k: Enable support to support additional ampdu streams Currently, we have 2 ampdu streams that can be created simultaneously. Firmware is capable of supporting additional streams. Add support to use these streams. Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 7df18faae1b6..9e08fc601bb4 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -101,6 +101,18 @@ MODULE_PARM_DESC(ap_mode_default, #define MWL8K_MAX_TX_QUEUES (MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES) #define mwl8k_tx_queues(priv) (MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues) +/* txpriorities are mapped with hw queues. + * Each hw queue has a txpriority. + */ +#define TOTAL_HW_TX_QUEUES 8 + +/* Each HW queue can have one AMPDU stream. + * But, because one of the hw queue is reserved, + * maximum AMPDU queues that can be created are + * one short of total tx queues. + */ +#define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1) + struct rxd_ops { int rxd_size; void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); @@ -1733,7 +1745,7 @@ mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid) struct mwl8k_priv *priv = hw->priv; int i; - for (i = 0; i < priv->num_ampdu_queues; i++) { + for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { stream = &priv->ampdu[i]; if (stream->state == AMPDU_NO_STREAM) { stream->sta = sta; @@ -1780,7 +1792,7 @@ mwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid) struct mwl8k_priv *priv = hw->priv; int i; - for (i = 0 ; i < priv->num_ampdu_queues; i++) { + for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { struct mwl8k_ampdu_stream *stream; stream = &priv->ampdu[i]; if (stream->state == AMPDU_NO_STREAM) @@ -1827,6 +1839,13 @@ static inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid) tx_stats->pkts++; } +/* The hardware ampdu queues start from 5. + * txpriorities for ampdu queues are + * 5 6 7 0 1 2 3 4 ie., queue 5 is highest + * and queue 3 is lowest (queue 4 is reserved) + */ +#define BA_QUEUE 5 + static void mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, @@ -1927,8 +1946,12 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, if (stream != NULL) { if (stream->state == AMPDU_STREAM_ACTIVE) { WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK)); - txpriority = stream->idx + MWL8K_TX_WMM_QUEUES; - index = stream->idx + MWL8K_TX_WMM_QUEUES; + txpriority = (BA_QUEUE + stream->idx) % + TOTAL_HW_TX_QUEUES; + if (stream->idx <= 1) + index = stream->idx + + MWL8K_TX_WMM_QUEUES; + } else if (stream->state == AMPDU_STREAM_NEW) { /* We get here if the driver sends us packets * after we've initiated a stream, but before From 07f6dda1e7ed653c87ed329c99d6ae12ee114d52 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 3 Jan 2013 13:23:33 +0530 Subject: [PATCH 227/478] mwl8k: changing mwl8k_destroy_ba prototype Currently, mwl8k_ampdu_stream is passed as one of the argument to the function mwl8k_destroy_ba. Instead of this pass only the stream index. This will be helpful during watchdog event handling when bitmap of stream indices are received. Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 9e08fc601bb4..76b2f9133159 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3788,7 +3788,7 @@ mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, } static void mwl8k_destroy_ba(struct ieee80211_hw *hw, - struct mwl8k_ampdu_stream *stream) + u8 idx) { struct mwl8k_cmd_bastream *cmd; @@ -3800,10 +3800,10 @@ static void mwl8k_destroy_ba(struct ieee80211_hw *hw, cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_BA_DESTROY); - cmd->destroy_params.ba_context = cpu_to_le32(stream->idx); + cmd->destroy_params.ba_context = cpu_to_le32(idx); mwl8k_post_cmd(hw, &cmd->header); - wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", stream->idx); + wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", idx); kfree(cmd); } @@ -5117,7 +5117,7 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int i, rc = 0; struct mwl8k_priv *priv = hw->priv; struct mwl8k_ampdu_stream *stream; - u8 *addr = sta->addr; + u8 *addr = sta->addr, idx; struct mwl8k_sta *sta_info = MWL8K_STA(sta); if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) @@ -5200,8 +5200,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: if (stream) { if (stream->state == AMPDU_STREAM_ACTIVE) { + idx = stream->idx; spin_unlock(&priv->stream_lock); - mwl8k_destroy_ba(hw, stream); + mwl8k_destroy_ba(hw, idx); spin_lock(&priv->stream_lock); } mwl8k_remove_stream(hw, stream); @@ -5217,8 +5218,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!rc) stream->state = AMPDU_STREAM_ACTIVE; else { + idx = stream->idx; spin_unlock(&priv->stream_lock); - mwl8k_destroy_ba(hw, stream); + mwl8k_destroy_ba(hw, idx); spin_lock(&priv->stream_lock); wiphy_debug(hw->wiphy, "Failed adding stream for sta %pM tid %d\n", From cfacba12f573abd04a202e3e86c09f246339071f Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 3 Jan 2013 13:24:03 +0530 Subject: [PATCH 228/478] mwl8k: Handle Watchdog events for the new ampdu streams With more ampdu streams, we need to handle watchdog events for the new ampdu streams. Handle these events appropriately. Earlier mwl8k_cmd_get_watchdog_bitmap used to return only one stream index and hence bitwise operations on the return value were not required. Now the function returns a bitmap with different bits are mapped with different stream indices. Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 44 ++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 76b2f9133159..19afd2cdaced 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3603,7 +3603,11 @@ static int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap) return rc; } -#define INVALID_BA 0xAA +#define MWL8K_WMM_QUEUE_NUMBER 3 + +static void mwl8k_destroy_ba(struct ieee80211_hw *hw, + u8 idx); + static void mwl8k_watchdog_ba_events(struct work_struct *work) { int rc; @@ -3611,24 +3615,40 @@ static void mwl8k_watchdog_ba_events(struct work_struct *work) struct mwl8k_ampdu_stream *streams; struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, watchdog_ba_handle); + struct ieee80211_hw *hw = priv->hw; + int i; + u32 status = 0; + + mwl8k_fw_lock(hw); rc = mwl8k_cmd_get_watchdog_bitmap(priv->hw, &bitmap); if (rc) - return; + goto done; - if (bitmap == INVALID_BA) - return; + spin_lock(&priv->stream_lock); /* the bitmap is the hw queue number. Map it to the ampdu queue. */ - stream_index = bitmap - MWL8K_TX_WMM_QUEUES; - - BUG_ON(stream_index >= priv->num_ampdu_queues); - - streams = &priv->ampdu[stream_index]; - - if (streams->state == AMPDU_STREAM_ACTIVE) - ieee80211_stop_tx_ba_session(streams->sta, streams->tid); + for (i = 0; i < TOTAL_HW_TX_QUEUES; i++) { + if (bitmap & (1 << i)) { + stream_index = (i + MWL8K_WMM_QUEUE_NUMBER) % + TOTAL_HW_TX_QUEUES; + streams = &priv->ampdu[stream_index]; + if (streams->state == AMPDU_STREAM_ACTIVE) { + ieee80211_stop_tx_ba_session(streams->sta, + streams->tid); + spin_unlock(&priv->stream_lock); + mwl8k_destroy_ba(hw, stream_index); + spin_lock(&priv->stream_lock); + } + } + } + spin_unlock(&priv->stream_lock); +done: + status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG), + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + mwl8k_fw_unlock(hw); return; } From c27a54d3f12383789b57a404649daf98a05cbd72 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 3 Jan 2013 13:24:19 +0530 Subject: [PATCH 229/478] mwl8k: Handle watchdog event with highest prioriry Currently, command path waits till all the tx host queues are empty. Whenever watchdog event is raised, firmware expects driver to destroy ampdu queues immediately. This requires corresponding commands i.e. mwl8k_cmd_get_watchdog_bitmap and mwl8k_destroy_ba to be sent without waiting for the tx queues to be completely empty. Use "watchdog_event_pending" to ensure the above mentioned two commands are sent down immediately. Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 19afd2cdaced..3b046cc81cdd 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -213,6 +213,8 @@ struct mwl8k_priv { int fw_mutex_depth; struct completion *hostcmd_wait; + atomic_t watchdog_event_pending; + /* lock held over TX and TX reap */ spinlock_t tx_lock; @@ -1527,6 +1529,9 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) return -EBUSY; } + if (atomic_read(&priv->watchdog_event_pending)) + return 0; + /* * The TX queues are stopped at this point, so this test * doesn't need to take ->tx_lock. @@ -1548,6 +1553,14 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) spin_unlock_bh(&priv->tx_lock); timeout = wait_for_completion_timeout(&tx_wait, msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS)); + + if (atomic_read(&priv->watchdog_event_pending)) { + spin_lock_bh(&priv->tx_lock); + priv->tx_wait = NULL; + spin_unlock_bh(&priv->tx_lock); + return 0; + } + spin_lock_bh(&priv->tx_lock); if (timeout) { @@ -3645,6 +3658,7 @@ static void mwl8k_watchdog_ba_events(struct work_struct *work) spin_unlock(&priv->stream_lock); done: + atomic_dec(&priv->watchdog_event_pending); status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG), priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); @@ -4346,6 +4360,10 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) } if (status & MWL8K_A2H_INT_BA_WATCHDOG) { + iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + + atomic_inc(&priv->watchdog_event_pending); status &= ~MWL8K_A2H_INT_BA_WATCHDOG; ieee80211_queue_work(hw, &priv->watchdog_ba_handle); } @@ -5520,6 +5538,7 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw) priv->sniffer_enabled = false; priv->wmm_enabled = false; priv->pending_tx_pkts = 0; + atomic_set(&priv->watchdog_event_pending, 0); rc = mwl8k_rxq_init(hw, 0); if (rc) From c2f2e2029bea2a8a759b2bc2a44f4d41539af236 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Thu, 3 Jan 2013 13:24:44 +0530 Subject: [PATCH 230/478] mwl8k: bump firmware API to 3 New firmware is required to support a) Add support for additional ampdu streams b) Handle corresponding watchdog events to destroy ampdu streams Bumping firmware API version by one. Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 3b046cc81cdd..c4d287b48e64 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5321,7 +5321,7 @@ enum { MWL8366, }; -#define MWL8K_8366_AP_FW_API 2 +#define MWL8K_8366_AP_FW_API 3 #define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw" #define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api) From 03c2dc73581ad4ae54f1ccfc1463929b16b8b736 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Thu, 3 Jan 2013 14:55:48 +0530 Subject: [PATCH 231/478] ath9k_hw: Fix calibration for AR9340 TX_IQ_ON_AGC_CAL should not be enabled for AR9340. TX-IQ calibration is run as part of AGC calibration only for AR9485, AR9462 and AR9565. For the others (AR9300, AR9330, AR9340), TX-IQ cal is done independent of AGC-cal. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index a562a5d47eab..fa67e84d51ea 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -319,7 +319,7 @@ static void ar9003_hw_init_cal_settings(struct ath_hw *ah) if (AR_SREV_9300_20_OR_LATER(ah)) { ah->enabled_cals |= TX_IQ_CAL; - if (AR_SREV_9485_OR_LATER(ah)) + if (AR_SREV_9485_OR_LATER(ah) && !AR_SREV_9340(ah)) ah->enabled_cals |= TX_IQ_ON_AGC_CAL; } From 929a03aeb149017e88fbe5878c2b6585cfda8704 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Fri, 4 Jan 2013 00:51:20 +0100 Subject: [PATCH 232/478] bcma: make bcma_find_core_unit() accessible This bcma_find_core_unit() is needed by the mips driver in the next patch. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/bcma_private.h | 2 ++ drivers/bcma/main.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 4a2d72ec6d43..d35294e81d15 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -31,6 +31,8 @@ int __init bcma_bus_early_register(struct bcma_bus *bus, int bcma_bus_suspend(struct bcma_bus *bus); int bcma_bus_resume(struct bcma_bus *bus); #endif +struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid, + u8 unit); /* scan.c */ int bcma_bus_scan(struct bcma_bus *bus); diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 53ba20ca17e0..d12b7da556e1 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -81,8 +81,8 @@ struct bcma_device *bcma_find_core(struct bcma_bus *bus, u16 coreid) } EXPORT_SYMBOL_GPL(bcma_find_core); -static struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid, - u8 unit) +struct bcma_device *bcma_find_core_unit(struct bcma_bus *bus, u16 coreid, + u8 unit) { struct bcma_device *core; From e3f05a42faac627d8704c76c1927e09b22443b7b Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Fri, 4 Jan 2013 00:51:21 +0100 Subject: [PATCH 233/478] bcma: mips: explicit assign IRQ numbers The assignment of the IRQs to the cores of the chips by iterating over the cores is complicated and causes problems with SoC like the BCM4706 with two GMAC core where just one should get a dedicated IRQ number. Now the code assigns the same IRQs to the cores as the code from the Broadcom SDK does. If the SoC is not know the current assigned IRQs are only read out and an error message is printed. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_mips.c | 84 +++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 792daad28cbc..171be0f541e0 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -147,6 +147,22 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) dev->id.id, oldirq + 2, irq + 2); } +static void bcma_core_mips_set_irq_name(struct bcma_bus *bus, unsigned int irq, + u16 coreid, u8 unit) +{ + struct bcma_device *core; + + core = bcma_find_core_unit(bus, coreid, unit); + if (!core) { + bcma_warn(bus, + "Can not find core (id: 0x%x, unit %i) for IRQ configuration.\n", + coreid, unit); + return; + } + + bcma_core_mips_set_irq(core, irq); +} + static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq) { int i; @@ -242,35 +258,47 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore) mcore->assigned_irqs = 1; - /* Assign IRQs to all cores on the bus */ - list_for_each_entry(core, &bus->cores, list) { - int mips_irq; - if (core->irq) - continue; - - mips_irq = bcma_core_mips_irq(core); - if (mips_irq > 4) - core->irq = 0; - else - core->irq = mips_irq + 2; - if (core->irq > 5) - continue; - switch (core->id.id) { - case BCMA_CORE_PCI: - case BCMA_CORE_PCIE: - case BCMA_CORE_ETHERNET: - case BCMA_CORE_ETHERNET_GBIT: - case BCMA_CORE_MAC_GBIT: - case BCMA_CORE_80211: - case BCMA_CORE_USB20_HOST: - /* These devices get their own IRQ line if available, - * the rest goes on IRQ0 - */ - if (mcore->assigned_irqs <= 4) - bcma_core_mips_set_irq(core, - mcore->assigned_irqs++); - break; + switch (bus->chipinfo.id) { + case BCMA_CHIP_ID_BCM4716: + case BCMA_CHIP_ID_BCM4748: + bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); + bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); + bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_USB20_HOST, 0); + bcma_core_mips_set_irq_name(bus, 4, BCMA_CORE_PCIE, 0); + bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); + bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_I2S, 0); + break; + case BCMA_CHIP_ID_BCM5356: + case BCMA_CHIP_ID_BCM47162: + case BCMA_CHIP_ID_BCM53572: + bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); + bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); + bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); + break; + case BCMA_CHIP_ID_BCM5357: + case BCMA_CHIP_ID_BCM4749: + bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); + bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); + bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_USB20_HOST, 0); + bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); + bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_I2S, 0); + break; + case BCMA_CHIP_ID_BCM4706: + bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_PCIE, 0); + bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_4706_MAC_GBIT, + 0); + bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_PCIE, 1); + bcma_core_mips_set_irq_name(bus, 4, BCMA_CORE_USB20_HOST, 0); + bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_4706_CHIPCOMMON, + 0); + break; + default: + list_for_each_entry(core, &bus->cores, list) { + core->irq = bcma_core_irq(core); } + bcma_err(bus, + "Unknown device (0x%x) found, can not configure IRQs\n", + bus->chipinfo.id); } bcma_info(bus, "IRQ reconfiguration done\n"); bcma_core_mips_dump_irq(bus); From 7401cb63d00994dbdfdc2916b949b22e27475436 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Fri, 4 Jan 2013 00:51:22 +0100 Subject: [PATCH 234/478] bcma: mips: make some info messages debug messages Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_mips.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 171be0f541e0..486aa2dba581 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -143,8 +143,8 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) 1 << irqflag); } - bcma_info(bus, "set_irq: core 0x%04x, irq %d => %d\n", - dev->id.id, oldirq + 2, irq + 2); + bcma_debug(bus, "set_irq: core 0x%04x, irq %d => %d\n", + dev->id.id, oldirq + 2, irq + 2); } static void bcma_core_mips_set_irq_name(struct bcma_bus *bus, unsigned int irq, @@ -167,7 +167,7 @@ static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq) { int i; static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"}; - printk(KERN_INFO KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id); + printk(KERN_DEBUG KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id); for (i = 0; i <= 6; i++) printk(" %s%s", irq_name[i], i == irq ? "*" : " "); printk("\n"); @@ -252,7 +252,7 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore) if (mcore->setup_done) return; - bcma_info(bus, "Initializing MIPS core...\n"); + bcma_debug(bus, "Initializing MIPS core...\n"); bcma_core_mips_early_init(mcore); @@ -300,7 +300,7 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore) "Unknown device (0x%x) found, can not configure IRQs\n", bus->chipinfo.id); } - bcma_info(bus, "IRQ reconfiguration done\n"); + bcma_debug(bus, "IRQ reconfiguration done\n"); bcma_core_mips_dump_irq(bus); mcore->setup_done = true; From db5230d1b3c5f7c62564de2ef07ea1c633548959 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Fri, 4 Jan 2013 00:51:23 +0100 Subject: [PATCH 235/478] bcma: mips: show also disabled IRQs Some cores do not have a IRQ assigned and some do not support when an IRQ is assigned to them, this is now handled and printed out in the code. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_mips.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 486aa2dba581..69815079a6dd 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -74,11 +74,16 @@ static u32 bcma_core_mips_irqflag(struct bcma_device *dev) return dev->core_index; flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30); - return flag & 0x1F; + if (flag) + return flag & 0x1F; + else + return 0x3f; } /* Get the MIPS IRQ assignment for a specified device. * If unassigned, 0 is returned. + * If disabled, 5 is returned. + * If not supported, 6 is returned. */ unsigned int bcma_core_mips_irq(struct bcma_device *dev) { @@ -87,13 +92,15 @@ unsigned int bcma_core_mips_irq(struct bcma_device *dev) unsigned int irq; irqflag = bcma_core_mips_irqflag(dev); + if (irqflag == 0x3f) + return 6; - for (irq = 1; irq <= 4; irq++) + for (irq = 0; irq <= 4; irq++) if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) & (1 << irqflag)) return irq; - return 0; + return 5; } EXPORT_SYMBOL(bcma_core_mips_irq); @@ -114,7 +121,7 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0), bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) & ~(1 << irqflag)); - else + else if (oldirq != 5) bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(oldirq), 0); /* assign the new one */ @@ -144,7 +151,7 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) } bcma_debug(bus, "set_irq: core 0x%04x, irq %d => %d\n", - dev->id.id, oldirq + 2, irq + 2); + dev->id.id, oldirq <= 4 ? oldirq + 2 : 0, irq + 2); } static void bcma_core_mips_set_irq_name(struct bcma_bus *bus, unsigned int irq, From 8f9dc85348ac37ff3b6b031d22e93a5b59d81f83 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Fri, 4 Jan 2013 00:51:24 +0100 Subject: [PATCH 236/478] bcma: mips: remove assigned_irqs from structure This member is not needed any more. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_mips.c | 2 -- include/linux/bcma/bcma_driver_mips.h | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 69815079a6dd..c6d7be33b972 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -263,8 +263,6 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore) bcma_core_mips_early_init(mcore); - mcore->assigned_irqs = 1; - switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4716: case BCMA_CHIP_ID_BCM4748: diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h index 0baf8a56b794..6495579e3f35 100644 --- a/include/linux/bcma/bcma_driver_mips.h +++ b/include/linux/bcma/bcma_driver_mips.h @@ -36,7 +36,6 @@ struct bcma_drv_mips { struct bcma_device *core; u8 setup_done:1; u8 early_setup_done:1; - unsigned int assigned_irqs; }; #ifdef CONFIG_BCMA_DRIVER_MIPS From 6ba1eafed183c6e0886d3483c8acac4efe1e95db Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Fri, 4 Jan 2013 00:51:25 +0100 Subject: [PATCH 237/478] bcma: mips: rename oldirqflag to irqinitmask The new name better matches the use of this variable. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_mips.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index c6d7be33b972..8a51c7950536 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -130,9 +130,9 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) | (1 << irqflag)); } else { - u32 oldirqflag = bcma_read32(mdev, - BCMA_MIPS_MIPS74K_INTMASK(irq)); - if (oldirqflag) { + u32 irqinitmask = bcma_read32(mdev, + BCMA_MIPS_MIPS74K_INTMASK(irq)); + if (irqinitmask) { struct bcma_device *core; /* backplane irq line is in use, find out who uses @@ -140,7 +140,7 @@ static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) */ list_for_each_entry(core, &bus->cores, list) { if ((1 << bcma_core_mips_irqflag(core)) == - oldirqflag) { + irqinitmask) { bcma_core_mips_set_irq(core, 0); break; } From 659c4788611f725ede2def6e4db5d8d4a59b93b5 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Thu, 3 Jan 2013 21:21:28 -0800 Subject: [PATCH 238/478] mwifiex: access interrupt status only while holding lock This patch fixes a bug for few instances where PCIe interrupt status variable is accessed without holding spin lock. This can result into missing interrupts. Fix this by copying interrupt status to a local variable and then using it for calling specific routine. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/pcie.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 13fbc4eb1595..e4c2cfdd049c 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1594,39 +1594,40 @@ exit: static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) { int ret; - u32 pcie_ireg = 0; + u32 pcie_ireg; unsigned long flags; spin_lock_irqsave(&adapter->int_lock, flags); /* Clear out unused interrupts */ - adapter->int_status &= HOST_INTR_MASK; + pcie_ireg = adapter->int_status; + adapter->int_status = 0; spin_unlock_irqrestore(&adapter->int_lock, flags); - while (adapter->int_status & HOST_INTR_MASK) { - if (adapter->int_status & HOST_INTR_DNLD_DONE) { - adapter->int_status &= ~HOST_INTR_DNLD_DONE; + while (pcie_ireg & HOST_INTR_MASK) { + if (pcie_ireg & HOST_INTR_DNLD_DONE) { + pcie_ireg &= ~HOST_INTR_DNLD_DONE; if (adapter->data_sent) { dev_dbg(adapter->dev, "info: DATA sent intr\n"); adapter->data_sent = false; } } - if (adapter->int_status & HOST_INTR_UPLD_RDY) { - adapter->int_status &= ~HOST_INTR_UPLD_RDY; + if (pcie_ireg & HOST_INTR_UPLD_RDY) { + pcie_ireg &= ~HOST_INTR_UPLD_RDY; dev_dbg(adapter->dev, "info: Rx DATA\n"); ret = mwifiex_pcie_process_recv_data(adapter); if (ret) return ret; } - if (adapter->int_status & HOST_INTR_EVENT_RDY) { - adapter->int_status &= ~HOST_INTR_EVENT_RDY; + if (pcie_ireg & HOST_INTR_EVENT_RDY) { + pcie_ireg &= ~HOST_INTR_EVENT_RDY; dev_dbg(adapter->dev, "info: Rx EVENT\n"); ret = mwifiex_pcie_process_event_ready(adapter); if (ret) return ret; } - if (adapter->int_status & HOST_INTR_CMD_DONE) { - adapter->int_status &= ~HOST_INTR_CMD_DONE; + if (pcie_ireg & HOST_INTR_CMD_DONE) { + pcie_ireg &= ~HOST_INTR_CMD_DONE; if (adapter->cmd_sent) { dev_dbg(adapter->dev, "info: CMD sent Interrupt\n"); @@ -1654,8 +1655,6 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) "Write register failed\n"); return -1; } - adapter->int_status |= pcie_ireg; - adapter->int_status &= HOST_INTR_MASK; } } From c6d1d87a10d1e5317701676c39bbec20e4651b91 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Thu, 3 Jan 2013 21:21:29 -0800 Subject: [PATCH 239/478] mwifiex: add init_fw_port handler for PCIe interface This patch adds support for init_fw_port handler for PCIe interface, which resets RXBD read pointer for PCIe. This fixes issue where RX doesn't work until some TX from driver happens. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 6 ++++++ drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/pcie.c | 20 ++++++++++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 39f03ce5a5b1..e00b8060aff7 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -591,6 +591,12 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter) return -1; } } + + if (adapter->if_ops.init_fw_port) { + if (adapter->if_ops.init_fw_port(adapter)) + return -1; + } + for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index d717c9859fb9..ef02a5bf57b1 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -599,6 +599,7 @@ struct mwifiex_if_ops { int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*data_complete) (struct mwifiex_adapter *, struct sk_buff *); + int (*init_fw_port) (struct mwifiex_adapter *); int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); void (*card_reset) (struct mwifiex_adapter *); }; diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index e4c2cfdd049c..191c6825a6b6 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -966,8 +966,23 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) return 0; } -/* - * This function downloads commands to the device +/* This function init rx port in firmware which in turn enables to receive data + * from device before transmitting any packet. + */ +static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + + /* Write the RX ring read pointer in to REG_RXBD_RDPTR */ + if (mwifiex_write_reg(adapter, REG_RXBD_RDPTR, card->rxbd_rdptr | 0)) { + dev_err(adapter->dev, + "RECV DATA: failed to write REG_RXBD_RDPTR\n"); + return -1; + } + return 0; +} + +/* This function downloads commands to the device */ static int mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) @@ -1899,6 +1914,7 @@ static struct mwifiex_if_ops pcie_ops = { .event_complete = mwifiex_pcie_event_complete, .update_mp_end_port = NULL, .cleanup_mpa_buf = NULL, + .init_fw_port = mwifiex_pcie_init_fw_port, }; /* From fc3314609047daf472b3ffc49f9a1c5608068713 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Thu, 3 Jan 2013 21:21:30 -0800 Subject: [PATCH 240/478] mwifiex: use pci_alloc/free_consistent APIs for PCIe This patch uses pci_alloc_consistent and pci_free_consistent APIs for mwifiex_pcie driver. Consistent DMA memory is allocated for TX, RX and event rings. Command buffer and command response buffer also uses map/unmap memory APIs to download commands and get command responses. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/pcie.c | 362 +++++++++++++++---------- drivers/net/wireless/mwifiex/pcie.h | 9 +- drivers/net/wireless/mwifiex/sta_cmd.c | 10 +- drivers/net/wireless/mwifiex/util.h | 8 +- 4 files changed, 235 insertions(+), 154 deletions(-) diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 191c6825a6b6..ac20bc4bc1a0 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -39,17 +39,20 @@ static struct semaphore add_remove_card_sem; static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter); static int mwifiex_pcie_resume(struct pci_dev *pdev); -/* - * This function is called after skb allocation to update - * "skb->cb" with physical address of data pointer. - */ -static phys_addr_t *mwifiex_update_sk_buff_pa(struct sk_buff *skb) +static int +mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, + int size, int flags) { - phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb); + struct pcie_service_card *card = adapter->card; + dma_addr_t buf_pa; - *buf_pa = (phys_addr_t)virt_to_phys(skb->data); - - return buf_pa; + buf_pa = pci_map_single(card->dev, skb->data, size, flags); + if (pci_dma_mapping_error(card->dev, buf_pa)) { + dev_err(adapter->dev, "failed to map pci memory!\n"); + return -1; + } + memcpy(skb->cb, &buf_pa, sizeof(dma_addr_t)); + return 0; } /* @@ -60,8 +63,8 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) u32 *cookie_addr; struct pcie_service_card *card = adapter->card; - if (card->sleep_cookie) { - cookie_addr = (u32 *)card->sleep_cookie->data; + if (card->sleep_cookie_vbase) { + cookie_addr = (u32 *)card->sleep_cookie_vbase; dev_dbg(adapter->dev, "info: ACCESS_HW: sleep cookie=0x%x\n", *cookie_addr); if (*cookie_addr == FW_AWAKE_COOKIE) @@ -366,9 +369,7 @@ static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter) static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; - struct sk_buff *skb; int i; - phys_addr_t *buf_pa; /* * driver maintaines the write pointer and firmware maintaines the read @@ -384,16 +385,18 @@ static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) MWIFIEX_MAX_TXRX_BD; dev_dbg(adapter->dev, "info: txbd_ring: Allocating %d bytes\n", card->txbd_ring_size); - card->txbd_ring_vbase = kzalloc(card->txbd_ring_size, GFP_KERNEL); + card->txbd_ring_vbase = pci_alloc_consistent(card->dev, + card->txbd_ring_size, + &card->txbd_ring_pbase); if (!card->txbd_ring_vbase) { - dev_err(adapter->dev, "Unable to alloc buffer for txbd ring\n"); + dev_err(adapter->dev, + "allocate consistent memory (%d bytes) failed!\n", + card->txbd_ring_size); return -ENOMEM; } - card->txbd_ring_pbase = virt_to_phys(card->txbd_ring_vbase); - dev_dbg(adapter->dev, "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n", - card->txbd_ring_vbase, (u32)card->txbd_ring_pbase, + card->txbd_ring_vbase, (unsigned int)card->txbd_ring_pbase, (u32)((u64)card->txbd_ring_pbase >> 32), card->txbd_ring_size); for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { @@ -402,24 +405,9 @@ static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) (sizeof(struct mwifiex_pcie_buf_desc) * i)); - /* Allocate buffer here so that firmware can DMA data from it */ - skb = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE); - if (!skb) { - dev_err(adapter->dev, "Unable to allocate skb for TX ring.\n"); - kfree(card->txbd_ring_vbase); - return -ENOMEM; - } - buf_pa = mwifiex_update_sk_buff_pa(skb); - - skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE); - dev_dbg(adapter->dev, "info: TX ring: add new skb base: %p, " - "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n", - skb, skb->data, (u32)*buf_pa, - (u32)(((u64)*buf_pa >> 32)), skb->len); - - card->tx_buf_list[i] = skb; - card->txbd_ring[i]->paddr = *buf_pa; - card->txbd_ring[i]->len = (u16)skb->len; + card->tx_buf_list[i] = NULL; + card->txbd_ring[i]->paddr = 0; + card->txbd_ring[i]->len = 0; card->txbd_ring[i]->flags = 0; } @@ -429,11 +417,16 @@ static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; + struct sk_buff *skb; int i; for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - if (card->tx_buf_list[i]) - dev_kfree_skb_any(card->tx_buf_list[i]); + if (card->tx_buf_list[i]) { + skb = card->tx_buf_list[i]; + pci_unmap_single(card->dev, card->txbd_ring[i]->paddr, + skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb_any(skb); + } card->tx_buf_list[i] = NULL; card->txbd_ring[i]->paddr = 0; card->txbd_ring[i]->len = 0; @@ -441,11 +434,15 @@ static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) card->txbd_ring[i] = NULL; } - kfree(card->txbd_ring_vbase); + if (card->txbd_ring_vbase) + pci_free_consistent(card->dev, card->txbd_ring_size, + card->txbd_ring_vbase, + card->txbd_ring_pbase); card->txbd_ring_size = 0; card->txbd_wrptr = 0; card->txbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND; card->txbd_ring_vbase = NULL; + card->txbd_ring_pbase = 0; return 0; } @@ -458,7 +455,7 @@ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) struct pcie_service_card *card = adapter->card; struct sk_buff *skb; int i; - phys_addr_t *buf_pa; + dma_addr_t buf_pa; /* * driver maintaines the read pointer and firmware maintaines the write @@ -472,13 +469,15 @@ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) MWIFIEX_MAX_TXRX_BD; dev_dbg(adapter->dev, "info: rxbd_ring: Allocating %d bytes\n", card->rxbd_ring_size); - card->rxbd_ring_vbase = kzalloc(card->rxbd_ring_size, GFP_KERNEL); + card->rxbd_ring_vbase = pci_alloc_consistent(card->dev, + card->rxbd_ring_size, + &card->rxbd_ring_pbase); if (!card->rxbd_ring_vbase) { - dev_err(adapter->dev, "Unable to allocate buffer for " - "rxbd_ring.\n"); + dev_err(adapter->dev, + "allocate consistent memory (%d bytes) failed!\n", + card->rxbd_ring_size); return -ENOMEM; } - card->rxbd_ring_pbase = virt_to_phys(card->rxbd_ring_vbase); dev_dbg(adapter->dev, "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n", @@ -500,16 +499,20 @@ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) kfree(card->rxbd_ring_vbase); return -ENOMEM; } - buf_pa = mwifiex_update_sk_buff_pa(skb); - skb_put(skb, MWIFIEX_RX_DATA_BUF_SIZE); + if (mwifiex_map_pci_memory(adapter, skb, + MWIFIEX_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + MWIFIEX_SKB_PACB(skb, &buf_pa); dev_dbg(adapter->dev, "info: RX ring: add new skb base: %p, " "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n", - skb, skb->data, (u32)*buf_pa, (u32)((u64)*buf_pa >> 32), + skb, skb->data, (u32)buf_pa, (u32)((u64)buf_pa >> 32), skb->len); card->rx_buf_list[i] = skb; - card->rxbd_ring[i]->paddr = *buf_pa; + card->rxbd_ring[i]->paddr = buf_pa; card->rxbd_ring[i]->len = (u16)skb->len; card->rxbd_ring[i]->flags = 0; } @@ -523,11 +526,17 @@ static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; + struct sk_buff *skb; int i; for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - if (card->rx_buf_list[i]) - dev_kfree_skb_any(card->rx_buf_list[i]); + if (card->rx_buf_list[i]) { + skb = card->rx_buf_list[i]; + pci_unmap_single(card->dev, card->rxbd_ring[i]->paddr , + MWIFIEX_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + } card->rx_buf_list[i] = NULL; card->rxbd_ring[i]->paddr = 0; card->rxbd_ring[i]->len = 0; @@ -535,11 +544,15 @@ static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) card->rxbd_ring[i] = NULL; } - kfree(card->rxbd_ring_vbase); + if (card->rxbd_ring_vbase) + pci_free_consistent(card->dev, card->rxbd_ring_size, + card->rxbd_ring_vbase, + card->rxbd_ring_pbase); card->rxbd_ring_size = 0; card->rxbd_wrptr = 0; card->rxbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND; card->rxbd_ring_vbase = NULL; + card->rxbd_ring_pbase = 0; return 0; } @@ -552,7 +565,7 @@ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) struct pcie_service_card *card = adapter->card; struct sk_buff *skb; int i; - phys_addr_t *buf_pa; + dma_addr_t buf_pa; /* * driver maintaines the read pointer and firmware maintaines the write @@ -566,13 +579,15 @@ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) MWIFIEX_MAX_EVT_BD; dev_dbg(adapter->dev, "info: evtbd_ring: Allocating %d bytes\n", card->evtbd_ring_size); - card->evtbd_ring_vbase = kzalloc(card->evtbd_ring_size, GFP_KERNEL); + card->evtbd_ring_vbase = pci_alloc_consistent(card->dev, + card->evtbd_ring_size, + &card->evtbd_ring_pbase); if (!card->evtbd_ring_vbase) { dev_err(adapter->dev, - "Unable to allocate buffer. Terminating download\n"); + "allocate consistent memory (%d bytes) failed!\n", + card->evtbd_ring_size); return -ENOMEM; } - card->evtbd_ring_pbase = virt_to_phys(card->evtbd_ring_vbase); dev_dbg(adapter->dev, "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n", @@ -594,16 +609,20 @@ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) kfree(card->evtbd_ring_vbase); return -ENOMEM; } - buf_pa = mwifiex_update_sk_buff_pa(skb); skb_put(skb, MAX_EVENT_SIZE); + if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + MWIFIEX_SKB_PACB(skb, &buf_pa); dev_dbg(adapter->dev, "info: Evt ring: add new skb. base: %p, " "buf_base: %p, buf_pbase: %#x:%x, buf_len: %#x\n", - skb, skb->data, (u32)*buf_pa, (u32)((u64)*buf_pa >> 32), + skb, skb->data, (u32)buf_pa, (u32)((u64)buf_pa >> 32), skb->len); card->evt_buf_list[i] = skb; - card->evtbd_ring[i]->paddr = *buf_pa; + card->evtbd_ring[i]->paddr = buf_pa; card->evtbd_ring[i]->len = (u16)skb->len; card->evtbd_ring[i]->flags = 0; } @@ -617,11 +636,16 @@ static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; + struct sk_buff *skb; int i; for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { - if (card->evt_buf_list[i]) - dev_kfree_skb_any(card->evt_buf_list[i]); + if (card->evt_buf_list[i]) { + skb = card->evt_buf_list[i]; + pci_unmap_single(card->dev, card->evtbd_ring[i]->paddr, + MAX_EVENT_SIZE, PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + } card->evt_buf_list[i] = NULL; card->evtbd_ring[i]->paddr = 0; card->evtbd_ring[i]->len = 0; @@ -629,11 +653,15 @@ static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) card->evtbd_ring[i] = NULL; } - kfree(card->evtbd_ring_vbase); + if (card->evtbd_ring_vbase) + pci_free_consistent(card->dev, card->evtbd_ring_size, + card->evtbd_ring_vbase, + card->evtbd_ring_pbase); card->evtbd_wrptr = 0; card->evtbd_rdptr = 0 | MWIFIEX_BD_FLAG_ROLLOVER_IND; card->evtbd_ring_size = 0; card->evtbd_ring_vbase = NULL; + card->evtbd_ring_pbase = 0; return 0; } @@ -653,21 +681,12 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) "Unable to allocate skb for command response data.\n"); return -ENOMEM; } - mwifiex_update_sk_buff_pa(skb); skb_put(skb, MWIFIEX_UPLD_SIZE); - card->cmdrsp_buf = skb; + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; - skb = NULL; - /* Allocate memory for sending command to firmware */ - skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); - if (!skb) { - dev_err(adapter->dev, - "Unable to allocate skb for command data.\n"); - return -ENOMEM; - } - mwifiex_update_sk_buff_pa(skb); - skb_put(skb, MWIFIEX_SIZE_OF_CMD_BUFFER); - card->cmd_buf = skb; + card->cmdrsp_buf = skb; return 0; } @@ -678,18 +697,26 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) { struct pcie_service_card *card; + dma_addr_t buf_pa; if (!adapter) return 0; card = adapter->card; - if (card && card->cmdrsp_buf) + if (card && card->cmdrsp_buf) { + MWIFIEX_SKB_PACB(card->cmdrsp_buf, &buf_pa); + pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(card->cmdrsp_buf); + } - if (card && card->cmd_buf) + if (card && card->cmd_buf) { + MWIFIEX_SKB_PACB(card->cmd_buf, &buf_pa); + pci_unmap_single(card->dev, buf_pa, MWIFIEX_SIZE_OF_CMD_BUFFER, + PCI_DMA_TODEVICE); dev_kfree_skb_any(card->cmd_buf); - + } return 0; } @@ -698,27 +725,19 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) */ static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) { - struct sk_buff *skb; struct pcie_service_card *card = adapter->card; - /* Allocate memory for sleep cookie */ - skb = dev_alloc_skb(sizeof(u32)); - if (!skb) { - dev_err(adapter->dev, - "Unable to allocate skb for sleep cookie!\n"); + card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32), + &card->sleep_cookie_pbase); + if (!card->sleep_cookie_vbase) { + dev_err(adapter->dev, "pci_alloc_consistent failed!\n"); return -ENOMEM; } - mwifiex_update_sk_buff_pa(skb); - skb_put(skb, sizeof(u32)); - /* Init val of Sleep Cookie */ - *(u32 *)skb->data = FW_AWAKE_COOKIE; + *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE; dev_dbg(adapter->dev, "alloc_scook: sleep cookie=0x%x\n", - *((u32 *)skb->data)); - - /* Save the sleep cookie */ - card->sleep_cookie = skb; + *((u32 *)card->sleep_cookie_vbase)); return 0; } @@ -735,9 +754,11 @@ static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter) card = adapter->card; - if (card && card->sleep_cookie) { - dev_kfree_skb_any(card->sleep_cookie); - card->sleep_cookie = NULL; + if (card && card->sleep_cookie_vbase) { + pci_free_consistent(card->dev, sizeof(u32), + card->sleep_cookie_vbase, + card->sleep_cookie_pbase); + card->sleep_cookie_vbase = NULL; } return 0; @@ -751,7 +772,7 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; u32 wrindx, rdptr; - phys_addr_t *buf_pa; + phys_addr_t buf_pa; __le16 *tmp; if (!mwifiex_pcie_ok_to_access_hw(adapter)) @@ -785,8 +806,8 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb) *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); skb_put(skb_data, MWIFIEX_RX_DATA_BUF_SIZE - skb_data->len); skb_trim(skb_data, skb->len); - buf_pa = MWIFIEX_SKB_PACB(skb_data); - card->txbd_ring[wrindx]->paddr = *buf_pa; + MWIFIEX_SKB_PACB(skb_data, &buf_pa); + card->txbd_ring[wrindx]->paddr = buf_pa; card->txbd_ring[wrindx]->len = (u16)skb_data->len; card->txbd_ring[wrindx]->flags = MWIFIEX_BD_FLAG_FIRST_DESC | MWIFIEX_BD_FLAG_LAST_DESC; @@ -918,32 +939,41 @@ done: static int mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) { - phys_addr_t *buf_pa = MWIFIEX_SKB_PACB(skb); + dma_addr_t buf_pa; + struct pcie_service_card *card = adapter->card; - if (!(skb->data && skb->len && *buf_pa)) { + if (!(skb->data && skb->len)) { dev_err(adapter->dev, - "Invalid parameter in %s <%p, %#x:%x, %x>\n", - __func__, skb->data, skb->len, - (u32)*buf_pa, (u32)((u64)*buf_pa >> 32)); + "Invalid parameter in %s <%p. len %d>\n", + __func__, skb->data, skb->len); return -1; } + if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE)) + return -1; + + MWIFIEX_SKB_PACB(skb, &buf_pa); + /* Write the lower 32bits of the physical address to scratch * register 0 */ - if (mwifiex_write_reg(adapter, PCIE_SCRATCH_0_REG, (u32)*buf_pa)) { + if (mwifiex_write_reg(adapter, PCIE_SCRATCH_0_REG, (u32)buf_pa)) { dev_err(adapter->dev, "%s: failed to write download command to boot code.\n", __func__); + pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, + PCI_DMA_TODEVICE); return -1; } /* Write the upper 32bits of the physical address to scratch * register 1 */ if (mwifiex_write_reg(adapter, PCIE_SCRATCH_1_REG, - (u32)((u64)*buf_pa >> 32))) { + (u32)((u64)buf_pa >> 32))) { dev_err(adapter->dev, "%s: failed to write download command to boot code.\n", __func__); + pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, + PCI_DMA_TODEVICE); return -1; } @@ -952,6 +982,8 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) dev_err(adapter->dev, "%s: failed to write command len to scratch reg 2\n", __func__); + pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, + PCI_DMA_TODEVICE); return -1; } @@ -960,6 +992,8 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) CPU_INTR_DOOR_BELL)) { dev_err(adapter->dev, "%s: failed to assert door-bell intr\n", __func__); + pci_unmap_single(card->dev, buf_pa, + MWIFIEX_UPLD_SIZE, PCI_DMA_TODEVICE); return -1; } @@ -989,8 +1023,8 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; int ret = 0; - phys_addr_t *cmd_buf_pa; - phys_addr_t *cmdrsp_buf_pa; + dma_addr_t cmd_buf_pa, cmdrsp_buf_pa; + u8 *payload = (u8 *)skb->data; if (!(skb->data && skb->len)) { dev_err(adapter->dev, "Invalid parameter in %s <%p, %#x>\n", @@ -1005,17 +1039,18 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) return -EBUSY; } - /* Make sure a command buffer is available */ - if (!card->cmd_buf) { - dev_err(adapter->dev, "Command buffer not available\n"); - return -EBUSY; - } + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); adapter->cmd_sent = true; - /* Copy the given skb in to DMA accessable shared buffer */ - skb_put(card->cmd_buf, MWIFIEX_SIZE_OF_CMD_BUFFER - card->cmd_buf->len); - skb_trim(card->cmd_buf, skb->len); - memcpy(card->cmd_buf->data, skb->data, skb->len); + + *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len); + *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD); + + if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) + return -1; + + card->cmd_buf = skb; /* To send a command, the driver will: 1. Write the 64bit physical address of the data buffer to @@ -1028,11 +1063,11 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) */ if (card->cmdrsp_buf) { - cmdrsp_buf_pa = MWIFIEX_SKB_PACB(card->cmdrsp_buf); + MWIFIEX_SKB_PACB(card->cmdrsp_buf, &cmdrsp_buf_pa); /* Write the lower 32bits of the cmdrsp buffer physical address */ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_LO, - (u32)*cmdrsp_buf_pa)) { + (u32)cmdrsp_buf_pa)) { dev_err(adapter->dev, "Failed to write download cmd to boot code.\n"); ret = -1; @@ -1041,7 +1076,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) /* Write the upper 32bits of the cmdrsp buffer physical address */ if (mwifiex_write_reg(adapter, REG_CMDRSP_ADDR_HI, - (u32)((u64)*cmdrsp_buf_pa >> 32))) { + (u32)((u64)cmdrsp_buf_pa >> 32))) { dev_err(adapter->dev, "Failed to write download cmd to boot code.\n"); ret = -1; @@ -1049,9 +1084,9 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) } } - cmd_buf_pa = MWIFIEX_SKB_PACB(card->cmd_buf); + MWIFIEX_SKB_PACB(card->cmd_buf, &cmd_buf_pa); /* Write the lower 32bits of the physical address to REG_CMD_ADDR_LO */ - if (mwifiex_write_reg(adapter, REG_CMD_ADDR_LO, (u32)*cmd_buf_pa)) { + if (mwifiex_write_reg(adapter, REG_CMD_ADDR_LO, (u32)cmd_buf_pa)) { dev_err(adapter->dev, "Failed to write download cmd to boot code.\n"); ret = -1; @@ -1059,7 +1094,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) } /* Write the upper 32bits of the physical address to REG_CMD_ADDR_HI */ if (mwifiex_write_reg(adapter, REG_CMD_ADDR_HI, - (u32)((u64)*cmd_buf_pa >> 32))) { + (u32)((u64)cmd_buf_pa >> 32))) { dev_err(adapter->dev, "Failed to write download cmd to boot code.\n"); ret = -1; @@ -1098,11 +1133,22 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) struct pcie_service_card *card = adapter->card; struct sk_buff *skb = card->cmdrsp_buf; int count = 0; + u16 rx_len; + __le16 pkt_len; + dma_addr_t buf_pa; dev_dbg(adapter->dev, "info: Rx CMD Response\n"); + MWIFIEX_SKB_PACB(skb, &buf_pa); + pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE); + + pkt_len = *((__le16 *)skb->data); + rx_len = le16_to_cpu(pkt_len); + skb_trim(skb, rx_len); + skb_pull(skb, INTF_HEADER_LEN); + if (!adapter->curr_cmd) { - skb_pull(skb, INTF_HEADER_LEN); if (adapter->ps_state == PS_STATE_SLEEP_CFM) { mwifiex_process_sleep_confirm_resp(adapter, skb->data, skb->len); @@ -1115,9 +1161,12 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) } memcpy(adapter->upld_buf, skb->data, min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); - skb_push(skb, INTF_HEADER_LEN); + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + MWIFIEX_SKB_PACB(skb, &buf_pa); } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { - skb_pull(skb, INTF_HEADER_LEN); adapter->curr_cmd->resp_skb = skb; adapter->cmd_resp_received = true; /* Take the pointer and set it to CMD node and will @@ -1151,10 +1200,23 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; + dma_addr_t buf_pa; + struct sk_buff *skb_tmp; if (skb) { card->cmdrsp_buf = skb; skb_push(card->cmdrsp_buf, INTF_HEADER_LEN); + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + } + + skb_tmp = card->cmd_buf; + if (skb_tmp) { + MWIFIEX_SKB_PACB(skb_tmp, &buf_pa); + pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE); + card->cmd_buf = NULL; } return 0; @@ -1168,6 +1230,10 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) struct pcie_service_card *card = adapter->card; u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; u32 wrptr, event; + dma_addr_t buf_pa; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); if (adapter->event_received) { dev_dbg(adapter->dev, "info: Event being processed, " @@ -1199,6 +1265,10 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: Read Index: %d\n", rdptr); skb_cmd = card->evt_buf_list[rdptr]; + MWIFIEX_SKB_PACB(skb_cmd, &buf_pa); + pci_unmap_single(card->dev, buf_pa, MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE); + /* Take the pointer and set it to event pointer in adapter and will return back after event handling callback */ card->evt_buf_list[rdptr] = NULL; @@ -1243,7 +1313,7 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, int ret = 0; u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; u32 wrptr; - phys_addr_t *buf_pa; + dma_addr_t buf_pa; if (!skb) return 0; @@ -1263,9 +1333,14 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, if (!card->evt_buf_list[rdptr]) { skb_push(skb, INTF_HEADER_LEN); + if (mwifiex_map_pci_memory(adapter, skb, + MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + MWIFIEX_SKB_PACB(skb, &buf_pa); card->evt_buf_list[rdptr] = skb; - buf_pa = MWIFIEX_SKB_PACB(skb); - card->evtbd_ring[rdptr]->paddr = *buf_pa; + MWIFIEX_SKB_PACB(skb, &buf_pa); + card->evtbd_ring[rdptr]->paddr = buf_pa; card->evtbd_ring[rdptr]->len = (u16)skb->len; card->evtbd_ring[rdptr]->flags = 0; skb = NULL; @@ -1314,11 +1389,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct sk_buff *skb; u32 txlen, tx_blocks = 0, tries, len; u32 block_retry_cnt = 0; - - if (!adapter) { - pr_err("adapter structure is not valid\n"); - return -1; - } + dma_addr_t buf_pa; + struct pcie_service_card *card = adapter->card; if (!firmware || !firmware_len) { dev_err(adapter->dev, @@ -1340,7 +1412,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, ret = -ENOMEM; goto done; } - mwifiex_update_sk_buff_pa(skb); /* Perform firmware data transfer */ do { @@ -1415,6 +1486,9 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, ret = -1; goto done; } + + MWIFIEX_SKB_PACB(skb, &buf_pa); + /* Wait for the command done interrupt */ do { if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, @@ -1422,11 +1496,17 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, dev_err(adapter->dev, "%s: Failed to read " "interrupt status during fw dnld.\n", __func__); + pci_unmap_single(card->dev, buf_pa, skb->len, + PCI_DMA_TODEVICE); ret = -1; goto done; } } while ((ireg_intr & CPU_INTR_DOOR_BELL) == CPU_INTR_DOOR_BELL); + + pci_unmap_single(card->dev, buf_pa, skb->len, + PCI_DMA_TODEVICE); + offset += txlen; } while (true); @@ -1828,15 +1908,8 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) struct pcie_service_card *card = adapter->card; struct pci_dev *pdev = card->dev; - mwifiex_pcie_delete_sleep_cookie_buf(adapter); - mwifiex_pcie_delete_cmdrsp_buf(adapter); - mwifiex_pcie_delete_evtbd_ring(adapter); - mwifiex_pcie_delete_rxbd_ring(adapter); - mwifiex_pcie_delete_txbd_ring(adapter); - card->cmdrsp_buf = NULL; - - dev_dbg(adapter->dev, "Clearing driver ready signature\n"); if (user_rmmod) { + dev_dbg(adapter->dev, "Clearing driver ready signature\n"); if (mwifiex_write_reg(adapter, REG_DRV_READY, 0x00000000)) dev_err(adapter->dev, "Failed to write driver not-ready signature\n"); @@ -1893,6 +1966,13 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) if (card) { dev_dbg(adapter->dev, "%s(): calling free_irq()\n", __func__); free_irq(card->dev->irq, card->dev); + + mwifiex_pcie_delete_sleep_cookie_buf(adapter); + mwifiex_pcie_delete_cmdrsp_buf(adapter); + mwifiex_pcie_delete_evtbd_ring(adapter); + mwifiex_pcie_delete_rxbd_ring(adapter); + mwifiex_pcie_delete_txbd_ring(adapter); + card->cmdrsp_buf = NULL; } } diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index 2f218f9a3fd3..87201f554d2e 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h @@ -118,7 +118,7 @@ struct pcie_service_card { u32 txbd_rdptr; u32 txbd_ring_size; u8 *txbd_ring_vbase; - phys_addr_t txbd_ring_pbase; + dma_addr_t txbd_ring_pbase; struct mwifiex_pcie_buf_desc *txbd_ring[MWIFIEX_MAX_TXRX_BD]; struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD]; @@ -126,7 +126,7 @@ struct pcie_service_card { u32 rxbd_rdptr; u32 rxbd_ring_size; u8 *rxbd_ring_vbase; - phys_addr_t rxbd_ring_pbase; + dma_addr_t rxbd_ring_pbase; struct mwifiex_pcie_buf_desc *rxbd_ring[MWIFIEX_MAX_TXRX_BD]; struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD]; @@ -134,13 +134,14 @@ struct pcie_service_card { u32 evtbd_rdptr; u32 evtbd_ring_size; u8 *evtbd_ring_vbase; - phys_addr_t evtbd_ring_pbase; + dma_addr_t evtbd_ring_pbase; struct mwifiex_pcie_buf_desc *evtbd_ring[MWIFIEX_MAX_EVT_BD]; struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD]; struct sk_buff *cmd_buf; struct sk_buff *cmdrsp_buf; - struct sk_buff *sleep_cookie; + u8 *sleep_cookie_vbase; + dma_addr_t sleep_cookie_pbase; void __iomem *pci_mmap; void __iomem *pci_mmap1; }; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 5d87195390f8..c4607859d59d 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -931,7 +931,6 @@ mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv, struct host_cmd_ds_pcie_details *host_spec = &cmd->params.pcie_host_spec; struct pcie_service_card *card = priv->adapter->card; - phys_addr_t *buf_pa; cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS); cmd->size = cpu_to_le16(sizeof(struct @@ -953,10 +952,11 @@ mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv, host_spec->evtbd_addr_lo = (u32)(card->evtbd_ring_pbase); host_spec->evtbd_addr_hi = (u32)(((u64)card->evtbd_ring_pbase)>>32); host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD; - if (card->sleep_cookie) { - buf_pa = MWIFIEX_SKB_PACB(card->sleep_cookie); - host_spec->sleep_cookie_addr_lo = (u32) *buf_pa; - host_spec->sleep_cookie_addr_hi = (u32) (((u64)*buf_pa) >> 32); + if (card->sleep_cookie_vbase) { + host_spec->sleep_cookie_addr_lo = + (u32)(card->sleep_cookie_pbase); + host_spec->sleep_cookie_addr_hi = + (u32)(((u64)(card->sleep_cookie_pbase)) >> 32); dev_dbg(priv->adapter->dev, "sleep_cook_lo phy addr: 0x%x\n", host_spec->sleep_cookie_addr_lo); } diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h index f6d36b9654a0..cb2d0582bd36 100644 --- a/drivers/net/wireless/mwifiex/util.h +++ b/drivers/net/wireless/mwifiex/util.h @@ -22,16 +22,16 @@ static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) { - return (struct mwifiex_rxinfo *)(skb->cb + sizeof(phys_addr_t)); + return (struct mwifiex_rxinfo *)(skb->cb + sizeof(dma_addr_t)); } static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) { - return (struct mwifiex_txinfo *)(skb->cb + sizeof(phys_addr_t)); + return (struct mwifiex_txinfo *)(skb->cb + sizeof(dma_addr_t)); } -static inline phys_addr_t *MWIFIEX_SKB_PACB(struct sk_buff *skb) +static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb, dma_addr_t *buf_pa) { - return (phys_addr_t *)skb->cb; + memcpy(buf_pa, skb->cb, sizeof(dma_addr_t)); } #endif /* !_MWIFIEX_UTIL_H_ */ From fbd7e7ac6db3db8bccf3210d23b5a2a66471c538 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Thu, 3 Jan 2013 21:21:31 -0800 Subject: [PATCH 241/478] mwifiex: flush TX ring for PCIe after disconnect or bss stop This patch adds handler to clean PCIe TX rings after disconnect or bss stop is called for PCIe based mwifiex driver. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/pcie.c | 32 +++++++++++++++++++++++++++++ drivers/net/wireless/mwifiex/pcie.h | 13 ++++++++++++ drivers/net/wireless/mwifiex/wmm.c | 2 ++ 4 files changed, 48 insertions(+) diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index ef02a5bf57b1..51044e3ea89b 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -602,6 +602,7 @@ struct mwifiex_if_ops { int (*init_fw_port) (struct mwifiex_adapter *); int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); void (*card_reset) (struct mwifiex_adapter *); + int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); }; struct mwifiex_adapter { diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index ac20bc4bc1a0..19b5a83f4e7f 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -764,6 +764,37 @@ static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter) return 0; } +/* This function flushes the TX buffer descriptor ring + * This function defined as handler is also called while cleaning TXRX + * during disconnect/ bss stop. + */ +static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + u32 rdptr; + + /* Read the TX ring read pointer set by firmware */ + if (mwifiex_read_reg(adapter, REG_TXBD_RDPTR, &rdptr)) { + dev_err(adapter->dev, + "Flush TXBD: failed to read REG_TXBD_RDPTR\n"); + return -1; + } + + if (!mwifiex_pcie_txbd_empty(card, rdptr)) { + card->txbd_flush = 1; + /* write pointer already set at last send + * send dnld-rdy intr again, wait for completion. + */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DNLD_RDY)) { + dev_err(adapter->dev, + "failed to assert dnld-rdy interrupt.\n"); + return -1; + } + } + return 0; +} + /* * This function sends data buffer to device */ @@ -1995,6 +2026,7 @@ static struct mwifiex_if_ops pcie_ops = { .update_mp_end_port = NULL, .cleanup_mpa_buf = NULL, .init_fw_port = mwifiex_pcie_init_fw_port, + .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, }; /* diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index 87201f554d2e..24a39b32a222 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h @@ -114,6 +114,7 @@ struct pcie_service_card { struct pci_dev *dev; struct mwifiex_adapter *adapter; + u8 txbd_flush; u32 txbd_wrptr; u32 txbd_rdptr; u32 txbd_ring_size; @@ -146,4 +147,16 @@ struct pcie_service_card { void __iomem *pci_mmap1; }; +static inline int +mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr) +{ + if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) == + (rdptr & MWIFIEX_TXBD_MASK)) && + ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) != + (rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) + return 1; + + return 0; +} + #endif /* _MWIFIEX_PCIE_H */ diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 818f871ae987..6d641cbce726 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -568,6 +568,8 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) mwifiex_wmm_delete_all_ralist(priv); memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); + if (priv->adapter->if_ops.clean_pcie_ring) + priv->adapter->if_ops.clean_pcie_ring(priv->adapter); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); } From e7f767a7d9f809c494bfffffeda2bbdbfec110b4 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Thu, 3 Jan 2013 21:21:32 -0800 Subject: [PATCH 242/478] mwifiex: use map/unmap APIs in TX and RX to reduce memcpy This patch is an enhacement to mwifiex_pcie driver to use map/unmap PCI memory APIs. This reduces one memcpy each in TX path and RX path, and enhances throughput. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n_aggr.c | 6 +- drivers/net/wireless/mwifiex/pcie.c | 219 ++++++++++++++++++------ drivers/net/wireless/mwifiex/pcie.h | 11 ++ drivers/net/wireless/mwifiex/txrx.c | 6 +- drivers/net/wireless/mwifiex/wmm.c | 6 +- 5 files changed, 193 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 68d52cfc1ebd..af8fe6352eed 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -278,14 +278,16 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); break; case -1: - adapter->data_sent = false; + if (adapter->iface_type != MWIFIEX_PCIE) + adapter->data_sent = false; dev_err(adapter->dev, "%s: host_to_card failed: %#x\n", __func__, ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); return 0; case -EINPROGRESS: - adapter->data_sent = false; + if (adapter->iface_type != MWIFIEX_PCIE) + adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 19b5a83f4e7f..3da89b456c13 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -796,15 +796,15 @@ static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) } /* - * This function sends data buffer to device + * This function unmaps and frees downloaded data buffer */ -static int -mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb) +static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) { + const u32 num_tx_buffs = MWIFIEX_MAX_TXRX_BD; + struct sk_buff *skb; + dma_addr_t buf_pa; + u32 wrdoneidx, rdptr, unmap_count = 0; struct pcie_service_card *card = adapter->card; - u32 wrindx, rdptr; - phys_addr_t buf_pa; - __le16 *tmp; if (!mwifiex_pcie_ok_to_access_hw(adapter)) mwifiex_pm_wakeup_card(adapter); @@ -812,34 +812,112 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb) /* Read the TX ring read pointer set by firmware */ if (mwifiex_read_reg(adapter, REG_TXBD_RDPTR, &rdptr)) { dev_err(adapter->dev, - "SEND DATA: failed to read REG_TXBD_RDPTR\n"); + "SEND COMP: failed to read REG_TXBD_RDPTR\n"); return -1; } - wrindx = card->txbd_wrptr & MWIFIEX_TXBD_MASK; + dev_dbg(adapter->dev, "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n", + card->txbd_rdptr, rdptr); - dev_dbg(adapter->dev, "info: SEND DATA: \n", rdptr, - card->txbd_wrptr); - if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) != - (rdptr & MWIFIEX_TXBD_MASK)) || - ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) != - (rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) { - struct sk_buff *skb_data; + /* free from previous txbd_rdptr to current txbd_rdptr */ + while (((card->txbd_rdptr & MWIFIEX_TXBD_MASK) != + (rdptr & MWIFIEX_TXBD_MASK)) || + ((card->txbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) != + (rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) { + wrdoneidx = card->txbd_rdptr & MWIFIEX_TXBD_MASK; + + skb = card->tx_buf_list[wrdoneidx]; + if (skb) { + dev_dbg(adapter->dev, + "SEND COMP: Detach skb %p at txbd_rdidx=%d\n", + skb, wrdoneidx); + MWIFIEX_SKB_PACB(skb, &buf_pa); + pci_unmap_single(card->dev, buf_pa, skb->len, + PCI_DMA_TODEVICE); + + unmap_count++; + + if (card->txbd_flush) + mwifiex_write_data_complete(adapter, skb, 0, + -1); + else + mwifiex_write_data_complete(adapter, skb, 0, 0); + } + + card->tx_buf_list[wrdoneidx] = NULL; + card->txbd_ring[wrdoneidx]->paddr = 0; + card->rxbd_ring[wrdoneidx]->len = 0; + card->rxbd_ring[wrdoneidx]->flags = 0; + card->txbd_rdptr++; + + if ((card->txbd_rdptr & MWIFIEX_TXBD_MASK) == num_tx_buffs) + card->txbd_rdptr = ((card->txbd_rdptr & + MWIFIEX_BD_FLAG_ROLLOVER_IND) ^ + MWIFIEX_BD_FLAG_ROLLOVER_IND); + } + + if (unmap_count) + adapter->data_sent = false; + + if (card->txbd_flush) { + if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) == + (card->txbd_rdptr & MWIFIEX_TXBD_MASK)) && + ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) != + (card->txbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) + card->txbd_flush = 0; + else + mwifiex_clean_pcie_ring_buf(adapter); + } + + return 0; +} + +/* This function sends data buffer to device. First 4 bytes of payload + * are filled with payload length and payload type. Then this payload + * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. + * Download ready interrupt to FW is deffered if Tx ring is not full and + * additional payload can be accomodated. + */ +static int +mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct pcie_service_card *card = adapter->card; + u32 wrindx; + int ret; + dma_addr_t buf_pa; + __le16 *tmp; + + if (!(skb->data && skb->len)) { + dev_err(adapter->dev, "%s(): invalid parameter <%p, %#x>\n", + __func__, skb->data, skb->len); + return -1; + } + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + dev_dbg(adapter->dev, "info: SEND DATA: \n", + card->txbd_rdptr, card->txbd_wrptr); + if (mwifiex_pcie_txbd_not_full(card)) { u8 *payload; adapter->data_sent = true; - skb_data = card->tx_buf_list[wrindx]; - memcpy(skb_data->data, skb->data, skb->len); - payload = skb_data->data; + payload = skb->data; tmp = (__le16 *)&payload[0]; *tmp = cpu_to_le16((u16)skb->len); tmp = (__le16 *)&payload[2]; *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); - skb_put(skb_data, MWIFIEX_RX_DATA_BUF_SIZE - skb_data->len); - skb_trim(skb_data, skb->len); - MWIFIEX_SKB_PACB(skb_data, &buf_pa); + + if (mwifiex_map_pci_memory(adapter, skb, skb->len , + PCI_DMA_TODEVICE)) + return -1; + + wrindx = card->txbd_wrptr & MWIFIEX_TXBD_MASK; + MWIFIEX_SKB_PACB(skb, &buf_pa); + card->tx_buf_list[wrindx] = skb; card->txbd_ring[wrindx]->paddr = buf_pa; - card->txbd_ring[wrindx]->len = (u16)skb_data->len; + card->txbd_ring[wrindx]->len = (u16)skb->len; card->txbd_ring[wrindx]->flags = MWIFIEX_BD_FLAG_FIRST_DESC | MWIFIEX_BD_FLAG_LAST_DESC; @@ -854,19 +932,28 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb) card->txbd_wrptr)) { dev_err(adapter->dev, "SEND DATA: failed to write REG_TXBD_WRPTR\n"); - return 0; + ret = -1; + goto done_unmap; } - - /* Send the TX ready interrupt */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DNLD_RDY)) { - dev_err(adapter->dev, - "SEND DATA: failed to assert door-bell intr\n"); - return -1; + if ((mwifiex_pcie_txbd_not_full(card)) && + tx_param->next_pkt_len) { + /* have more packets and TxBD still can hold more */ + dev_dbg(adapter->dev, + "SEND DATA: delay dnld-rdy interrupt.\n"); + adapter->data_sent = false; + } else { + /* Send the TX ready interrupt */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DNLD_RDY)) { + dev_err(adapter->dev, + "SEND DATA: failed to assert dnld-rdy interrupt.\n"); + ret = -1; + goto done_unmap; + } } dev_dbg(adapter->dev, "info: SEND DATA: Updated and sent packet to firmware successfully\n", - rdptr, card->txbd_wrptr); + card->txbd_rdptr, card->txbd_wrptr); } else { dev_dbg(adapter->dev, "info: TX Ring full, can't send packets to fw\n"); @@ -879,7 +966,15 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb) return -EBUSY; } - return 0; + return -EINPROGRESS; +done_unmap: + MWIFIEX_SKB_PACB(skb, &buf_pa); + pci_unmap_single(card->dev, buf_pa, skb->len, PCI_DMA_TODEVICE); + card->tx_buf_list[wrindx] = NULL; + card->txbd_ring[wrindx]->paddr = 0; + card->txbd_ring[wrindx]->len = 0; + card->txbd_ring[wrindx]->flags = 0; + return ret; } /* @@ -890,9 +985,13 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; u32 wrptr, rd_index; + dma_addr_t buf_pa; int ret = 0; struct sk_buff *skb_tmp = NULL; + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + /* Read the RX ring Write pointer set by firmware */ if (mwifiex_read_reg(adapter, REG_RXBD_WRPTR, &wrptr)) { dev_err(adapter->dev, @@ -900,6 +999,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) ret = -1; goto done; } + card->rxbd_wrptr = wrptr; while (((wrptr & MWIFIEX_RXBD_MASK) != (card->rxbd_rdptr & MWIFIEX_RXBD_MASK)) || @@ -907,27 +1007,50 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) (card->rxbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) { struct sk_buff *skb_data; u16 rx_len; + __le16 pkt_len; rd_index = card->rxbd_rdptr & MWIFIEX_RXBD_MASK; skb_data = card->rx_buf_list[rd_index]; + MWIFIEX_SKB_PACB(skb_data, &buf_pa); + pci_unmap_single(card->dev, buf_pa, MWIFIEX_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE); + card->rx_buf_list[rd_index] = NULL; + /* Get data length from interface header - - first byte is len, second byte is type */ - rx_len = *((u16 *)skb_data->data); + * first 2 bytes for len, next 2 bytes is for type + */ + pkt_len = *((__le16 *)skb_data->data); + rx_len = le16_to_cpu(pkt_len); + skb_put(skb_data, rx_len); dev_dbg(adapter->dev, "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", card->rxbd_rdptr, wrptr, rx_len); - skb_tmp = dev_alloc_skb(rx_len); + skb_pull(skb_data, INTF_HEADER_LEN); + mwifiex_handle_rx_packet(adapter, skb_data); + + skb_tmp = dev_alloc_skb(MWIFIEX_RX_DATA_BUF_SIZE); if (!skb_tmp) { - dev_dbg(adapter->dev, - "info: Failed to alloc skb for RX\n"); - ret = -EBUSY; - goto done; + dev_err(adapter->dev, + "Unable to allocate skb.\n"); + return -ENOMEM; } - skb_put(skb_tmp, rx_len); + if (mwifiex_map_pci_memory(adapter, skb_tmp, + MWIFIEX_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + MWIFIEX_SKB_PACB(skb_tmp, &buf_pa); + + dev_dbg(adapter->dev, + "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n", + skb_tmp, rd_index); + card->rx_buf_list[rd_index] = skb_tmp; + card->rxbd_ring[rd_index]->paddr = buf_pa; + card->rxbd_ring[rd_index]->len = skb_tmp->len; + card->rxbd_ring[rd_index]->flags = 0; - memcpy(skb_tmp->data, skb_data->data + INTF_HEADER_LEN, rx_len); if ((++card->rxbd_rdptr & MWIFIEX_RXBD_MASK) == MWIFIEX_MAX_TXRX_BD) { card->rxbd_rdptr = ((card->rxbd_rdptr & @@ -955,12 +1078,10 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) } dev_dbg(adapter->dev, "info: RECV DATA: Rcvd packet from fw successfully\n"); - mwifiex_handle_rx_packet(adapter, skb_tmp); + card->rxbd_wrptr = wrptr; } done: - if (ret && skb_tmp) - dev_kfree_skb_any(skb_tmp); return ret; } @@ -1732,10 +1853,10 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) while (pcie_ireg & HOST_INTR_MASK) { if (pcie_ireg & HOST_INTR_DNLD_DONE) { pcie_ireg &= ~HOST_INTR_DNLD_DONE; - if (adapter->data_sent) { - dev_dbg(adapter->dev, "info: DATA sent intr\n"); - adapter->data_sent = false; - } + dev_dbg(adapter->dev, "info: TX DNLD Done\n"); + ret = mwifiex_pcie_send_data_complete(adapter); + if (ret) + return ret; } if (pcie_ireg & HOST_INTR_UPLD_RDY) { pcie_ireg &= ~HOST_INTR_UPLD_RDY; @@ -1812,7 +1933,7 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, } if (type == MWIFIEX_TYPE_DATA) - return mwifiex_pcie_send_data(adapter, skb); + return mwifiex_pcie_send_data(adapter, skb, tx_param); else if (type == MWIFIEX_TYPE_CMD) return mwifiex_pcie_send_cmd(adapter, skb); diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index 24a39b32a222..37eeb2ca6b29 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h @@ -159,4 +159,15 @@ mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr) return 0; } +static inline int +mwifiex_pcie_txbd_not_full(struct pcie_service_card *card) +{ + if (((card->txbd_wrptr & MWIFIEX_TXBD_MASK) != + (card->txbd_rdptr & MWIFIEX_TXBD_MASK)) || + ((card->txbd_wrptr & MWIFIEX_BD_FLAG_ROLLOVER_IND) != + (card->txbd_rdptr & MWIFIEX_BD_FLAG_ROLLOVER_IND))) + return 1; + + return 0; +} #endif /* _MWIFIEX_PCIE_H */ diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 8c80024c30ff..296faec14365 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -117,14 +117,16 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); break; case -1: - adapter->data_sent = false; + if (adapter->iface_type != MWIFIEX_PCIE) + adapter->data_sent = false; dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n", ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: - adapter->data_sent = false; + if (adapter->iface_type != MWIFIEX_PCIE) + adapter->data_sent = false; break; case 0: mwifiex_write_data_complete(adapter, skb, 0, ret); diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 6d641cbce726..135d96df2063 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -1208,13 +1208,15 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, ra_list_flags); break; case -1: - adapter->data_sent = false; + if (adapter->iface_type != MWIFIEX_PCIE) + adapter->data_sent = false; dev_err(adapter->dev, "host_to_card failed: %#x\n", ret); adapter->dbg.num_tx_host_to_card_failure++; mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: - adapter->data_sent = false; + if (adapter->iface_type != MWIFIEX_PCIE) + adapter->data_sent = false; default: break; } From 4a484cffc5e1836114e9556f7c704412d491200c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 9 Jan 2013 10:22:07 +0100 Subject: [PATCH 243/478] regulatory: fix uevent The uevent callback doesn't protect its access to last_request, which now causes a warning since the conversion to get_last_request(). Fix this by allowing to use RCU protection for last_request. Reported-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/reg.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 0b35de001937..2193f62bb8ae 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -140,8 +140,8 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r) static struct regulatory_request *get_last_request(void) { - return rcu_dereference_protected(last_request, - lockdep_is_held(®_mutex)); + return rcu_dereference_check(last_request, + lockdep_is_held(®_mutex)); } /* Used to queue up regulatory hints */ @@ -2250,14 +2250,21 @@ int set_regdom(const struct ieee80211_regdomain *rd) #ifdef CONFIG_HOTPLUG int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct regulatory_request *lr = get_last_request(); + struct regulatory_request *lr; + u8 alpha2[2]; + bool add = false; + rcu_read_lock(); + lr = get_last_request(); if (lr && !lr->processed) { - if (add_uevent_var(env, "COUNTRY=%c%c", - lr->alpha2[0], lr->alpha2[1])) - return -ENOMEM; + memcpy(alpha2, lr->alpha2, 2); + add = true; } + rcu_read_unlock(); + if (add) + return add_uevent_var(env, "COUNTRY=%c%c", + alpha2[0], alpha2[1]); return 0; } #else From 2d31986764926630a6df72123a5e217219b32594 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 9 Jan 2013 12:01:38 +0100 Subject: [PATCH 244/478] regulatory: fix restore_regulatory_settings My commit 379b82f4c9dc6e67bf61aa61b096c06a2f320f60 ("regulatory: pass new regdomain to reset function") broke the restore_regulatory_settings() function due to a logic change. Consider this change: - reset_regdomains(true); - cfg80211_regdomain = cfg80211_world_regdom; + reset_regdomains(true, cfg80211_world_regdom); This looks innocent enough, until you realise that the called function (reset_regdomains) also resets the cfg80211_world_regdom pointer, so that the old version of the code would use the new object it pointed to and the new version of the code uses the old object. This lead to a double-free of this object. Since reset_regdomains() sets it to &world_regdom, use that directly. Reported-by: Sujith Manoharan Tested-by: Sujith Manoharan Reported-by: Bob Copeland Reported-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2193f62bb8ae..8c114e8a9135 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1848,7 +1848,7 @@ static void restore_regulatory_settings(bool reset_user) mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true, cfg80211_world_regdom); + reset_regdomains(true, &world_regdom); restore_alpha2(alpha2, reset_user); /* From d6b67c6c0c54c1b18507c15cf1667a362959a0d3 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Sat, 8 Dec 2012 07:08:25 +0200 Subject: [PATCH 245/478] Bluetooth: Remove unnecessary include l2cap.h This patch removes unnecessary include of in bluetooth/bnep/core.c. Signed-off-by: Rami Rosen Signed-off-by: Gustavo Padovan --- net/bluetooth/bnep/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index a5b639702637..e430b1abcd2f 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -33,7 +33,6 @@ #include #include -#include #include "bnep.h" From 7952861f4a8d67b6624b2e6c92cd63bd11a332ce Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Tue, 18 Dec 2012 11:48:01 +0200 Subject: [PATCH 246/478] Bluetooth: remove an unused variable in a header file This patch removes srej_queue_next from include/net/bluetooth/l2cap.h as it is not used. Signed-off-by: Rami Rosen Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7588ef44ebaf..cdd33021f831 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -496,7 +496,6 @@ struct l2cap_chan { __u16 frames_sent; __u16 unacked_frames; __u8 retry_count; - __u16 srej_queue_next; __u16 sdu_len; struct sk_buff *sdu; struct sk_buff *sdu_last_frag; From 8e05e3ba88adcf7ac644e6ef26676ea7c048a08c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 7 Dec 2012 14:59:05 +0200 Subject: [PATCH 247/478] Bluetooth: AMP: Send A2MP Create Phylink Rsp after Assoc write Postpone sending A2MP Create Phylink Response until we got successful HCI Command Complete after HCI Write Remote AMP Assoc. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 ++ net/bluetooth/a2mp.c | 38 ++++++++++++++++++++++++++++++++++-- net/bluetooth/amp.c | 4 +++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 42f21766c538..8b39327a5200 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -23,6 +23,7 @@ enum amp_mgr_state { READ_LOC_AMP_INFO, READ_LOC_AMP_ASSOC, READ_LOC_AMP_ASSOC_FINAL, + WRITE_REMOTE_AMP_ASSOC, }; struct amp_mgr { @@ -144,5 +145,6 @@ void a2mp_discover_amp(struct l2cap_chan *chan); void a2mp_send_getinfo_rsp(struct hci_dev *hdev); void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status); +void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status); #endif /* __A2MP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 2f67d5ecc907..a200edf63117 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -499,8 +499,16 @@ send_rsp: if (hdev) hci_dev_put(hdev); - a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp), - &rsp); + /* Reply error now and success after HCI Write Remote AMP Assoc + command complete with success status + */ + if (rsp.status != A2MP_STATUS_SUCCESS) { + a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, + sizeof(rsp), &rsp); + } else { + mgr->state = WRITE_REMOTE_AMP_ASSOC; + mgr->ident = hdr->ident; + } skb_pull(skb, le16_to_cpu(hdr->len)); return 0; @@ -949,6 +957,32 @@ clean: kfree(req); } +void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status) +{ + struct amp_mgr *mgr; + struct a2mp_physlink_rsp rsp; + struct hci_conn *hs_hcon; + + mgr = amp_mgr_lookup_by_state(WRITE_REMOTE_AMP_ASSOC); + if (!mgr) + return; + + hs_hcon = hci_conn_hash_lookup_state(hdev, AMP_LINK, BT_CONNECT); + if (!hs_hcon) { + rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; + } else { + rsp.remote_id = hs_hcon->remote_id; + rsp.status = A2MP_STATUS_SUCCESS; + } + + BT_DBG("%s mgr %p hs_hcon %p status %u", hdev->name, mgr, hs_hcon, + status); + + rsp.local_id = hdev->id; + a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, mgr->ident, sizeof(rsp), &rsp); + amp_mgr_put(mgr); +} + void a2mp_discover_amp(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 1b0d92c0643a..522865776ec6 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -317,7 +317,9 @@ void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle) if (!hcon) return; - amp_write_rem_assoc_frag(hdev, hcon); + /* Send A2MP create phylink rsp when all fragments are written */ + if (amp_write_rem_assoc_frag(hdev, hcon)) + a2mp_send_create_phy_link_rsp(hdev, 0); } void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle) From 7a9898c6ff67ad640304fd3d02f9a22874483c3d Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 7 Dec 2012 14:59:06 +0200 Subject: [PATCH 248/478] Bluetooth: AMP: Clean up logical link create / accept Use chan->hs_hcon instead of lookup by dst address. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/amp.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 522865776ec6..6b829b28eb80 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -405,26 +405,20 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon) void amp_create_logical_link(struct l2cap_chan *chan) { + struct hci_conn *hs_hcon = chan->hs_hcon; struct hci_cp_create_accept_logical_link cp; - struct hci_conn *hcon; struct hci_dev *hdev; - BT_DBG("chan %p", chan); + BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst); - if (!chan->hs_hcon) + if (!hs_hcon) return; hdev = hci_dev_hold(chan->hs_hcon->hdev); if (!hdev) return; - BT_DBG("chan %p dst %pMR", chan, chan->conn->dst); - - hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst); - if (!hcon) - goto done; - - cp.phy_handle = hcon->handle; + cp.phy_handle = hs_hcon->handle; cp.tx_flow_spec.id = chan->local_id; cp.tx_flow_spec.stype = chan->local_stype; @@ -440,14 +434,13 @@ void amp_create_logical_link(struct l2cap_chan *chan) cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat); cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to); - if (hcon->out) + if (hs_hcon->out) hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp), &cp); else hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp), &cp); -done: hci_dev_put(hdev); } From cbf54ad104cb2ec6f5734d95be1dc783bea0343b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 7 Dec 2012 14:59:07 +0200 Subject: [PATCH 249/478] Bluetooth: AMP: Remove dead code Remove code which cannot execute. l2cap_conn_add for AMP_LINK might only be invoked when receiving data in l2cap_recv_acldata. But this case is checked in the first statement there. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2c78208d793e..82a3bdc8dc5b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1527,17 +1527,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); switch (hcon->type) { - case AMP_LINK: - conn->mtu = hcon->hdev->block_mtu; - break; - case LE_LINK: if (hcon->hdev->le_mtu) { conn->mtu = hcon->hdev->le_mtu; break; } /* fall through */ - default: conn->mtu = hcon->hdev->acl_mtu; break; From cb6801c640c759fe02c812728c2661bd8ba5a302 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 7 Dec 2012 14:59:08 +0200 Subject: [PATCH 250/478] Bluetooth: AMP: Use set_bit / test_bit for amp_mgr state Using bit operations solves problems with multiple requests and clearing state. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 +- net/bluetooth/a2mp.c | 6 +++--- net/bluetooth/amp.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 8b39327a5200..487b54c1308f 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -34,7 +34,7 @@ struct amp_mgr { struct kref kref; __u8 ident; __u8 handle; - enum amp_mgr_state state; + unsigned long state; unsigned long flags; struct list_head amp_ctrls; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index a200edf63117..eb0f4b16ff09 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -290,7 +290,7 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, goto done; } - mgr->state = READ_LOC_AMP_INFO; + set_bit(READ_LOC_AMP_INFO, &mgr->state); hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); done: @@ -506,7 +506,7 @@ send_rsp: a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp); } else { - mgr->state = WRITE_REMOTE_AMP_ASSOC; + set_bit(WRITE_REMOTE_AMP_ASSOC, &mgr->state); mgr->ident = hdr->ident; } @@ -848,7 +848,7 @@ struct amp_mgr *amp_mgr_lookup_by_state(u8 state) mutex_lock(&_mgr_list_lock); list_for_each_entry(mgr, &_mgr_list, list) { - if (mgr->state == state) { + if (test_and_clear_bit(state, &mgr->state)) { amp_mgr_get(mgr); mutex_unlock(&_mgr_list_lock); return mgr; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 6b829b28eb80..d459ed43c779 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -236,7 +236,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) cp.max_len = cpu_to_le16(hdev->amp_assoc_size); - mgr->state = READ_LOC_AMP_ASSOC; + set_bit(READ_LOC_AMP_ASSOC, &mgr->state); hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); } @@ -250,7 +250,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev, cp.len_so_far = cpu_to_le16(0); cp.max_len = cpu_to_le16(hdev->amp_assoc_size); - mgr->state = READ_LOC_AMP_ASSOC_FINAL; + set_bit(READ_LOC_AMP_ASSOC_FINAL, &mgr->state); /* Read Local AMP Assoc final link information data */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); From 47c37941b8895557409db286e6a441e6d557d62f Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 13 Dec 2012 15:11:20 +0100 Subject: [PATCH 251/478] Bluetooth: mgmt: Remove not needed restriction on add/remove OOB data Those commands don't send any HCI commands to controller so there is no need to restrict them to only powered up controller. This also makes implementation more consistent as already stored remote OOB data persist power toggle. Signed-off-by: Szymon Janc Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f559b966279c..ef385824a144 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2254,13 +2254,6 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, &cp->addr, - sizeof(cp->addr)); - goto unlock; - } - err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash, cp->randomizer); if (err < 0) @@ -2271,7 +2264,6 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); -unlock: hci_dev_unlock(hdev); return err; } @@ -2287,14 +2279,6 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_REMOVE_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, &cp->addr, - sizeof(cp->addr)); - goto unlock; - } - err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr); if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; @@ -2304,7 +2288,6 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); -unlock: hci_dev_unlock(hdev); return err; } From a6785be2f76e2c39b3008820e7bfef8f5fd838bc Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 13 Dec 2012 15:11:21 +0100 Subject: [PATCH 252/478] Bluetooth: mgmt: Avoid using magic number in status code Use MGMT_STATUS_SUCCESS for success return code. Signed-off-by: Szymon Janc Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ef385824a144..e5502a56365b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2259,7 +2259,7 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, if (err < 0) status = MGMT_STATUS_FAILED; else - status = 0; + status = MGMT_STATUS_SUCCESS; err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); @@ -2283,7 +2283,7 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; else - status = 0; + status = MGMT_STATUS_SUCCESS; err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); @@ -2515,7 +2515,7 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, if (err < 0) status = MGMT_STATUS_FAILED; else - status = 0; + status = MGMT_STATUS_SUCCESS; err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); @@ -2540,7 +2540,7 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; else - status = 0; + status = MGMT_STATUS_SUCCESS; err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); From dbccd791a3fbbdac12c33834b73beff3984988e9 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Tue, 11 Dec 2012 08:51:19 +0100 Subject: [PATCH 253/478] Bluetooth: Fix sending HCI commands after reset After sending reset command wait for its command complete event before sending next command. Some chips sends CC event for command received before reset if reset was send before chip replied with CC. This is also required by specification that host shall not send additional HCI commands before receiving CC for reset. < HCI Command: Reset (0x03|0x0003) plen 0 [hci0] 18.404612 > HCI Event: Command Complete (0x0e) plen 4 [hci0] 18.405850 Write Extended Inquiry Response (0x03|0x0052) ncmd 1 Status: Success (0x00) < HCI Command: Read Local Supported Features (0x04|0x0003) plen 0 [hci0] 18.406079 > HCI Event: Command Complete (0x0e) plen 4 [hci0] 18.407864 Reset (0x03|0x0003) ncmd 1 Status: Success (0x00) < HCI Command: Read Local Supported Features (0x04|0x0003) plen 0 [hci0] 18.408062 > HCI Event: Command Complete (0x0e) plen 12 [hci0] 18.408835 Signed-off-by: Szymon Janc Cc: stable@vger.kernel.org Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 705078a0cc39..81b44481d0d9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2688,7 +2688,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); - if (ev->ncmd) { + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) queue_work(hdev->workqueue, &hdev->cmd_work); From eed307e290b96636def331375e065aca43f9069a Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Tue, 11 Dec 2012 11:41:20 +0800 Subject: [PATCH 254/478] Bluetooth: Add support for IMC Networks [13d3:3393] Add support for the AR9462 chip T: Bus=02 Lev=02 Prnt=02 Port=04 Cnt=01 Dev#= 3 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=13d3 ProdID=3393 Rev=00.01 C: #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I: If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb I: If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb Signed-off-by: AceLan Kao Signed-off-by: Gustavo Padovan --- drivers/bluetooth/ath3k.c | 2 ++ drivers/bluetooth/btusb.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index b00000e8aef6..3418bf988d01 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -81,6 +81,7 @@ static struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0CF3, 0xE004) }, { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0489, 0xe057) }, + { USB_DEVICE(0x13d3, 0x3393) }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xE02C) }, @@ -108,6 +109,7 @@ static struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a1d4ede5b892..acd5be40fc04 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -139,6 +139,7 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, From 2c262b2a5235b7f5b18d4d58394511f76371d7cb Mon Sep 17 00:00:00 2001 From: Daniel Schaal Date: Sat, 29 Dec 2012 11:14:34 +0100 Subject: [PATCH 255/478] Bluetooth: Add support for GC-WB300D PCIe [04ca:3006] to ath3k. T: Bus=02 Lev=02 Prnt=02 Port=06 Cnt=01 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=04ca ProdID=3006 Rev= 0.02 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Daniel Schaal Signed-off-by: Gustavo Padovan --- drivers/bluetooth/ath3k.c | 2 ++ drivers/bluetooth/btusb.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 3418bf988d01..afe9d8e8901d 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -77,6 +77,7 @@ static struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0CF3, 0x311D) }, { USB_DEVICE(0x13d3, 0x3375) }, { USB_DEVICE(0x04CA, 0x3005) }, + { USB_DEVICE(0x04CA, 0x3006) }, { USB_DEVICE(0x13d3, 0x3362) }, { USB_DEVICE(0x0CF3, 0xE004) }, { USB_DEVICE(0x0930, 0x0219) }, @@ -105,6 +106,7 @@ static struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index acd5be40fc04..83ff97089e31 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -135,6 +135,7 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, From 3a61eda81ebcfc006ebb1496764299d53e5bf67f Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Thu, 3 Jan 2013 12:24:59 +0800 Subject: [PATCH 256/478] Bluetooth: Add support for Foxconn / Hon Hai [0489:e04e] Add support for the AR9462 chip T: Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 5 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e04e Rev=00.02 C: #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I: If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb I: If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb Signed-off-by: AceLan Kao Signed-off-by: Gustavo Padovan --- drivers/bluetooth/ath3k.c | 2 ++ drivers/bluetooth/btusb.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index afe9d8e8901d..47fc6b5c6488 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -83,6 +83,7 @@ static struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0489, 0xe057) }, { USB_DEVICE(0x13d3, 0x3393) }, + { USB_DEVICE(0x0489, 0xe04e) }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xE02C) }, @@ -112,6 +113,7 @@ static struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 83ff97089e31..9a52f8b6bfd9 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -141,6 +141,7 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, From 2582d529c44d12502a700f1c3db062b9f44679bc Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Thu, 3 Jan 2013 12:25:00 +0800 Subject: [PATCH 257/478] Bluetooth: Add support for Foxconn / Hon Hai [0489:e056] Add support for the AR9462 chip T: Bus=01 Lev=02 Prnt=02 Port=05 Cnt=01 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0489 ProdID=e056 Rev=00.01 C: #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I: If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb I: If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb Signed-off-by: AceLan Kao Signed-off-by: Gustavo Padovan --- drivers/bluetooth/ath3k.c | 2 ++ drivers/bluetooth/btusb.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 47fc6b5c6488..be178949bc8b 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -84,6 +84,7 @@ static struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0489, 0xe057) }, { USB_DEVICE(0x13d3, 0x3393) }, { USB_DEVICE(0x0489, 0xe04e) }, + { USB_DEVICE(0x0489, 0xe056) }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xE02C) }, @@ -114,6 +115,7 @@ static struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9a52f8b6bfd9..3f6a993539e8 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -142,6 +142,7 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, From 72a525cbb8037ecf8663720b7087e8e6fc77a49a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 6 Jan 2013 21:48:50 +0100 Subject: [PATCH 258/478] ssb: add place for serial flash driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville --- drivers/ssb/Kconfig | 5 +++++ drivers/ssb/Makefile | 1 + drivers/ssb/driver_chipcommon_sflash.c | 18 ++++++++++++++++++ drivers/ssb/driver_mipscore.c | 3 ++- drivers/ssb/ssb_private.h | 11 +++++++++++ 5 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 drivers/ssb/driver_chipcommon_sflash.c diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index ff3c8a21f10d..f7305e2fddcc 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -136,6 +136,11 @@ config SSB_DRIVER_MIPS If unsure, say N +config SSB_SFLASH + bool "SSB serial flash support" + depends on SSB_DRIVER_MIPS && BROKEN + default y + # Assumption: We are on embedded, if we compile the MIPS core. config SSB_EMBEDDED bool diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile index 9159ba77c388..b1ddc116d387 100644 --- a/drivers/ssb/Makefile +++ b/drivers/ssb/Makefile @@ -11,6 +11,7 @@ ssb-$(CONFIG_SSB_SDIOHOST) += sdio.o # built-in drivers ssb-y += driver_chipcommon.o ssb-y += driver_chipcommon_pmu.o +ssb-$(CONFIG_SSB_SFLASH) += driver_chipcommon_sflash.o ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o diff --git a/drivers/ssb/driver_chipcommon_sflash.c b/drivers/ssb/driver_chipcommon_sflash.c new file mode 100644 index 000000000000..866269774e86 --- /dev/null +++ b/drivers/ssb/driver_chipcommon_sflash.c @@ -0,0 +1,18 @@ +/* + * Sonics Silicon Backplane + * ChipCommon serial flash interface + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include + +#include "ssb_private.h" + +/* Initialize serial flash access */ +int ssb_sflash_init(struct ssb_chipcommon *cc) +{ + pr_err("Serial flash support is not implemented yet!\n"); + + return -ENOTSUPP; +} diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index 5bd05b136d22..2a7684c90243 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -203,7 +203,8 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { case SSB_CHIPCO_FLASHT_STSER: case SSB_CHIPCO_FLASHT_ATSER: - pr_err("Serial flash not supported\n"); + pr_debug("Found serial flash\n"); + ssb_sflash_init(&bus->chipco); break; case SSB_CHIPCO_FLASHT_PARA: pr_debug("Found parallel flash\n"); diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 6c10b66c796c..77d942630750 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -217,6 +217,17 @@ extern u32 ssb_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks); extern u32 ssb_chipco_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms); +/* driver_chipcommon_sflash.c */ +#ifdef CONFIG_SSB_SFLASH +int ssb_sflash_init(struct ssb_chipcommon *cc); +#else +static inline int ssb_sflash_init(struct ssb_chipcommon *cc) +{ + pr_err("Serial flash not supported\n"); + return 0; +} +#endif /* CONFIG_SSB_SFLASH */ + #ifdef CONFIG_SSB_DRIVER_EXTIF extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks); extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms); From 4656b8f680b4405d68a84e6223b1709b3e1944a6 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 7 Jan 2013 13:53:46 +0530 Subject: [PATCH 259/478] ath9k_hw: Do not enable IQ-CAL for half/quarter rates IQ calibration doesn't complete and times out for half/quarter rates, so skip it correctly. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index fa67e84d51ea..61caf1cbd77a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -1013,7 +1013,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, } } - if (!(ah->enabled_cals & TX_IQ_CAL)) + if ((IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) || + !(ah->enabled_cals & TX_IQ_CAL)) goto skip_tx_iqcal; /* Do Tx IQ Calibration */ @@ -1040,12 +1041,10 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) ar9003_mci_init_cal_req(ah, &is_reusable); - if (!(IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))) { - txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); - REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); - udelay(5); - REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); - } + txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); + udelay(5); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { From dfcca60b884185cc1e12de932156bd806d896747 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 7 Jan 2013 13:53:47 +0530 Subject: [PATCH 260/478] ath9k_hw: Fix MCI init for AGC calibration When AGC calibration is to be done, a GPM message with the payload, MCI_GPM_WLAN_CAL_REQ has to be sent. Currently this falls within the IQ-CAL code block which is incorrect. Fix this by using a separate variable to decide when IQ-CAL is to be done separately and call ar9003_mci_init_cal_req correctly. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 61caf1cbd77a..15f9b89fcc62 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -967,7 +967,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, struct ath9k_hw_cal_data *caldata = ah->caldata; bool txiqcal_done = false, txclcal_done = false; bool is_reusable = true, status = true; - bool run_rtt_cal = false, run_agc_cal; + bool run_rtt_cal = false, run_agc_cal, sep_iq_cal = false; bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | AR_PHY_AGC_CONTROL_FLTR_CAL | @@ -1034,19 +1034,22 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); txiqcal_done = run_agc_cal = true; - goto skip_tx_iqcal; - } else if (caldata && !caldata->done_txiqcal_once) + } else if (caldata && !caldata->done_txiqcal_once) { run_agc_cal = true; + sep_iq_cal = true; + } +skip_tx_iqcal: if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) ar9003_mci_init_cal_req(ah, &is_reusable); - txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); - REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); - udelay(5); - REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + if (sep_iq_cal) { + txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); + udelay(5); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + } -skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { /* Calibrate the AGC */ REG_WRITE(ah, AR_PHY_AGC_CONTROL, From 96da6fdd5ae28568be63ccf1f70b14a72135d493 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 7 Jan 2013 14:43:33 +0530 Subject: [PATCH 261/478] ath9k_hw: Use helper routines to simplify ar9003_hw_init_cal() Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 92 +++++++++++-------- drivers/net/wireless/ath/ath9k/hw.h | 1 + 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 15f9b89fcc62..9221f32a322e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -960,22 +960,68 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0); } +static void ar9003_hw_do_manual_peak_cal(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + int i; + + if (!AR_SREV_9462(ah) && !AR_SREV_9565(ah)) + return; + + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->rxchainmask & (1 << i))) + continue; + ar9003_hw_manual_peak_cal(ah, i, IS_CHAN_2GHZ(chan)); + } +} + +static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable) +{ + u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0, + AR_PHY_CL_TAB_1, + AR_PHY_CL_TAB_2 }; + struct ath9k_hw_cal_data *caldata = ah->caldata; + bool txclcal_done = false; + int i, j; + + if (!caldata || !(ah->enabled_cals & TX_CL_CAL)) + return; + + txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & + AR_PHY_AGC_CONTROL_CLC_SUCCESS); + + if (caldata->done_txclcal_once) { + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->txchainmask & (1 << i))) + continue; + for (j = 0; j < MAX_CL_TAB_ENTRY; j++) + REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]), + caldata->tx_clcal[i][j]); + } + } else if (is_reusable && txclcal_done) { + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->txchainmask & (1 << i))) + continue; + for (j = 0; j < MAX_CL_TAB_ENTRY; j++) + caldata->tx_clcal[i][j] = + REG_READ(ah, CL_TAB_ENTRY(cl_idx[i])); + } + caldata->done_txclcal_once = true; + } +} + static bool ar9003_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = ah->caldata; - bool txiqcal_done = false, txclcal_done = false; + bool txiqcal_done = false; bool is_reusable = true, status = true; bool run_rtt_cal = false, run_agc_cal, sep_iq_cal = false; bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | AR_PHY_AGC_CONTROL_FLTR_CAL | AR_PHY_AGC_CONTROL_PKDET_CAL; - int i, j; - u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0, - AR_PHY_CL_TAB_1, - AR_PHY_CL_TAB_2 }; if (rtt) { if (!ar9003_hw_rtt_restore(ah, chan)) @@ -1060,14 +1106,8 @@ skip_tx_iqcal: status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { - for (i = 0; i < AR9300_MAX_CHAINS; i++) { - if (!(ah->rxchainmask & (1 << i))) - continue; - ar9003_hw_manual_peak_cal(ah, i, - IS_CHAN_2GHZ(chan)); - } - } + + ar9003_hw_do_manual_peak_cal(ah, chan); } if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) @@ -1092,31 +1132,7 @@ skip_tx_iqcal: else if (caldata && caldata->done_txiqcal_once) ar9003_hw_tx_iq_cal_reload(ah); -#define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j)) - if (caldata && (ah->enabled_cals & TX_CL_CAL)) { - txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & - AR_PHY_AGC_CONTROL_CLC_SUCCESS); - if (caldata->done_txclcal_once) { - for (i = 0; i < AR9300_MAX_CHAINS; i++) { - if (!(ah->txchainmask & (1 << i))) - continue; - for (j = 0; j < MAX_CL_TAB_ENTRY; j++) - REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]), - caldata->tx_clcal[i][j]); - } - } else if (is_reusable && txclcal_done) { - for (i = 0; i < AR9300_MAX_CHAINS; i++) { - if (!(ah->txchainmask & (1 << i))) - continue; - for (j = 0; j < MAX_CL_TAB_ENTRY; j++) - caldata->tx_clcal[i][j] = - REG_READ(ah, - CL_TAB_ENTRY(cl_idx[i])); - } - caldata->done_txclcal_once = true; - } - } -#undef CL_TAB_ENTRY + ar9003_hw_cl_cal_post_proc(ah, is_reusable); if (run_rtt_cal && caldata) { if (is_reusable) { diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 38c5a8702fb2..39a226f5eda4 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -397,6 +397,7 @@ enum ath9k_int { #define MAX_RTT_TABLE_ENTRY 6 #define MAX_IQCAL_MEASUREMENT 8 #define MAX_CL_TAB_ENTRY 16 +#define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j)) struct ath9k_hw_cal_data { u16 channel; From 9b0b11fb1e286e03ce911b94844952edd05f554e Mon Sep 17 00:00:00 2001 From: Nishant Sarmukadam Date: Tue, 8 Jan 2013 10:16:05 +0530 Subject: [PATCH 262/478] mwl8k: Disable tx_wait completion Whenever TX ring is drained, priv->tx_wait still points to a valid completion. Making sure that it points to NULL before returning from the mwl8k_tx_wait_empty check. Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index c4d287b48e64..04442c6290a6 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1588,6 +1588,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) rc = -ETIMEDOUT; } + priv->tx_wait = NULL; spin_unlock_bh(&priv->tx_lock); return rc; From 0dd13a48a913f096cb44dbcb6c1daebd94b2cf84 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Tue, 8 Jan 2013 10:16:37 +0530 Subject: [PATCH 263/478] mwl8k: Delete ampdu streams with state AMPDU_STREAM_NEW in sta remove When a station deauths, we do not delete the streams with state AMPDU_STREAM_NEW and these remain created forever. Fix this issue by removing such streams in the driver Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 04442c6290a6..fc4d4a442139 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3935,7 +3935,30 @@ static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *addr) { struct mwl8k_cmd_set_new_stn *cmd; - int rc; + struct mwl8k_priv *priv = hw->priv; + int rc, i; + u8 idx; + + spin_lock(&priv->stream_lock); + /* Destroy any active ampdu streams for this sta */ + for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { + struct mwl8k_ampdu_stream *s; + s = &priv->ampdu[i]; + if (s->state != AMPDU_NO_STREAM) { + if (memcmp(s->sta->addr, addr, ETH_ALEN) == 0) { + if (s->state == AMPDU_STREAM_ACTIVE) { + idx = s->idx; + spin_unlock(&priv->stream_lock); + mwl8k_destroy_ba(hw, idx); + spin_lock(&priv->stream_lock); + } else if (s->state == AMPDU_STREAM_NEW) { + mwl8k_remove_stream(hw, s); + } + } + } + } + + spin_unlock(&priv->stream_lock); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) From 4850b6d355b457738fe7d872c833474dbec25304 Mon Sep 17 00:00:00 2001 From: Nishant Sarmukadam Date: Tue, 8 Jan 2013 12:10:53 +0530 Subject: [PATCH 264/478] mwl8k: Wake up queues in mwl8k_start If queues are stopped in mwl8k_stop, these should be started in mwl8k_start Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index fc4d4a442139..a9c078899252 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4531,6 +4531,8 @@ static int mwl8k_start(struct ieee80211_hw *hw) priv->irq = -1; tasklet_disable(&priv->poll_tx_task); tasklet_disable(&priv->poll_rx_task); + } else { + ieee80211_wake_queues(hw); } return rc; From 3a6a62fbc99e1b49a5f7b9d926a7061cc623a1c2 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 8 Jan 2013 20:57:51 +0530 Subject: [PATCH 265/478] ath9k_hw: Update initvals for QCA955x Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- .../wireless/ath/ath9k/ar955x_1p0_initvals.h | 132 +++++++++--------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h index df97f21c52dc..ccc5b6c99add 100644 --- a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h @@ -23,16 +23,16 @@ static const u32 ar955x_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00016098, 0xd2dd5554, 0xd2dd5554, 0xd28b3330, 0xd28b3330}, - {0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x06345f2a, 0x06345f2a}, - {0x000160ac, 0xa4647c00, 0xa4647c00, 0xa4646800, 0xa4646800}, - {0x000160b0, 0x01885f52, 0x01885f52, 0x04accf3a, 0x04accf3a}, - {0x00016104, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001}, + {0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x0a566f3a, 0x0a566f3a}, + {0x000160ac, 0xa4647c00, 0xa4647c00, 0x24647c00, 0x24647c00}, + {0x000160b0, 0x01885f52, 0x01885f52, 0x01885f52, 0x01885f52}, + {0x00016104, 0xb7a00000, 0xb7a00000, 0xb7a00001, 0xb7a00001}, {0x0001610c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016140, 0x10804008, 0x10804008, 0x10804008, 0x10804008}, - {0x00016504, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001}, + {0x00016504, 0xb7a00000, 0xb7a00000, 0xb7a00001, 0xb7a00001}, {0x0001650c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016540, 0x10804008, 0x10804008, 0x10804008, 0x10804008}, - {0x00016904, 0xb7a00001, 0xb7a00001, 0xb7a00001, 0xb7a00001}, + {0x00016904, 0xb7a00000, 0xb7a00000, 0xb7a00001, 0xb7a00001}, {0x0001690c, 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016940, 0x10804008, 0x10804008, 0x10804008, 0x10804008}, }; @@ -69,15 +69,15 @@ static const u32 ar955x_1p0_baseband_postamble[][5] = { {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, - {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, + {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, - {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e}, {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, - {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e}, {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, @@ -125,7 +125,7 @@ static const u32 ar955x_1p0_radio_core[][2] = { {0x00016094, 0x00000000}, {0x000160a0, 0x0a108ffe}, {0x000160a4, 0x812fc370}, - {0x000160a8, 0x423c8000}, + {0x000160a8, 0x423c8100}, {0x000160b4, 0x92480080}, {0x000160c0, 0x006db6d0}, {0x000160c4, 0x6db6db60}, @@ -134,7 +134,7 @@ static const u32 ar955x_1p0_radio_core[][2] = { {0x00016100, 0x11999601}, {0x00016108, 0x00080010}, {0x00016144, 0x02084080}, - {0x00016148, 0x000080c0}, + {0x00016148, 0x00008040}, {0x00016280, 0x01800804}, {0x00016284, 0x00038dc5}, {0x00016288, 0x00000000}, @@ -178,7 +178,7 @@ static const u32 ar955x_1p0_radio_core[][2] = { {0x00016500, 0x11999601}, {0x00016508, 0x00080010}, {0x00016544, 0x02084080}, - {0x00016548, 0x000080c0}, + {0x00016548, 0x00008040}, {0x00016780, 0x00000000}, {0x00016784, 0x00000000}, {0x00016788, 0x00400705}, @@ -218,7 +218,7 @@ static const u32 ar955x_1p0_radio_core[][2] = { {0x00016900, 0x11999601}, {0x00016908, 0x00080010}, {0x00016944, 0x02084080}, - {0x00016948, 0x000080c0}, + {0x00016948, 0x00008040}, {0x00016b80, 0x00000000}, {0x00016b84, 0x00000000}, {0x00016b88, 0x00400705}, @@ -245,9 +245,9 @@ static const u32 ar955x_1p0_radio_core[][2] = { static const u32 ar955x_1p0_modes_xpa_tx_gain_table[][9] = { /* Addr 5G_HT20_L 5G_HT40_L 5G_HT20_M 5G_HT40_M 5G_HT20_H 5G_HT40_H 2G_HT40 2G_HT20 */ - {0x0000a2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa}, - {0x0000a2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc}, - {0x0000a2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0}, + {0x0000a2dc, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xfffd5aaa, 0xfffd5aaa}, + {0x0000a2e0, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffe9ccc, 0xfffe9ccc}, + {0x0000a2e4, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffffe0f0, 0xffffe0f0}, {0x0000a2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00}, {0x0000a410, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050de, 0x000050da, 0x000050da}, {0x0000a500, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000000, 0x00000000}, @@ -256,63 +256,63 @@ static const u32 ar955x_1p0_modes_xpa_tx_gain_table[][9] = { {0x0000a50c, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c00000b, 0x0c000006, 0x0c000006}, {0x0000a510, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x1000000d, 0x0f00000a, 0x0f00000a}, {0x0000a514, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x14000011, 0x1300000c, 0x1300000c}, - {0x0000a518, 0x19004008, 0x19004008, 0x19004008, 0x19004008, 0x18004008, 0x18004008, 0x1700000e, 0x1700000e}, - {0x0000a51c, 0x1d00400a, 0x1d00400a, 0x1d00400a, 0x1d00400a, 0x1c00400a, 0x1c00400a, 0x1b000064, 0x1b000064}, - {0x0000a520, 0x230020a2, 0x230020a2, 0x210020a2, 0x210020a2, 0x200020a2, 0x200020a2, 0x1f000242, 0x1f000242}, - {0x0000a524, 0x2500006e, 0x2500006e, 0x2500006e, 0x2500006e, 0x2400006e, 0x2400006e, 0x23000229, 0x23000229}, - {0x0000a528, 0x29022221, 0x29022221, 0x28022221, 0x28022221, 0x27022221, 0x27022221, 0x270002a2, 0x270002a2}, - {0x0000a52c, 0x2d00062a, 0x2d00062a, 0x2c00062a, 0x2c00062a, 0x2a00062a, 0x2a00062a, 0x2c001203, 0x2c001203}, - {0x0000a530, 0x340220a5, 0x340220a5, 0x320220a5, 0x320220a5, 0x2f0220a5, 0x2f0220a5, 0x30001803, 0x30001803}, - {0x0000a534, 0x380022c5, 0x380022c5, 0x350022c5, 0x350022c5, 0x320022c5, 0x320022c5, 0x33000881, 0x33000881}, - {0x0000a538, 0x3b002486, 0x3b002486, 0x39002486, 0x39002486, 0x36002486, 0x36002486, 0x38001809, 0x38001809}, - {0x0000a53c, 0x3f00248a, 0x3f00248a, 0x3d00248a, 0x3d00248a, 0x3a00248a, 0x3a00248a, 0x3a000814, 0x3a000814}, - {0x0000a540, 0x4202242c, 0x4202242c, 0x4102242c, 0x4102242c, 0x3f02242c, 0x3f02242c, 0x3f001a0c, 0x3f001a0c}, - {0x0000a544, 0x490044c6, 0x490044c6, 0x460044c6, 0x460044c6, 0x420044c6, 0x420044c6, 0x43001a0e, 0x43001a0e}, - {0x0000a548, 0x4d024485, 0x4d024485, 0x4a024485, 0x4a024485, 0x46024485, 0x46024485, 0x46001812, 0x46001812}, - {0x0000a54c, 0x51044483, 0x51044483, 0x4e044483, 0x4e044483, 0x4a044483, 0x4a044483, 0x49001884, 0x49001884}, - {0x0000a550, 0x5404a40c, 0x5404a40c, 0x5204a40c, 0x5204a40c, 0x4d04a40c, 0x4d04a40c, 0x4d001e84, 0x4d001e84}, - {0x0000a554, 0x57024632, 0x57024632, 0x55024632, 0x55024632, 0x52024632, 0x52024632, 0x50001e69, 0x50001e69}, - {0x0000a558, 0x5c00a634, 0x5c00a634, 0x5900a634, 0x5900a634, 0x5600a634, 0x5600a634, 0x550006f4, 0x550006f4}, - {0x0000a55c, 0x5f026832, 0x5f026832, 0x5d026832, 0x5d026832, 0x5a026832, 0x5a026832, 0x59000ad3, 0x59000ad3}, - {0x0000a560, 0x6602b012, 0x6602b012, 0x6202b012, 0x6202b012, 0x5d02b012, 0x5d02b012, 0x5e000ad5, 0x5e000ad5}, - {0x0000a564, 0x6e02d0e1, 0x6e02d0e1, 0x6802d0e1, 0x6802d0e1, 0x6002d0e1, 0x6002d0e1, 0x61001ced, 0x61001ced}, - {0x0000a568, 0x7202b4c4, 0x7202b4c4, 0x6c02b4c4, 0x6c02b4c4, 0x6502b4c4, 0x6502b4c4, 0x660018d4, 0x660018d4}, - {0x0000a56c, 0x75007894, 0x75007894, 0x70007894, 0x70007894, 0x6b007894, 0x6b007894, 0x660018d4, 0x660018d4}, - {0x0000a570, 0x7b025c74, 0x7b025c74, 0x75025c74, 0x75025c74, 0x70025c74, 0x70025c74, 0x660018d4, 0x660018d4}, - {0x0000a574, 0x8300bcb5, 0x8300bcb5, 0x7a00bcb5, 0x7a00bcb5, 0x7600bcb5, 0x7600bcb5, 0x660018d4, 0x660018d4}, - {0x0000a578, 0x8a04dc74, 0x8a04dc74, 0x7f04dc74, 0x7f04dc74, 0x7c04dc74, 0x7c04dc74, 0x660018d4, 0x660018d4}, - {0x0000a57c, 0x8a04dc74, 0x8a04dc74, 0x7f04dc74, 0x7f04dc74, 0x7c04dc74, 0x7c04dc74, 0x660018d4, 0x660018d4}, + {0x0000a518, 0x1700002b, 0x1700002b, 0x1700002b, 0x1700002b, 0x1600002b, 0x1600002b, 0x1700000e, 0x1700000e}, + {0x0000a51c, 0x1b00002d, 0x1b00002d, 0x1b00002d, 0x1b00002d, 0x1a00002d, 0x1a00002d, 0x1b000064, 0x1b000064}, + {0x0000a520, 0x20000031, 0x20000031, 0x1f000031, 0x1f000031, 0x1e000031, 0x1e000031, 0x1f000242, 0x1f000242}, + {0x0000a524, 0x24000051, 0x24000051, 0x23000051, 0x23000051, 0x23000051, 0x23000051, 0x23000229, 0x23000229}, + {0x0000a528, 0x27000071, 0x27000071, 0x27000071, 0x27000071, 0x26000071, 0x26000071, 0x270002a2, 0x270002a2}, + {0x0000a52c, 0x2b000092, 0x2b000092, 0x2b000092, 0x2b000092, 0x2b000092, 0x2b000092, 0x2c001203, 0x2c001203}, + {0x0000a530, 0x3000028c, 0x3000028c, 0x2f00028c, 0x2f00028c, 0x2e00028c, 0x2e00028c, 0x30001803, 0x30001803}, + {0x0000a534, 0x34000290, 0x34000290, 0x33000290, 0x33000290, 0x32000290, 0x32000290, 0x33000881, 0x33000881}, + {0x0000a538, 0x37000292, 0x37000292, 0x36000292, 0x36000292, 0x35000292, 0x35000292, 0x38001809, 0x38001809}, + {0x0000a53c, 0x3b02028d, 0x3b02028d, 0x3a02028d, 0x3a02028d, 0x3902028d, 0x3902028d, 0x3a000814, 0x3a000814}, + {0x0000a540, 0x3f020291, 0x3f020291, 0x3e020291, 0x3e020291, 0x3d020291, 0x3d020291, 0x3f001a0c, 0x3f001a0c}, + {0x0000a544, 0x44020490, 0x44020490, 0x43020490, 0x43020490, 0x42020490, 0x42020490, 0x43001a0e, 0x43001a0e}, + {0x0000a548, 0x48020492, 0x48020492, 0x47020492, 0x47020492, 0x46020492, 0x46020492, 0x46001812, 0x46001812}, + {0x0000a54c, 0x4c020692, 0x4c020692, 0x4b020692, 0x4b020692, 0x4a020692, 0x4a020692, 0x49001884, 0x49001884}, + {0x0000a550, 0x50020892, 0x50020892, 0x4f020892, 0x4f020892, 0x4e020892, 0x4e020892, 0x4d001e84, 0x4d001e84}, + {0x0000a554, 0x53040891, 0x53040891, 0x53040891, 0x53040891, 0x52040891, 0x52040891, 0x50001e69, 0x50001e69}, + {0x0000a558, 0x58040893, 0x58040893, 0x57040893, 0x57040893, 0x56040893, 0x56040893, 0x550006f4, 0x550006f4}, + {0x0000a55c, 0x5c0408b4, 0x5c0408b4, 0x5a0408b4, 0x5a0408b4, 0x5a0408b4, 0x5a0408b4, 0x59000ad3, 0x59000ad3}, + {0x0000a560, 0x610408b6, 0x610408b6, 0x5e0408b6, 0x5e0408b6, 0x5e0408b6, 0x5e0408b6, 0x5e000ad5, 0x5e000ad5}, + {0x0000a564, 0x670408f6, 0x670408f6, 0x620408f6, 0x620408f6, 0x620408f6, 0x620408f6, 0x61001ced, 0x61001ced}, + {0x0000a568, 0x6a040cf6, 0x6a040cf6, 0x66040cf6, 0x66040cf6, 0x66040cf6, 0x66040cf6, 0x660018d4, 0x660018d4}, + {0x0000a56c, 0x6d040d76, 0x6d040d76, 0x6a040d76, 0x6a040d76, 0x6a040d76, 0x6a040d76, 0x660018d4, 0x660018d4}, + {0x0000a570, 0x70060db6, 0x70060db6, 0x6e060db6, 0x6e060db6, 0x6e060db6, 0x6e060db6, 0x660018d4, 0x660018d4}, + {0x0000a574, 0x730a0df6, 0x730a0df6, 0x720a0df6, 0x720a0df6, 0x720a0df6, 0x720a0df6, 0x660018d4, 0x660018d4}, + {0x0000a578, 0x770a13f6, 0x770a13f6, 0x760a13f6, 0x760a13f6, 0x760a13f6, 0x760a13f6, 0x660018d4, 0x660018d4}, + {0x0000a57c, 0x770a13f6, 0x770a13f6, 0x760a13f6, 0x760a13f6, 0x760a13f6, 0x760a13f6, 0x660018d4, 0x660018d4}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x03804000, 0x03804000}, - {0x0000a610, 0x04c08c01, 0x04c08c01, 0x04808b01, 0x04808b01, 0x04808a01, 0x04808a01, 0x0300ca02, 0x0300ca02}, - {0x0000a614, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00000e04, 0x00000e04}, - {0x0000a618, 0x04010c01, 0x04010c01, 0x03c10b01, 0x03c10b01, 0x03810a01, 0x03810a01, 0x03014000, 0x03014000}, - {0x0000a61c, 0x03814e05, 0x03814e05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03414d05, 0x00000000, 0x00000000}, - {0x0000a620, 0x04010303, 0x04010303, 0x03c10303, 0x03c10303, 0x03810303, 0x03810303, 0x00000000, 0x00000000}, - {0x0000a624, 0x03814e05, 0x03814e05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03414d05, 0x03014000, 0x03014000}, - {0x0000a628, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x00c0c000, 0x03804c05, 0x03804c05}, - {0x0000a62c, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x00c0c303, 0x0701de06, 0x0701de06}, - {0x0000a630, 0x03418000, 0x03418000, 0x03018000, 0x03018000, 0x02c18000, 0x02c18000, 0x07819c07, 0x07819c07}, - {0x0000a634, 0x03815004, 0x03815004, 0x03414f04, 0x03414f04, 0x03414e04, 0x03414e04, 0x0701dc07, 0x0701dc07}, - {0x0000a638, 0x03005302, 0x03005302, 0x02c05202, 0x02c05202, 0x02805202, 0x02805202, 0x0701dc07, 0x0701dc07}, - {0x0000a63c, 0x04c09302, 0x04c09302, 0x04809202, 0x04809202, 0x04809202, 0x04809202, 0x0701dc07, 0x0701dc07}, - {0x0000b2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa}, - {0x0000b2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc}, - {0x0000b2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0}, + {0x0000a60c, 0x02c04b01, 0x02c04b01, 0x02c04b01, 0x02c04b01, 0x02c04b01, 0x02c04b01, 0x03804000, 0x03804000}, + {0x0000a610, 0x04008b01, 0x04008b01, 0x04008b01, 0x04008b01, 0x03c08b01, 0x03c08b01, 0x0300ca02, 0x0300ca02}, + {0x0000a614, 0x05811403, 0x05811403, 0x05411303, 0x05411303, 0x05411303, 0x05411303, 0x00000e04, 0x00000e04}, + {0x0000a618, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x03014000, 0x03014000}, + {0x0000a61c, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x00000000, 0x00000000}, + {0x0000a620, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x00000000, 0x00000000}, + {0x0000a624, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x03014000, 0x03014000}, + {0x0000a628, 0x05811604, 0x05811604, 0x05411504, 0x05411504, 0x05411504, 0x05411504, 0x03804c05, 0x03804c05}, + {0x0000a62c, 0x06815604, 0x06815604, 0x06415504, 0x06415504, 0x06015504, 0x06015504, 0x0701de06, 0x0701de06}, + {0x0000a630, 0x07819a05, 0x07819a05, 0x07419905, 0x07419905, 0x07019805, 0x07019805, 0x07819c07, 0x07819c07}, + {0x0000a634, 0x07819e06, 0x07819e06, 0x07419d06, 0x07419d06, 0x07019c06, 0x07019c06, 0x0701dc07, 0x0701dc07}, + {0x0000a638, 0x07819e06, 0x07819e06, 0x07419d06, 0x07419d06, 0x07019c06, 0x07019c06, 0x0701dc07, 0x0701dc07}, + {0x0000a63c, 0x07819e06, 0x07819e06, 0x07419d06, 0x07419d06, 0x07019c06, 0x07019c06, 0x0701dc07, 0x0701dc07}, + {0x0000b2dc, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xfffd5aaa, 0xfffd5aaa}, + {0x0000b2e0, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffe9ccc, 0xfffe9ccc}, + {0x0000b2e4, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffffe0f0, 0xffffe0f0}, {0x0000b2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00}, - {0x0000c2dc, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xffffaaaa, 0xfffd5aaa, 0xfffd5aaa}, - {0x0000c2e0, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xffffcccc, 0xfffe9ccc, 0xfffe9ccc}, - {0x0000c2e4, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xfffff0f0, 0xffffe0f0, 0xffffe0f0}, + {0x0000c2dc, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xffff6aaa, 0xfffd5aaa, 0xfffd5aaa}, + {0x0000c2e0, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffdcccc, 0xfffe9ccc, 0xfffe9ccc}, + {0x0000c2e4, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffe3b0f0, 0xffffe0f0, 0xffffe0f0}, {0x0000c2e8, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xffffff00, 0xfffcff00, 0xfffcff00}, {0x00016044, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4}, - {0x00016048, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401}, + {0x00016048, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401}, {0x00016280, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01801e84, 0x01808e84, 0x01808e84}, {0x00016444, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4}, - {0x00016448, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401}, + {0x00016448, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401}, {0x00016844, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x056db2d4, 0x010002d4, 0x010002d4}, - {0x00016848, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x62482401, 0x66482401, 0x66482401}, + {0x00016848, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401}, }; static const u32 ar955x_1p0_mac_core[][2] = { @@ -846,7 +846,7 @@ static const u32 ar955x_1p0_baseband_core[][2] = { {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a458, 0x00000000}, - {0x0000a644, 0x3fad9d74}, + {0x0000a644, 0xbfad9d74}, {0x0000a648, 0x0048060a}, {0x0000a64c, 0x00003c37}, {0x0000a670, 0x03020100}, @@ -1277,7 +1277,7 @@ static const u32 ar955x_1p0_modes_fast_clock[][3] = { {0x0000801c, 0x148ec02b, 0x148ec057}, {0x00008318, 0x000044c0, 0x00008980}, {0x00009e00, 0x0372131c, 0x0372131c}, - {0x0000a230, 0x0000000b, 0x00000016}, + {0x0000a230, 0x0000400b, 0x00004016}, {0x0000a254, 0x00000898, 0x00001130}, }; From 2d7caefbafc4ca00cc87ec675c7981e07fa7f37b Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 8 Jan 2013 20:57:52 +0530 Subject: [PATCH 266/478] ath9k_hw: Add TempCompensation feature for AR9550 Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- .../net/wireless/ath/ath9k/ar9003_eeprom.c | 130 ++++++++++++++---- drivers/net/wireless/ath/ath9k/ar9003_phy.h | 2 +- 2 files changed, 101 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 562186ca9b52..881e989ea470 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -4586,14 +4586,14 @@ static int ar9003_hw_cal_pier_get(struct ath_hw *ah, return 0; } -static int ar9003_hw_power_control_override(struct ath_hw *ah, - int frequency, - int *correction, - int *voltage, int *temperature) +static void ar9003_hw_power_control_override(struct ath_hw *ah, + int frequency, + int *correction, + int *voltage, int *temperature) { - int tempSlope = 0; + int temp_slope = 0, temp_slope1 = 0, temp_slope2 = 0; struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; - int f[8], t[8], i; + int f[8], t[8], t1[3], t2[3], i; REG_RMW(ah, AR_PHY_TPC_11_B0, (correction[0] << AR_PHY_TPC_OLPC_GAIN_DELTA_S), @@ -4624,38 +4624,108 @@ static int ar9003_hw_power_control_override(struct ath_hw *ah, * enable temperature compensation * Need to use register names */ - if (frequency < 4000) - tempSlope = eep->modalHeader2G.tempSlope; - else if ((eep->baseEepHeader.miscConfiguration & 0x20) != 0) { - for (i = 0; i < 8; i++) { - t[i] = eep->base_ext1.tempslopextension[i]; - f[i] = FBIN2FREQ(eep->calFreqPier5G[i], 0); - } - tempSlope = ar9003_hw_power_interpolate((s32) frequency, - f, t, 8); - } else if (eep->base_ext2.tempSlopeLow != 0) { - t[0] = eep->base_ext2.tempSlopeLow; - f[0] = 5180; - t[1] = eep->modalHeader5G.tempSlope; - f[1] = 5500; - t[2] = eep->base_ext2.tempSlopeHigh; - f[2] = 5785; - tempSlope = ar9003_hw_power_interpolate((s32) frequency, - f, t, 3); - } else - tempSlope = eep->modalHeader5G.tempSlope; + if (frequency < 4000) { + temp_slope = eep->modalHeader2G.tempSlope; + } else { + if (AR_SREV_9550(ah)) { + t[0] = eep->base_ext1.tempslopextension[2]; + t1[0] = eep->base_ext1.tempslopextension[3]; + t2[0] = eep->base_ext1.tempslopextension[4]; + f[0] = 5180; - REG_RMW_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM, tempSlope); + t[1] = eep->modalHeader5G.tempSlope; + t1[1] = eep->base_ext1.tempslopextension[0]; + t2[1] = eep->base_ext1.tempslopextension[1]; + f[1] = 5500; + + t[2] = eep->base_ext1.tempslopextension[5]; + t1[2] = eep->base_ext1.tempslopextension[6]; + t2[2] = eep->base_ext1.tempslopextension[7]; + f[2] = 5785; + + temp_slope = ar9003_hw_power_interpolate(frequency, + f, t, 3); + temp_slope1 = ar9003_hw_power_interpolate(frequency, + f, t1, 3); + temp_slope2 = ar9003_hw_power_interpolate(frequency, + f, t2, 3); + + goto tempslope; + } + + if ((eep->baseEepHeader.miscConfiguration & 0x20) != 0) { + for (i = 0; i < 8; i++) { + t[i] = eep->base_ext1.tempslopextension[i]; + f[i] = FBIN2FREQ(eep->calFreqPier5G[i], 0); + } + temp_slope = ar9003_hw_power_interpolate((s32) frequency, + f, t, 8); + } else if (eep->base_ext2.tempSlopeLow != 0) { + t[0] = eep->base_ext2.tempSlopeLow; + f[0] = 5180; + t[1] = eep->modalHeader5G.tempSlope; + f[1] = 5500; + t[2] = eep->base_ext2.tempSlopeHigh; + f[2] = 5785; + temp_slope = ar9003_hw_power_interpolate((s32) frequency, + f, t, 3); + } else { + temp_slope = eep->modalHeader5G.tempSlope; + } + } + +tempslope: + if (AR_SREV_9550(ah)) { + /* + * AR955x has tempSlope register for each chain. + * Check whether temp_compensation feature is enabled or not. + */ + if (eep->baseEepHeader.featureEnable & 0x1) { + if (frequency < 4000) { + REG_RMW_FIELD(ah, AR_PHY_TPC_19, + AR_PHY_TPC_19_ALPHA_THERM, + eep->base_ext2.tempSlopeLow); + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, + AR_PHY_TPC_19_ALPHA_THERM, + temp_slope); + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, + AR_PHY_TPC_19_ALPHA_THERM, + eep->base_ext2.tempSlopeHigh); + } else { + REG_RMW_FIELD(ah, AR_PHY_TPC_19, + AR_PHY_TPC_19_ALPHA_THERM, + temp_slope); + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, + AR_PHY_TPC_19_ALPHA_THERM, + temp_slope1); + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, + AR_PHY_TPC_19_ALPHA_THERM, + temp_slope2); + } + } else { + /* + * If temp compensation is not enabled, + * set all registers to 0. + */ + REG_RMW_FIELD(ah, AR_PHY_TPC_19, + AR_PHY_TPC_19_ALPHA_THERM, 0); + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, + AR_PHY_TPC_19_ALPHA_THERM, 0); + REG_RMW_FIELD(ah, AR_PHY_TPC_19_B2, + AR_PHY_TPC_19_ALPHA_THERM, 0); + } + } else { + REG_RMW_FIELD(ah, AR_PHY_TPC_19, + AR_PHY_TPC_19_ALPHA_THERM, temp_slope); + } if (AR_SREV_9462_20(ah)) REG_RMW_FIELD(ah, AR_PHY_TPC_19_B1, - AR_PHY_TPC_19_B1_ALPHA_THERM, tempSlope); + AR_PHY_TPC_19_B1_ALPHA_THERM, temp_slope); REG_RMW_FIELD(ah, AR_PHY_TPC_18, AR_PHY_TPC_18_THERM_CAL_VALUE, temperature[0]); - - return 0; } /* Apply the recorded correction values. */ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 107956298488..e71774196c01 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -1028,7 +1028,7 @@ #define AR_PHY_TPC_5_B2 (AR_SM2_BASE + 0x208) #define AR_PHY_TPC_6_B2 (AR_SM2_BASE + 0x20c) #define AR_PHY_TPC_11_B2 (AR_SM2_BASE + 0x220) -#define AR_PHY_PDADC_TAB_2 (AR_SM2_BASE + 0x240) +#define AR_PHY_TPC_19_B2 (AR_SM2_BASE + 0x240) #define AR_PHY_TX_IQCAL_STATUS_B2 (AR_SM2_BASE + 0x48c) #define AR_PHY_TX_IQCAL_CORR_COEFF_B2(_i) (AR_SM2_BASE + 0x450 + ((_i) << 2)) From 1a26cda8e0d954257ef2e4e732350232e1506a65 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 8 Jan 2013 20:57:53 +0530 Subject: [PATCH 267/478] ath9k_hw: Fix radio programming for AR9550 For AR9550, program the synth value based on the ref. clock. The logic for AR9550 is similar to AR9330, but keep the code separate since changes for AR9330 are required - which would be done later. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 31 +++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index ce19c09fa8e8..0c4c5a6ffa16 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -68,7 +68,7 @@ static const int m2ThreshExt_off = 127; static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) { u16 bMode, fracMode = 0, aModeRefSel = 0; - u32 freq, channelSel = 0, reg32 = 0; + u32 freq, chan_frac, div, channelSel = 0, reg32 = 0; struct chan_centers centers; int loadSynthChannel; @@ -77,9 +77,6 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) if (freq < 4800) { /* 2 GHz, fractional mode */ if (AR_SREV_9330(ah)) { - u32 chan_frac; - u32 div; - if (ah->is_clk_25mhz) div = 75; else @@ -89,34 +86,40 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) chan_frac = (((freq * 4) % div) * 0x20000) / div; channelSel = (channelSel << 17) | chan_frac; } else if (AR_SREV_9485(ah) || AR_SREV_9565(ah)) { - u32 chan_frac; - /* - * freq_ref = 40 / (refdiva >> amoderefsel); where refdiva=1 and amoderefsel=0 + * freq_ref = 40 / (refdiva >> amoderefsel); + * where refdiva=1 and amoderefsel=0 * ndiv = ((chan_mhz * 4) / 3) / freq_ref; * chansel = int(ndiv), chanfrac = (ndiv - chansel) * 0x20000 */ channelSel = (freq * 4) / 120; chan_frac = (((freq * 4) % 120) * 0x20000) / 120; channelSel = (channelSel << 17) | chan_frac; - } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) { + } else if (AR_SREV_9340(ah)) { if (ah->is_clk_25mhz) { - u32 chan_frac; - channelSel = (freq * 2) / 75; chan_frac = (((freq * 2) % 75) * 0x20000) / 75; channelSel = (channelSel << 17) | chan_frac; - } else + } else { channelSel = CHANSEL_2G(freq) >> 1; - } else + } + } else if (AR_SREV_9550(ah)) { + if (ah->is_clk_25mhz) + div = 75; + else + div = 120; + + channelSel = (freq * 4) / div; + chan_frac = (((freq * 4) % div) * 0x20000) / div; + channelSel = (channelSel << 17) | chan_frac; + } else { channelSel = CHANSEL_2G(freq); + } /* Set to 2G mode */ bMode = 1; } else { if ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) && ah->is_clk_25mhz) { - u32 chan_frac; - channelSel = freq / 75; chan_frac = ((freq % 75) * 0x20000) / 75; channelSel = (channelSel << 17) | chan_frac; From e93d083f42a126b5ad8137b5f0e8d6f900b332b8 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Tue, 8 Jan 2013 14:48:58 +0100 Subject: [PATCH 268/478] ath9k: add spectral scan feature Adds the spectral scan feature for ath9k. AR92xx and AR93xx chips are supported for now. The spectral scan is triggered by configuring a mode through a debugfs control file. Samples can be gathered via another relay debugfs file. Essentially, to try it out: echo chanscan > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl iw dev wlan0 scan cat /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan0 > samples echo disable > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl This feature is still experimental. The special "chanscan" mode is used to perform spectral scan while mac80211 is scanning for channels. To allow this, sw_scan_start/complete() ops have been added. The patch contains code snippets and information from Zefir Kurtisi and information provided by Adrian Chadd and Felix Fietkau. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_phy.c | 64 +++++++++ drivers/net/wireless/ath/ath9k/ar9003_phy.c | 64 +++++++++ drivers/net/wireless/ath/ath9k/ath9k.h | 152 ++++++++++++++++++++ drivers/net/wireless/ath/ath9k/debug.c | 115 +++++++++++++++ drivers/net/wireless/ath/ath9k/debug.h | 5 + drivers/net/wireless/ath/ath9k/hw.h | 39 +++++ drivers/net/wireless/ath/ath9k/init.c | 6 + drivers/net/wireless/ath/ath9k/mac.h | 3 +- drivers/net/wireless/ath/ath9k/main.c | 100 +++++++++++++ drivers/net/wireless/ath/ath9k/recv.c | 106 ++++++++++++++ 10 files changed, 653 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 8b119811b2d9..51b7c8130ae4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -555,6 +555,67 @@ static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval); } +void ar9002_hw_spectral_scan_config(struct ath_hw *ah, + struct ath_spec_scan *param) +{ + u8 count; + + if (!param->enabled) { + REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_ENABLE); + return; + } + REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_FFT_ENA); + REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE); + + if (param->short_repeat) + REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT); + else + REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT); + + /* on AR92xx, the highest bit of count will make the the chip send + * spectral samples endlessly. Check if this really was intended, + * and fix otherwise. + */ + count = param->count; + if (param->endless) + count = 0; + else if (count & 0x80) + count = 0x7f; + + REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_COUNT, count); + REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_PERIOD, param->period); + REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_FFT_PERIOD, param->fft_period); + + return; +} + +static void ar9002_hw_spectral_scan_trigger(struct ath_hw *ah) +{ + REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE); + /* Activate spectral scan */ + REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_ACTIVE); +} + +static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + /* Poll for spectral scan complete */ + if (!ath9k_hw_wait(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_ACTIVE, + 0, AH_WAIT_TIMEOUT)) { + ath_err(common, "spectral scan wait failed\n"); + return; + } +} + void ar9002_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -569,6 +630,9 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah) ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get; ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set; + ops->spectral_scan_config = ar9002_hw_spectral_scan_config; + ops->spectral_scan_trigger = ar9002_hw_spectral_scan_trigger; + ops->spectral_scan_wait = ar9002_hw_spectral_scan_wait; ar9002_hw_set_nf_limits(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 0c4c5a6ffa16..bf119a5f865a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1453,6 +1453,67 @@ set_rfmode: return 0; } +static void ar9003_hw_spectral_scan_config(struct ath_hw *ah, + struct ath_spec_scan *param) +{ + u8 count; + + if (!param->enabled) { + REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_ENABLE); + return; + } + + REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_FFT_ENA); + REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE); + + /* on AR93xx and newer, count = 0 will make the the chip send + * spectral samples endlessly. Check if this really was intended, + * and fix otherwise. + */ + count = param->count; + if (param->endless) + count = 0; + else if (param->count == 0) + count = 1; + + if (param->short_repeat) + REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT); + else + REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT); + + REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_COUNT, count); + REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_PERIOD, param->period); + REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_FFT_PERIOD, param->fft_period); + + return; +} + +static void ar9003_hw_spectral_scan_trigger(struct ath_hw *ah) +{ + /* Activate spectral scan */ + REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_ACTIVE); +} + +static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + /* Poll for spectral scan complete */ + if (!ath9k_hw_wait(ah, AR_PHY_SPECTRAL_SCAN, + AR_PHY_SPECTRAL_SCAN_ACTIVE, + 0, AH_WAIT_TIMEOUT)) { + ath_err(common, "spectral scan wait failed\n"); + return; + } +} + void ar9003_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -1486,6 +1547,9 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get; ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set; ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv; + ops->spectral_scan_config = ar9003_hw_spectral_scan_config; + ops->spectral_scan_trigger = ar9003_hw_spectral_scan_trigger; + ops->spectral_scan_wait = ar9003_hw_spectral_scan_wait; ar9003_hw_set_nf_limits(ah); ar9003_hw_set_radar_conf(ah); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 72501073b499..094a9322f092 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -670,6 +670,23 @@ struct ath9k_vif_iter_data { int nadhocs; /* number of adhoc vifs */ }; +/* enum spectral_mode: + * + * @SPECTRAL_DISABLED: spectral mode is disabled + * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with + * something else. + * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples + * is performed manually. + * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels + * during a channel scan. + */ +enum spectral_mode { + SPECTRAL_DISABLED = 0, + SPECTRAL_BACKGROUND, + SPECTRAL_MANUAL, + SPECTRAL_CHANSCAN, +}; + struct ath_softc { struct ieee80211_hw *hw; struct device *dev; @@ -738,6 +755,10 @@ struct ath_softc { u8 ant_tx, ant_rx; struct dfs_pattern_detector *dfs_detector; u32 wow_enabled; + /* relay(fs) channel for spectral scan */ + struct rchan *rfs_chan_spec_scan; + enum spectral_mode spectral_mode; + int scanning; #ifdef CONFIG_PM_SLEEP atomic_t wow_got_bmiss_intr; @@ -746,6 +767,133 @@ struct ath_softc { #endif }; +#define SPECTRAL_SCAN_BITMASK 0x10 +/* Radar info packet format, used for DFS and spectral formats. */ +struct ath_radar_info { + u8 pulse_length_pri; + u8 pulse_length_ext; + u8 pulse_bw_info; +} __packed; + +/* The HT20 spectral data has 4 bytes of additional information at it's end. + * + * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: all bins max_magnitude[9:2] + * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]} + * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) + */ +struct ath_ht20_mag_info { + u8 all_bins[3]; + u8 max_exp; +} __packed; + +#define SPECTRAL_HT20_NUM_BINS 56 + +/* WARNING: don't actually use this struct! MAC may vary the amount of + * data by -1/+2. This struct is for reference only. + */ +struct ath_ht20_fft_packet { + u8 data[SPECTRAL_HT20_NUM_BINS]; + struct ath_ht20_mag_info mag_info; + struct ath_radar_info radar_info; +} __packed; + +#define SPECTRAL_HT20_TOTAL_DATA_LEN (sizeof(struct ath_ht20_fft_packet)) + +/* Dynamic 20/40 mode: + * + * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: lower bins max_magnitude[9:2] + * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]} + * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: upper bins max_magnitude[9:2] + * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]} + * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) + */ +struct ath_ht20_40_mag_info { + u8 lower_bins[3]; + u8 upper_bins[3]; + u8 max_exp; +} __packed; + +#define SPECTRAL_HT20_40_NUM_BINS 128 + +/* WARNING: don't actually use this struct! MAC may vary the amount of + * data. This struct is for reference only. + */ +struct ath_ht20_40_fft_packet { + u8 data[SPECTRAL_HT20_40_NUM_BINS]; + struct ath_ht20_40_mag_info mag_info; + struct ath_radar_info radar_info; +} __packed; + + +#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet)) + +/* grabs the max magnitude from the all/upper/lower bins */ +static inline u16 spectral_max_magnitude(u8 *bins) +{ + return (bins[0] & 0xc0) >> 6 | + (bins[1] & 0xff) << 2 | + (bins[2] & 0x03) << 10; +} + +/* return the max magnitude from the all/upper/lower bins */ +static inline u8 spectral_max_index(u8 *bins) +{ + s8 m = (bins[2] & 0xfc) >> 2; + + /* TODO: this still doesn't always report the right values ... */ + if (m > 32) + m |= 0xe0; + else + m &= ~0xe0; + + return m + 29; +} + +/* return the bitmap weight from the all/upper/lower bins */ +static inline u8 spectral_bitmap_weight(u8 *bins) +{ + return bins[0] & 0x3f; +} + +/* FFT sample format given to userspace via debugfs. + * + * Please keep the type/length at the front position and change + * other fields after adding another sample type + * + * TODO: this might need rework when switching to nl80211-based + * interface. + */ +enum ath_fft_sample_type { + ATH_FFT_SAMPLE_HT20 = 0, +}; + +struct fft_sample_tlv { + u8 type; /* see ath_fft_sample */ + u16 length; + /* type dependent data follows */ +} __packed; + +struct fft_sample_ht20 { + struct fft_sample_tlv tlv; + + u8 __alignment; + + u16 freq; + s8 rssi; + s8 noise; + + u16 max_magnitude; + u8 max_index; + u8 bitmap_weight; + + u64 tsf; + + u16 data[SPECTRAL_HT20_NUM_BINS]; +} __packed; + void ath9k_tasklet(unsigned long data); int ath_cabq_update(struct ath_softc *); @@ -768,6 +916,10 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); void ath9k_reload_chainmask_settings(struct ath_softc *sc); bool ath9k_uses_beacons(int type); +void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw); +int ath9k_spectral_scan_config(struct ieee80211_hw *hw, + enum spectral_mode spectral_mode); + #ifdef CONFIG_ATH9K_PCI int ath_pci_init(void); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 13ff9edc2401..75b504ba3b3f 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "ath9k.h" @@ -966,6 +967,112 @@ static const struct file_operations fops_recv = { .llseek = default_llseek, }; +static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char *mode = ""; + unsigned int len; + + switch (sc->spectral_mode) { + case SPECTRAL_DISABLED: + mode = "disable"; + break; + case SPECTRAL_BACKGROUND: + mode = "background"; + break; + case SPECTRAL_CHANSCAN: + mode = "chanscan"; + break; + case SPECTRAL_MANUAL: + mode = "manual"; + break; + } + len = strlen(mode); + return simple_read_from_buffer(user_buf, count, ppos, mode, len); +} + +static ssize_t write_file_spec_scan_ctl(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (strncmp("trigger", buf, 7) == 0) { + ath9k_spectral_scan_trigger(sc->hw); + } else if (strncmp("background", buf, 9) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND); + ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n"); + } else if (strncmp("chanscan", buf, 8) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN); + ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n"); + } else if (strncmp("manual", buf, 6) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL); + ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n"); + } else if (strncmp("disable", buf, 7) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED); + ath_dbg(common, CONFIG, "spectral scan: disabled\n"); + } else { + return -EINVAL; + } + + return count; +} + +static const struct file_operations fops_spec_scan_ctl = { + .read = read_file_spec_scan_ctl, + .write = write_file_spec_scan_ctl, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static struct dentry *create_buf_file_handler(const char *filename, + struct dentry *parent, + umode_t mode, + struct rchan_buf *buf, + int *is_global) +{ + struct dentry *buf_file; + + buf_file = debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); + *is_global = 1; + return buf_file; +} + +static int remove_buf_file_handler(struct dentry *dentry) +{ + debugfs_remove(dentry); + + return 0; +} + +void ath_debug_send_fft_sample(struct ath_softc *sc, + struct fft_sample_tlv *fft_sample_tlv) +{ + if (!sc->rfs_chan_spec_scan) + return; + + relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, + fft_sample_tlv->length + sizeof(*fft_sample_tlv)); +} + +static struct rchan_callbacks rfs_spec_scan_cb = { + .create_buf_file = create_buf_file_handler, + .remove_buf_file = remove_buf_file_handler, +}; + + static ssize_t read_file_regidx(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1780,6 +1887,14 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_base_eeprom); debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_modal_eeprom); + sc->rfs_chan_spec_scan = relay_open("spectral_scan", + sc->debug.debugfs_phy, + 262144, 4, &rfs_spec_scan_cb, + NULL); + debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spec_scan_ctl); + #ifdef CONFIG_ATH9K_MAC_DEBUG debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_samps); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 375c3b46411e..b19bed7f74c2 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -23,6 +23,7 @@ struct ath_txq; struct ath_buf; +struct fft_sample_tlv; #ifdef CONFIG_ATH9K_DEBUGFS #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++ @@ -323,6 +324,10 @@ void ath9k_sta_remove_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); + +void ath_debug_send_fft_sample(struct ath_softc *sc, + struct fft_sample_tlv *fft_sample); + #else #define RX_STAT_INC(c) /* NOP */ diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 39a226f5eda4..d32ebf7a2414 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -657,6 +657,37 @@ struct ath_hw_private_ops { void (*ani_cache_ini_regs)(struct ath_hw *ah); }; +/** + * struct ath_spec_scan - parameters for Atheros spectral scan + * + * @enabled: enable/disable spectral scan + * @short_repeat: controls whether the chip is in spectral scan mode + * for 4 usec (enabled) or 204 usec (disabled) + * @count: number of scan results requested. There are special meanings + * in some chip revisions: + * AR92xx: highest bit set (>=128) for endless mode + * (spectral scan won't stopped until explicitly disabled) + * AR9300 and newer: 0 for endless mode + * @endless: true if endless mode is intended. Otherwise, count value is + * corrected to the next possible value. + * @period: time duration between successive spectral scan entry points + * (period*256*Tclk). Tclk = ath_common->clockrate + * @fft_period: PHY passes FFT frames to MAC every (fft_period+1)*4uS + * + * Note: Tclk = 40MHz or 44MHz depending upon operating mode. + * Typically it's 44MHz in 2/5GHz on later chips, but there's + * a "fast clock" check for this in 5GHz. + * + */ +struct ath_spec_scan { + bool enabled; + bool short_repeat; + bool endless; + u8 count; + u8 period; + u8 fft_period; +}; + /** * struct ath_hw_ops - callbacks used by hardware code and driver code * @@ -665,6 +696,10 @@ struct ath_hw_private_ops { * * @config_pci_powersave: * @calibrate: periodic calibration for NF, ANI, IQ, ADC gain, ADC-DC + * + * @spectral_scan_config: set parameters for spectral scan and enable/disable it + * @spectral_scan_trigger: trigger a spectral scan run + * @spectral_scan_wait: wait for a spectral scan run to finish */ struct ath_hw_ops { void (*config_pci_powersave)(struct ath_hw *ah, @@ -685,6 +720,10 @@ struct ath_hw_ops { void (*antdiv_comb_conf_set)(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf); void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable); + void (*spectral_scan_config)(struct ath_hw *ah, + struct ath_spec_scan *param); + void (*spectral_scan_trigger)(struct ath_hw *ah); + void (*spectral_scan_wait)(struct ath_hw *ah); }; struct ath_nf_limits { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 5c01f43c32b0..90cfcb3983d1 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "ath9k.h" @@ -916,6 +917,11 @@ static void ath9k_deinit_softc(struct ath_softc *sc) sc->dfs_detector->exit(sc->dfs_detector); ath9k_eeprom_release(sc); + + if (sc->rfs_chan_spec_scan) { + relay_close(sc->rfs_chan_spec_scan); + sc->rfs_chan_spec_scan = NULL; + } } void ath9k_deinit_device(struct ath_softc *sc) diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 4a745e68dd94..1ff817061ebc 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -226,7 +226,8 @@ enum ath9k_phyerr { ATH9K_PHYERR_HT_LENGTH_ILLEGAL = 35, ATH9K_PHYERR_HT_RATE_ILLEGAL = 36, - ATH9K_PHYERR_MAX = 37, + ATH9K_PHYERR_SPECTRAL = 38, + ATH9K_PHYERR_MAX = 39, }; struct ath_desc { diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e1fa70596e61..32417fd4bf5a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1075,6 +1075,86 @@ static void ath9k_disable_ps(struct ath_softc *sc) ath_dbg(common, PS, "PowerSave disabled\n"); } +void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + u32 rxfilter; + + if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { + ath_err(common, "spectrum analyzer not implemented on this hardware\n"); + return; + } + + ath9k_ps_wakeup(sc); + rxfilter = ath9k_hw_getrxfilter(ah); + ath9k_hw_setrxfilter(ah, rxfilter | + ATH9K_RX_FILTER_PHYRADAR | + ATH9K_RX_FILTER_PHYERR); + + /* TODO: usually this should not be neccesary, but for some reason + * (or in some mode?) the trigger must be called after the + * configuration, otherwise the register will have its values reset + * (on my ar9220 to value 0x01002310) + */ + ath9k_spectral_scan_config(hw, sc->spectral_mode); + ath9k_hw_ops(ah)->spectral_scan_trigger(ah); + ath9k_ps_restore(sc); +} + +int ath9k_spectral_scan_config(struct ieee80211_hw *hw, + enum spectral_mode spectral_mode) +{ + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_spec_scan param; + + if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { + ath_err(common, "spectrum analyzer not implemented on this hardware\n"); + return -1; + } + + /* NOTE: this will generate a few samples ... + * + * TODO: review default parameters, and/or define an interface to set + * them. + */ + param.enabled = 1; + param.short_repeat = true; + param.count = 8; + param.endless = false; + param.period = 0xFF; + param.fft_period = 0xF; + + switch (spectral_mode) { + case SPECTRAL_DISABLED: + param.enabled = 0; + break; + case SPECTRAL_BACKGROUND: + /* send endless samples. + * TODO: is this really useful for "background"? + */ + param.endless = 1; + break; + case SPECTRAL_CHANSCAN: + break; + case SPECTRAL_MANUAL: + break; + default: + return -1; + } + + ath9k_ps_wakeup(sc); + ath9k_hw_ops(ah)->spectral_scan_config(ah, ¶m); + ath9k_ps_restore(sc); + + sc->spectral_mode = spectral_mode; + + return 0; +} + static int ath9k_config(struct ieee80211_hw *hw, u32 changed) { struct ath_softc *sc = hw->priv; @@ -1188,6 +1268,11 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) */ if (old_pos >= 0) ath_update_survey_nf(sc, old_pos); + + /* perform spectral scan if requested. */ + if (sc->scanning && sc->spectral_mode == SPECTRAL_CHANSCAN) + ath9k_spectral_scan_trigger(hw); + } if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -2240,6 +2325,19 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) } #endif +static void ath9k_sw_scan_start(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + + sc->scanning = 1; +} + +static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + + sc->scanning = 0; +} struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, @@ -2286,4 +2384,6 @@ struct ieee80211_ops ath9k_ops = { .sta_add_debugfs = ath9k_sta_add_debugfs, .sta_remove_debugfs = ath9k_sta_remove_debugfs, #endif + .sw_scan_start = ath9k_sw_scan_start, + .sw_scan_complete = ath9k_sw_scan_complete, }; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 3d236aebf588..45f2d475ac1a 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -15,6 +15,7 @@ */ #include +#include #include "ath9k.h" #include "ar9003_mac.h" @@ -1025,6 +1026,108 @@ static void ath9k_rx_skb_postprocess(struct ath_common *common, rxs->flag &= ~RX_FLAG_DECRYPTED; } +static s8 fix_rssi_inv_only(u8 rssi_val) +{ + if (rssi_val == 128) + rssi_val = 0; + return (s8) rssi_val; +} + + +static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, + struct ath_rx_status *rs, u64 tsf) +{ +#ifdef CONFIG_ATH_DEBUG + struct ath_hw *ah = sc->sc_ah; + u8 bins[SPECTRAL_HT20_NUM_BINS]; + u8 *vdata = (u8 *)hdr; + struct fft_sample_ht20 fft_sample; + struct ath_radar_info *radar_info; + struct ath_ht20_mag_info *mag_info; + int len = rs->rs_datalen; + int i, dc_pos; + + /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer + * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT + * yet, but this is supposed to be possible as well. + */ + if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && + rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && + rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) + return; + + /* Variation in the data length is possible and will be fixed later. + * Note that we only support HT20 for now. + * + * TODO: add HT20_40 support as well. + */ + if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) || + (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1)) + return; + + /* check if spectral scan bit is set. This does not have to be checked + * if received through a SPECTRAL phy error, but shouldn't hurt. + */ + radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; + if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) + return; + + fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20; + fft_sample.tlv.length = sizeof(fft_sample) - sizeof(fft_sample.tlv); + + fft_sample.freq = ah->curchan->chan->center_freq; + fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); + fft_sample.noise = ah->noise; + + switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) { + case 0: + /* length correct, nothing to do. */ + memcpy(bins, vdata, SPECTRAL_HT20_NUM_BINS); + break; + case -1: + /* first byte missing, duplicate it. */ + memcpy(&bins[1], vdata, SPECTRAL_HT20_NUM_BINS - 1); + bins[0] = vdata[0]; + break; + case 2: + /* MAC added 2 extra bytes at bin 30 and 32, remove them. */ + memcpy(bins, vdata, 30); + bins[30] = vdata[31]; + memcpy(&bins[31], &vdata[33], SPECTRAL_HT20_NUM_BINS - 31); + break; + case 1: + /* MAC added 2 extra bytes AND first byte is missing. */ + bins[0] = vdata[0]; + memcpy(&bins[0], vdata, 30); + bins[31] = vdata[31]; + memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32); + break; + default: + return; + } + + /* DC value (value in the middle) is the blind spot of the spectral + * sample and invalid, interpolate it. + */ + dc_pos = SPECTRAL_HT20_NUM_BINS / 2; + bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2; + + /* mag data is at the end of the frame, in front of radar_info */ + mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + + /* Apply exponent and grab further auxiliary information. */ + for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) + fft_sample.data[i] = bins[i] << mag_info->max_exp; + + fft_sample.max_magnitude = spectral_max_magnitude(mag_info->all_bins); + fft_sample.max_index = spectral_max_index(mag_info->all_bins); + fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins); + fft_sample.tsf = tsf; + + ath_debug_send_fft_sample(sc, &fft_sample.tlv); +#endif +} + int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) { struct ath_buf *bf; @@ -1122,6 +1225,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) unlikely(tsf_lower - rs.rs_tstamp > 0x10000000)) rxs->mactime += 0x100000000ULL; + if ((rs.rs_status & ATH9K_RXERR_PHY)) + ath_process_fft(sc, hdr, &rs, rxs->mactime); + retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs, rxs, &decrypt_error); if (retval) From 0a9ab9bdb3e891762553f667066190c1d22ad62b Mon Sep 17 00:00:00 2001 From: Anderson Lizardo Date: Sun, 6 Jan 2013 18:28:53 -0400 Subject: [PATCH 269/478] Bluetooth: Fix incorrect strncpy() in hidp_setup_hid() The length parameter should be sizeof(req->name) - 1 because there is no guarantee that string provided by userspace will contain the trailing '\0'. Can be easily reproduced by manually setting req->name to 128 non-zero bytes prior to ioctl(HIDPCONNADD) and checking the device name setup on input subsystem: $ cat /sys/devices/pnp0/00\:04/tty/ttyS0/hci0/hci0\:1/input8/name AAAAAA[...]AAAAAAAAf0:af:f0:af:f0:af ("f0:af:f0:af:f0:af" is the device bluetooth address, taken from "phys" field in struct hid_device due to overflow.) Cc: stable@vger.kernel.org Signed-off-by: Anderson Lizardo Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hidp/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index b2bcbe2dc328..a7352ff3fd1e 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -931,7 +931,7 @@ static int hidp_setup_hid(struct hidp_session *session, hid->version = req->version; hid->country = req->country; - strncpy(hid->name, req->name, 128); + strncpy(hid->name, req->name, sizeof(req->name) - 1); snprintf(hid->phys, sizeof(hid->phys), "%pMR", &bt_sk(session->ctrl_sock->sk)->src); From 2c2d45bdcb7063ef58dc2c27d72026c394a8f584 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 27 Nov 2012 15:44:24 +0100 Subject: [PATCH 270/478] NFC: Add support for SO_TIMESTAMP LLCP socket option Set timestamp in sent and received sk_buffs. timestamp is then put in msghdr structure in llcp_sock_recvmsg(). Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/llcp/commands.c | 2 ++ net/nfc/llcp/llcp.c | 4 ++++ net/nfc/llcp/sock.c | 2 ++ 3 files changed, 8 insertions(+) diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index df24be48d4da..c6bc3bd95052 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -304,6 +304,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev) skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM); + __net_timestamp(skb); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); return nfc_data_exchange(dev, local->target_idx, skb, diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index ec43914c92a9..6c5971437826 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -668,6 +668,8 @@ static void nfc_llcp_tx_work(struct work_struct *work) if (ptype == LLCP_PDU_I) copy_skb = skb_copy(skb, GFP_ATOMIC); + __net_timestamp(skb); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); @@ -1245,6 +1247,8 @@ static void nfc_llcp_rx_work(struct work_struct *work) print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, true); + __net_timestamp(skb); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX); switch (ptype) { diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index fea22eb41b82..ba1900700958 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -678,6 +678,8 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, return -EFAULT; } + sock_recv_timestamp(msg, sk, skb); + if (sk->sk_type == SOCK_DGRAM && msg->msg_name) { struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb); struct sockaddr_nfc_llcp sockaddr; From 8d25ca799783788742bc7d9647eec44b6754766e Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:30 +0100 Subject: [PATCH 271/478] NFC: pn533: Remove in/out_maxlen as it is not used in_maxlen and out_maxlen was replaced with PN533_NORMAL_FRAME_MAX_LEN Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index ada681b01a17..cf94113b174a 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -349,11 +349,9 @@ struct pn533 { struct nfc_dev *nfc_dev; struct urb *out_urb; - int out_maxlen; struct pn533_frame *out_frame; struct urb *in_urb; - int in_maxlen; struct pn533_frame *in_frame; struct sk_buff_head resp_q; @@ -1454,8 +1452,9 @@ static int pn533_send_poll_frame(struct pn533 *dev) pn533_build_poll_frame(dev, dev->out_frame, cur_mod); rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen, pn533_poll_complete, - NULL, GFP_KERNEL); + PN533_NORMAL_FRAME_MAX_LEN, + pn533_poll_complete, + NULL, GFP_KERNEL); if (rc) nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc); @@ -1567,7 +1566,7 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev) pn533_tx_frame_finish(dev->out_frame); rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen); + PN533_NORMAL_FRAME_MAX_LEN); if (rc) return rc; @@ -1661,7 +1660,7 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, pn533_tx_frame_finish(dev->out_frame); rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen); + PN533_NORMAL_FRAME_MAX_LEN); if (rc) { nfc_dev_err(&dev->interface->dev, "Error when sending release" " command to the controller"); @@ -1822,8 +1821,9 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, pn533_tx_frame_finish(dev->out_frame); rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen, pn533_in_dep_link_up_complete, - cmd, GFP_KERNEL); + PN533_NORMAL_FRAME_MAX_LEN, + pn533_in_dep_link_up_complete, cmd, + GFP_KERNEL); if (rc < 0) kfree(cmd); @@ -2121,8 +2121,9 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) out_frame = (struct pn533_frame *) skb->data; rc = pn533_send_cmd_frame_async(dev, out_frame, dev->in_frame, - dev->in_maxlen, pn533_tm_send_complete, - skb, GFP_KERNEL); + PN533_NORMAL_FRAME_MAX_LEN, + pn533_tm_send_complete, skb, + GFP_KERNEL); if (rc) { nfc_dev_err(&dev->interface->dev, "Error %d when trying to send data", rc); @@ -2217,7 +2218,7 @@ static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, pn533_tx_frame_finish(dev->out_frame); rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen); + PN533_NORMAL_FRAME_MAX_LEN); return rc; } @@ -2238,7 +2239,7 @@ static int pn533_fw_reset(struct pn533 *dev) pn533_tx_frame_finish(dev->out_frame); rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen); + PN533_NORMAL_FRAME_MAX_LEN); return rc; } @@ -2359,16 +2360,11 @@ static int pn533_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - dev->in_maxlen = le16_to_cpu(endpoint->wMaxPacketSize); + if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint)) in_endpoint = endpoint->bEndpointAddress; - } - if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) { - dev->out_maxlen = - le16_to_cpu(endpoint->wMaxPacketSize); + if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) out_endpoint = endpoint->bEndpointAddress; - } } if (!in_endpoint || !out_endpoint) { @@ -2418,7 +2414,7 @@ static int pn533_probe(struct usb_interface *interface, pn533_tx_frame_finish(dev->out_frame); rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen); + PN533_NORMAL_FRAME_MAX_LEN); if (rc) goto destroy_wq; From a449ae1cbdcb5ab45c894362602b239e1d8d96c3 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:31 +0100 Subject: [PATCH 272/478] NFC: pn533: Remove unused definitions Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index cf94113b174a..ed8ecb3589be 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -41,11 +41,6 @@ #define SONY_VENDOR_ID 0x054c #define PASORI_PRODUCT_ID 0x02e1 -#define PN533_QUIRKS_TYPE_A BIT(0) -#define PN533_QUIRKS_TYPE_F BIT(1) -#define PN533_QUIRKS_DEP BIT(2) -#define PN533_QUIRKS_RAW_EXCHANGE BIT(3) - #define PN533_DEVICE_STD 0x1 #define PN533_DEVICE_PASORI 0x2 @@ -128,9 +123,6 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_CMD_MI_MASK 0x40 #define PN533_CMD_RET_SUCCESS 0x00 -/* PN533 status codes */ -#define PN533_STATUS_TARGET_RELEASED 0x29 - struct pn533; typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg, From b1bb290ac2308616b24c5986665d7199ff7b9df3 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:32 +0100 Subject: [PATCH 273/478] NFC: pn533: Add frame header length define PN533_CMD_DATAEXCH_HEAD_LEN includes a frame header length which is not seen at a glance. It can be missleading, so split it and define the frame header length explicitly. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 53 ++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index ed8ecb3589be..7040106cb7f2 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -82,10 +82,12 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_NORMAL_FRAME_MAX_LEN 262 /* 6 (PREAMBLE, SOF, LEN, LCS, TFI) 254 (DATA) 2 (DCS, postamble) */ +#define PN533_FRAME_HEADER_LEN (sizeof(struct pn533_frame) \ + + 2) /* data[0] TFI, data[1] CC */ +#define PN533_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/ -#define PN533_FRAME_TAIL_SIZE 2 #define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \ - PN533_FRAME_TAIL_SIZE) + PN533_FRAME_TAIL_LEN) #define PN533_FRAME_ACK_SIZE (sizeof(struct pn533_frame) + 1) #define PN533_FRAME_CHECKSUM(f) (f->data[f->datalen]) #define PN533_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1]) @@ -1233,7 +1235,7 @@ static int pn533_init_target_frame(struct pn533_frame *frame, return 0; } -#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3) +#define PN533_CMD_DATAEXCH_HEAD_LEN 1 #define PN533_CMD_DATAEXCH_DATA_MAXLEN 262 static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg, u8 *params, int params_len) @@ -1261,8 +1263,9 @@ static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg, } skb_put(skb_resp, PN533_FRAME_SIZE(in_frame)); - skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN); - skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE); + skb_pull(skb_resp, + PN533_FRAME_HEADER_LEN + PN533_CMD_DATAEXCH_HEAD_LEN); + skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_LEN); return nfc_tm_data_received(dev->nfc_dev, skb_resp); } @@ -1276,9 +1279,10 @@ static void pn533_wq_tg_get_data(struct work_struct *work) nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN + - PN533_CMD_DATAEXCH_DATA_MAXLEN + - PN533_FRAME_TAIL_SIZE; + skb_resp_len = PN533_FRAME_HEADER_LEN + + PN533_CMD_DATAEXCH_HEAD_LEN + + PN533_CMD_DATAEXCH_DATA_MAXLEN + + PN533_FRAME_TAIL_LEN; skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL); if (!skb_resp) @@ -1859,11 +1863,12 @@ static int pn533_build_tx_frame(struct pn533 *dev, struct sk_buff *skb, return -ENOSYS; } + skb_push(skb, PN533_FRAME_HEADER_LEN); + if (target == true) { switch (dev->device_type) { case PN533_DEVICE_PASORI: if (dev->tgt_active_prot == NFC_PROTO_FELICA) { - skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1); out_frame = (struct pn533_frame *) skb->data; pn533_tx_frame_init(out_frame, PN533_CMD_IN_COMM_THRU); @@ -1895,7 +1900,7 @@ static int pn533_build_tx_frame(struct pn533 *dev, struct sk_buff *skb, out_frame->datalen += payload_len; pn533_tx_frame_finish(out_frame); - skb_put(skb, PN533_FRAME_TAIL_SIZE); + skb_put(skb, PN533_FRAME_TAIL_LEN); return 0; } @@ -1975,8 +1980,9 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, } skb_put(skb_resp, PN533_FRAME_SIZE(in_frame)); + skb_pull(skb_resp, PN533_FRAME_HEADER_LEN); skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN); - skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE); + skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_LEN); skb_queue_tail(&dev->resp_q, skb_resp); if (status & PN533_CMD_MI_MASK) { @@ -2024,9 +2030,10 @@ static int pn533_transceive(struct nfc_dev *nfc_dev, if (rc) goto error; - skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN + - PN533_CMD_DATAEXCH_DATA_MAXLEN + - PN533_FRAME_TAIL_SIZE; + skb_resp_len = PN533_FRAME_HEADER_LEN + + PN533_CMD_DATAEXCH_HEAD_LEN + + PN533_CMD_DATAEXCH_DATA_MAXLEN + + PN533_FRAME_TAIL_LEN; skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL); if (!skb_resp) { @@ -2143,20 +2150,25 @@ static void pn533_wq_mi_recv(struct work_struct *work) nfc_dev_dbg(&dev->interface->dev, "%s", __func__); /* This is a zero payload size skb */ - skb_cmd = alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN + PN533_FRAME_TAIL_SIZE, + skb_cmd = alloc_skb(PN533_FRAME_HEADER_LEN + + PN533_CMD_DATAEXCH_HEAD_LEN + + PN533_FRAME_TAIL_LEN, GFP_KERNEL); if (skb_cmd == NULL) goto error_cmd; - skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN); + skb_reserve(skb_cmd, + PN533_FRAME_HEADER_LEN + PN533_CMD_DATAEXCH_HEAD_LEN); rc = pn533_build_tx_frame(dev, skb_cmd, true); if (rc) goto error_frame; - skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN + - PN533_CMD_DATAEXCH_DATA_MAXLEN + - PN533_FRAME_TAIL_SIZE; + skb_resp_len = PN533_FRAME_HEADER_LEN + + PN533_CMD_DATAEXCH_HEAD_LEN + + PN533_CMD_DATAEXCH_DATA_MAXLEN + + PN533_FRAME_TAIL_LEN; + skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL); if (!skb_resp) { rc = -ENOMEM; @@ -2433,8 +2445,9 @@ static int pn533_probe(struct usb_interface *interface, } dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols, + PN533_FRAME_HEADER_LEN + PN533_CMD_DATAEXCH_HEAD_LEN, - PN533_FRAME_TAIL_SIZE); + PN533_FRAME_TAIL_LEN); if (!dev->nfc_dev) goto destroy_wq; From d94ea4f54516b32affeda7ef097b93bca6e6fd32 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:33 +0100 Subject: [PATCH 274/478] NFC: pn533: Remove pointless flags param __pn533_send_cmd_frame_async() is called when lock is held so GFP_KERNEL flag will be always used. Thus, having extra param does not optimise the code. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 7040106cb7f2..5f3459d11e73 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -392,7 +392,6 @@ struct pn533_cmd { int in_frame_len; pn533_cmd_complete_t cmd_complete; void *arg; - gfp_t flags; }; struct pn533_frame { @@ -646,7 +645,7 @@ static int __pn533_send_cmd_frame_async(struct pn533 *dev, struct pn533_frame *in_frame, int in_frame_len, pn533_cmd_complete_t cmd_complete, - void *arg, gfp_t flags) + void *arg) { int rc; @@ -664,11 +663,11 @@ static int __pn533_send_cmd_frame_async(struct pn533 *dev, dev->in_urb->transfer_buffer = in_frame; dev->in_urb->transfer_buffer_length = in_frame_len; - rc = usb_submit_urb(dev->out_urb, flags); + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); if (rc) return rc; - rc = pn533_submit_urb_for_ack(dev, flags); + rc = pn533_submit_urb_for_ack(dev, GFP_KERNEL); if (rc) goto error; @@ -700,7 +699,7 @@ static void pn533_wq_cmd(struct work_struct *work) __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame, cmd->in_frame_len, cmd->cmd_complete, - cmd->arg, cmd->flags); + cmd->arg); kfree(cmd); } @@ -710,7 +709,7 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, struct pn533_frame *in_frame, int in_frame_len, pn533_cmd_complete_t cmd_complete, - void *arg, gfp_t flags) + void *arg) { struct pn533_cmd *cmd; int rc = 0; @@ -722,7 +721,7 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, if (!dev->cmd_pending) { rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, in_frame_len, cmd_complete, - arg, flags); + arg); if (!rc) dev->cmd_pending = 1; @@ -731,7 +730,7 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__); - cmd = kzalloc(sizeof(struct pn533_cmd), flags); + cmd = kzalloc(sizeof(struct pn533_cmd), GFP_KERNEL); if (!cmd) { rc = -ENOMEM; goto unlock; @@ -743,7 +742,6 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, cmd->in_frame_len = in_frame_len; cmd->cmd_complete = cmd_complete; cmd->arg = arg; - cmd->flags = flags; list_add_tail(&cmd->queue, &dev->cmd_queue); @@ -788,7 +786,7 @@ static int pn533_send_cmd_frame_sync(struct pn533 *dev, init_completion(&arg.done); rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, in_frame_len, - pn533_sync_cmd_complete, &arg, GFP_KERNEL); + pn533_sync_cmd_complete, &arg); if (rc) return rc; @@ -1296,7 +1294,7 @@ static void pn533_wq_tg_get_data(struct work_struct *work) pn533_send_cmd_frame_async(dev, dev->out_frame, in_frame, skb_resp_len, pn533_tm_get_data_complete, - skb_resp, GFP_KERNEL); + skb_resp); return; } @@ -1450,7 +1448,7 @@ static int pn533_send_poll_frame(struct pn533 *dev) rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, PN533_NORMAL_FRAME_MAX_LEN, pn533_poll_complete, - NULL, GFP_KERNEL); + NULL); if (rc) nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc); @@ -1818,8 +1816,7 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, PN533_NORMAL_FRAME_MAX_LEN, - pn533_in_dep_link_up_complete, cmd, - GFP_KERNEL); + pn533_in_dep_link_up_complete, cmd); if (rc < 0) kfree(cmd); @@ -2056,8 +2053,7 @@ static int pn533_transceive(struct nfc_dev *nfc_dev, arg->cb_context = cb_context; rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, skb_resp_len, - pn533_data_exchange_complete, arg, - GFP_KERNEL); + pn533_data_exchange_complete, arg); if (rc) { nfc_dev_err(&dev->interface->dev, "Error %d when trying to" " perform data_exchange", rc); @@ -2121,8 +2117,7 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) rc = pn533_send_cmd_frame_async(dev, out_frame, dev->in_frame, PN533_NORMAL_FRAME_MAX_LEN, - pn533_tm_send_complete, skb, - GFP_KERNEL); + pn533_tm_send_complete, skb); if (rc) { nfc_dev_err(&dev->interface->dev, "Error %d when trying to send data", rc); @@ -2184,7 +2179,7 @@ static void pn533_wq_mi_recv(struct work_struct *work) rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, skb_resp_len, pn533_data_exchange_complete, - dev->cmd_complete_arg, GFP_KERNEL); + dev->cmd_complete_arg); if (!rc) return; From aada17ac70e33b127c8f8dd425fc735cc0d30e25 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:34 +0100 Subject: [PATCH 275/478] NFC: pn533: Add a new pn533_send_cmd_async iface This is intendend to replace pn533_send_cmd_frame_async() which requires from the caller to create a complete frame. The new function constructs a frame and sends it out which hides the frame logic and avoid code duplication. The caller has to allocate skb and put its payload there, and finally provide the skb together with a complete cb to pn533_send_cmd_async(). Response skb is allocated by the core part and pass to the caller cb. Next, the caller has to free it when is not needed anymore or pass it up to the stack. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 167 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 5f3459d11e73..89f747479a4f 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -117,6 +117,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_CMD_TG_INIT_AS_TARGET 0x8c #define PN533_CMD_TG_GET_DATA 0x86 #define PN533_CMD_TG_SET_DATA 0x8e +#define PN533_CMD_UNDEF 0xff #define PN533_CMD_RESPONSE(cmd) (cmd + 1) @@ -130,6 +131,9 @@ struct pn533; typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg, u8 *params, int params_len); +typedef int (*pn533_send_async_complete_t) (struct pn533 *dev, void *arg, + struct sk_buff *resp); + /* structs for pn533 commands */ /* PN533_CMD_GET_FIRMWARE_VERSION */ @@ -390,6 +394,9 @@ struct pn533_cmd { struct pn533_frame *out_frame; struct pn533_frame *in_frame; int in_frame_len; + u8 cmd_code; + struct sk_buff *req; + struct sk_buff *resp; pn533_cmd_complete_t cmd_complete; void *arg; }; @@ -678,6 +685,151 @@ error: return rc; } +static void pn533_build_cmd_frame(u8 cmd_code, struct sk_buff *skb) +{ + struct pn533_frame *frame; + /* payload is already there, just update datalen */ + int payload_len = skb->len; + + skb_push(skb, PN533_FRAME_HEADER_LEN); + skb_put(skb, PN533_FRAME_TAIL_LEN); + + frame = (struct pn533_frame *)skb->data; + + pn533_tx_frame_init(frame, cmd_code); + frame->datalen += payload_len; + pn533_tx_frame_finish(frame); +} + +struct pn533_send_async_complete_arg { + pn533_send_async_complete_t complete_cb; + void *complete_cb_context; + struct sk_buff *resp; + struct sk_buff *req; +}; + +static int pn533_send_async_complete(struct pn533 *dev, void *_arg, u8 *params, + int params_len) +{ + struct pn533_send_async_complete_arg *arg = _arg; + + struct sk_buff *req = arg->req; + struct sk_buff *resp = arg->resp; + + struct pn533_frame *frame = (struct pn533_frame *)resp->data; + int rc; + + dev_kfree_skb(req); + + if (params_len < 0) { + nfc_dev_err(&dev->interface->dev, + "Error %d when starting as a target", + params_len); + + arg->complete_cb(dev, arg->complete_cb_context, + ERR_PTR(params_len)); + rc = params_len; + dev_kfree_skb(resp); + goto out; + } + + skb_put(resp, PN533_FRAME_SIZE(frame)); + skb_pull(resp, PN533_FRAME_HEADER_LEN); + skb_trim(resp, resp->len - PN533_FRAME_TAIL_LEN); + + rc = arg->complete_cb(dev, arg->complete_cb_context, resp); + +out: + kfree(arg); + return rc; +} + +static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req, struct sk_buff *resp, + int resp_len, + pn533_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct pn533_cmd *cmd; + struct pn533_send_async_complete_arg *arg; + int rc = 0; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + arg = kzalloc(sizeof(arg), GFP_KERNEL); + if (!arg) + return -ENOMEM; + + arg->complete_cb = complete_cb; + arg->complete_cb_context = complete_cb_context; + arg->resp = resp; + arg->req = req; + + pn533_build_cmd_frame(cmd_code, req); + + mutex_lock(&dev->cmd_lock); + + if (!dev->cmd_pending) { + rc = __pn533_send_cmd_frame_async(dev, + (struct pn533_frame *)req->data, + (struct pn533_frame *)resp->data, + resp_len, pn533_send_async_complete, + arg); + if (rc) + goto error; + + dev->cmd_pending = 1; + goto unlock; + } + + nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__); + + cmd = kzalloc(sizeof(struct pn533_cmd), GFP_KERNEL); + if (!cmd) { + rc = -ENOMEM; + goto error; + } + + INIT_LIST_HEAD(&cmd->queue); + cmd->cmd_code = cmd_code; + cmd->req = req; + cmd->resp = resp; + cmd->arg = arg; + + list_add_tail(&cmd->queue, &dev->cmd_queue); + + goto unlock; + +error: + kfree(arg); +unlock: + mutex_unlock(&dev->cmd_lock); + return rc; +} + +static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req, + pn533_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct sk_buff *resp; + int rc; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + resp = alloc_skb(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + rc = __pn533_send_async(dev, cmd_code, req, resp, + PN533_NORMAL_FRAME_MAX_LEN, + complete_cb, complete_cb_context); + if (rc) + dev_kfree_skb(resp); + + return rc; +} + static void pn533_wq_cmd(struct work_struct *work) { struct pn533 *dev = container_of(work, struct pn533, cmd_work); @@ -697,9 +849,17 @@ static void pn533_wq_cmd(struct work_struct *work) mutex_unlock(&dev->cmd_lock); - __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame, - cmd->in_frame_len, cmd->cmd_complete, - cmd->arg); + if (cmd->cmd_code != PN533_CMD_UNDEF) + __pn533_send_cmd_frame_async(dev, + (struct pn533_frame *)cmd->req->data, + (struct pn533_frame *)cmd->resp->data, + PN533_NORMAL_FRAME_MAX_LEN, + pn533_send_async_complete, + cmd->arg); + else + __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame, + cmd->in_frame_len, + cmd->cmd_complete, cmd->arg); kfree(cmd); } @@ -740,6 +900,7 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, cmd->out_frame = out_frame; cmd->in_frame = in_frame; cmd->in_frame_len = in_frame_len; + cmd->cmd_code = PN533_CMD_UNDEF; cmd->cmd_complete = cmd_complete; cmd->arg = arg; From 15461aeb53ae2d3bbde6b374de913e3df81d1ad0 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:35 +0100 Subject: [PATCH 276/478] NFC: pn533: Add a new pn533_send_data_async iface This iface is intended to be used with DEP transfers. It differs from pn533_send_cmd_async() in the way the response skb is allocated. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 89f747479a4f..0e9bf9dfbbb5 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -86,6 +86,12 @@ MODULE_DEVICE_TABLE(usb, pn533_table); + 2) /* data[0] TFI, data[1] CC */ #define PN533_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/ +/* + * Max extended frame payload len, excluding TFI and CC + * which are already in PN533_FRAME_HEADER_LEN. + */ +#define PN533_FRAME_MAX_PAYLOAD_LEN 263 + #define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \ PN533_FRAME_TAIL_LEN) #define PN533_FRAME_ACK_SIZE (sizeof(struct pn533_frame) + 1) @@ -807,6 +813,31 @@ unlock: return rc; } +static int pn533_send_data_async(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req, + pn533_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct sk_buff *resp; + int rc; + int resp_len = PN533_FRAME_HEADER_LEN + + PN533_FRAME_MAX_PAYLOAD_LEN + + PN533_FRAME_TAIL_LEN; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + resp = nfc_alloc_recv_skb(resp_len, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + rc = __pn533_send_async(dev, cmd_code, req, resp, resp_len, complete_cb, + complete_cb_context); + if (rc) + dev_kfree_skb(resp); + + return rc; +} + static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code, struct sk_buff *req, pn533_send_async_complete_t complete_cb, From 94c5c1561dde894766b1a9135e47b056fdbe13a4 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:36 +0100 Subject: [PATCH 277/478] NFC: pn533: Add a new pn533_send_sync iface It is intended to replace pn533_send_cmd_frame_sync() iface which requires from the caller to create complete frame. The new function constructs a complete frame itself and sends it out in sync manner. This way frame logic is hidden from the caller. pn533_send_cmd_sync() returns ERR_PTR in case of an error or a pointer to valid response sk_buff otherwise. The pointer must be freed by the caller when it's been consumed. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 55 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 0e9bf9dfbbb5..94c88bb601cd 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -945,6 +945,7 @@ unlock: struct pn533_sync_cmd_response { int rc; + struct sk_buff *resp; struct completion done; }; @@ -965,6 +966,58 @@ static int pn533_sync_cmd_complete(struct pn533 *dev, void *_arg, return 0; } +static int pn533_send_sync_complete(struct pn533 *dev, void *_arg, + struct sk_buff *resp) +{ + struct pn533_sync_cmd_response *arg = _arg; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + arg->resp = resp; + complete(&arg->done); + + return 0; +} + +/* pn533_send_cmd_sync + * + * Please note the req parameter is freed inside the function to + * limit a number of return value interpretations by the caller. + * + * 1. negative in case of error during TX path -> req should be freed + * + * 2. negative in case of error during RX path -> req should not be freed + * as it's been already freed at the begining of RX path by + * async_complete_cb. + * + * 3. valid pointer in case of succesfult RX path + * + * A caller has to check a return value with IS_ERR macro. If the test pass, + * the returned pointer is valid. + * + * */ +static struct sk_buff *pn533_send_cmd_sync(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req) +{ + int rc; + struct pn533_sync_cmd_response arg; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + init_completion(&arg.done); + + rc = pn533_send_cmd_async(dev, cmd_code, req, + pn533_send_sync_complete, &arg); + if (rc) { + dev_kfree_skb(req); + return ERR_PTR(rc); + } + + wait_for_completion(&arg.done); + + return arg.resp; +} + static int pn533_send_cmd_frame_sync(struct pn533 *dev, struct pn533_frame *out_frame, struct pn533_frame *in_frame, @@ -2432,7 +2485,7 @@ static int pn533_fw_reset(struct pn533 *dev) rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, PN533_NORMAL_FRAME_MAX_LEN); - return rc; + return 0; } static struct nfc_ops pn533_nfc_ops = { From d22b2db69035ae3b8f71a58dfe0bd10ae1ee58d5 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:37 +0100 Subject: [PATCH 278/478] NFC: pn533: Add pn533_alloc_skb for req allocation Allocate sk_buff for the request. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 94c88bb601cd..c34acd0dc5ff 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1062,6 +1062,20 @@ static void pn533_send_complete(struct urb *urb) } } +static struct sk_buff *pn533_alloc_skb(unsigned int size) +{ + struct sk_buff *skb; + + skb = alloc_skb(PN533_FRAME_HEADER_LEN + + size + + PN533_FRAME_TAIL_LEN, GFP_KERNEL); + + if (skb) + skb_reserve(skb, PN533_FRAME_HEADER_LEN); + + return skb; +} + struct pn533_target_type_a { __be16 sens_res; u8 sel_res; @@ -2390,15 +2404,11 @@ static void pn533_wq_mi_recv(struct work_struct *work) nfc_dev_dbg(&dev->interface->dev, "%s", __func__); /* This is a zero payload size skb */ - skb_cmd = alloc_skb(PN533_FRAME_HEADER_LEN + - PN533_CMD_DATAEXCH_HEAD_LEN + - PN533_FRAME_TAIL_LEN, - GFP_KERNEL); + skb_cmd = pn533_alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN); if (skb_cmd == NULL) goto error_cmd; - skb_reserve(skb_cmd, - PN533_FRAME_HEADER_LEN + PN533_CMD_DATAEXCH_HEAD_LEN); + skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN); rc = pn533_build_tx_frame(dev, skb_cmd, true); if (rc) From cb950d9304b2965bef07e26d6c0eb34a0b1f75fe Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 26 Nov 2012 14:18:38 +0100 Subject: [PATCH 279/478] NFC: pn533: Remove pn533_send_cmd_frame_sync Remove obsolate pn533_send_cmd_frame_sync() and use previously added new iface for sync send. The new interface require the use of individual skb for each cmd which removes some memcpy calls and hides frame logic. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 201 +++++++++++++++++++------------------------- 1 file changed, 87 insertions(+), 114 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index c34acd0dc5ff..e5615f538aaa 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -286,11 +286,6 @@ const struct pn533_poll_modulations poll_mod[] = { /* PN533_CMD_IN_ATR */ -struct pn533_cmd_activate_param { - u8 tg; - u8 next; -} __packed; - struct pn533_cmd_activate_response { u8 status; u8 nfcid3t[10]; @@ -944,28 +939,10 @@ unlock: } struct pn533_sync_cmd_response { - int rc; struct sk_buff *resp; struct completion done; }; -static int pn533_sync_cmd_complete(struct pn533 *dev, void *_arg, - u8 *params, int params_len) -{ - struct pn533_sync_cmd_response *arg = _arg; - - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - - arg->rc = 0; - - if (params_len < 0) /* error */ - arg->rc = params_len; - - complete(&arg->done); - - return 0; -} - static int pn533_send_sync_complete(struct pn533 *dev, void *_arg, struct sk_buff *resp) { @@ -1018,28 +995,6 @@ static struct sk_buff *pn533_send_cmd_sync(struct pn533 *dev, u8 cmd_code, return arg.resp; } -static int pn533_send_cmd_frame_sync(struct pn533 *dev, - struct pn533_frame *out_frame, - struct pn533_frame *in_frame, - int in_frame_len) -{ - int rc; - struct pn533_sync_cmd_response arg; - - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - - init_completion(&arg.done); - - rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, in_frame_len, - pn533_sync_cmd_complete, &arg); - if (rc) - return rc; - - wait_for_completion(&arg.done); - - return arg.rc; -} - static void pn533_send_complete(struct urb *urb) { struct pn533 *dev = urb->context; @@ -1801,38 +1756,37 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev) static int pn533_activate_target_nfcdep(struct pn533 *dev) { - struct pn533_cmd_activate_param param; - struct pn533_cmd_activate_response *resp; + struct pn533_cmd_activate_response *rsp; u16 gt_len; int rc; + struct sk_buff *skb; + struct sk_buff *resp; + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_ATR); + skb = pn533_alloc_skb(sizeof(u8) * 2); /*TG + Next*/ + if (!skb) + return -ENOMEM; - param.tg = 1; - param.next = 0; - memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), ¶m, - sizeof(struct pn533_cmd_activate_param)); - dev->out_frame->datalen += sizeof(struct pn533_cmd_activate_param); + *skb_put(skb, sizeof(u8)) = 1; /* TG */ + *skb_put(skb, sizeof(u8)) = 0; /* Next */ - pn533_tx_frame_finish(dev->out_frame); + resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_ATR, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); - rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - PN533_NORMAL_FRAME_MAX_LEN); - if (rc) - return rc; - - resp = (struct pn533_cmd_activate_response *) - PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame); - rc = resp->status & PN533_CMD_RET_MASK; + rsp = (struct pn533_cmd_activate_response *) resp->data; + rc = rsp->status & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) + dev_kfree_skb(resp); return -EIO; /* ATR_RES general bytes are located at offset 16 */ - gt_len = PN533_FRAME_CMD_PARAMS_LEN(dev->in_frame) - 16; - rc = nfc_set_remote_general_bytes(dev->nfc_dev, resp->gt, gt_len); + gt_len = resp->len - 16; + rc = nfc_set_remote_general_bytes(dev->nfc_dev, rsp->gt, gt_len); + dev_kfree_skb(resp); return rc; } @@ -1889,8 +1843,10 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, struct nfc_target *target) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); - u8 tg; - u8 status; + + struct sk_buff *skb; + struct sk_buff *resp; + int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); @@ -1901,31 +1857,24 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, } dev->tgt_active_prot = 0; - skb_queue_purge(&dev->resp_q); - pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_RELEASE); - - tg = 1; - memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), &tg, sizeof(u8)); - dev->out_frame->datalen += sizeof(u8); - - pn533_tx_frame_finish(dev->out_frame); - - rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - PN533_NORMAL_FRAME_MAX_LEN); - if (rc) { - nfc_dev_err(&dev->interface->dev, "Error when sending release" - " command to the controller"); + skb = pn533_alloc_skb(sizeof(u8)); + if (!skb) return; - } - status = PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame)[0]; - rc = status & PN533_CMD_RET_MASK; + *skb_put(skb, 1) = 1; /* TG*/ + + resp = pn533_send_cmd_sync(dev, PN533_CMD_IN_RELEASE, skb); + if (IS_ERR(resp)) + return; + + rc = resp->data[0] & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) nfc_dev_err(&dev->interface->dev, "Error 0x%x when releasing" " the target", rc); + dev_kfree_skb(resp); return; } @@ -2457,43 +2406,71 @@ error_cmd: static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, u8 cfgdata_len) { - int rc; - u8 *params; + struct sk_buff *skb; + struct sk_buff *resp; + + int skb_len; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - pn533_tx_frame_init(dev->out_frame, PN533_CMD_RF_CONFIGURATION); + skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */ - params = PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame); - params[0] = cfgitem; - memcpy(¶ms[1], cfgdata, cfgdata_len); - dev->out_frame->datalen += (1 + cfgdata_len); + skb = pn533_alloc_skb(skb_len); + if (!skb) + return -ENOMEM; - pn533_tx_frame_finish(dev->out_frame); + *skb_put(skb, sizeof(cfgitem)) = cfgitem; + memcpy(skb_put(skb, cfgdata_len), cfgdata, cfgdata_len); - rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - PN533_NORMAL_FRAME_MAX_LEN); + resp = pn533_send_cmd_sync(dev, PN533_CMD_RF_CONFIGURATION, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); - return rc; + dev_kfree_skb(resp); + return 0; +} + +static int pn533_get_firmware_version(struct pn533 *dev, + struct pn533_fw_version *fv) +{ + struct sk_buff *skb; + struct sk_buff *resp; + + skb = pn533_alloc_skb(0); + if (!skb) + return -ENOMEM; + + resp = pn533_send_cmd_sync(dev, PN533_CMD_GET_FIRMWARE_VERSION, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + fv->ic = resp->data[0]; + fv->ver = resp->data[1]; + fv->rev = resp->data[2]; + fv->support = resp->data[3]; + + dev_kfree_skb(resp); + return 0; } static int pn533_fw_reset(struct pn533 *dev) { - int rc; - u8 *params; + struct sk_buff *skb; + struct sk_buff *resp; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - pn533_tx_frame_init(dev->out_frame, 0x18); + skb = pn533_alloc_skb(sizeof(u8)); + if (!skb) + return -ENOMEM; - params = PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame); - params[0] = 0x1; - dev->out_frame->datalen += 1; + *skb_put(skb, sizeof(u8)) = 0x1; - pn533_tx_frame_finish(dev->out_frame); + resp = pn533_send_cmd_sync(dev, 0x18, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); - rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - PN533_NORMAL_FRAME_MAX_LEN); + dev_kfree_skb(resp); return 0; } @@ -2592,7 +2569,7 @@ static int pn533_setup(struct pn533 *dev) static int pn533_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct pn533_fw_version *fw_ver; + struct pn533_fw_version fw_ver; struct pn533 *dev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; @@ -2664,18 +2641,14 @@ static int pn533_probe(struct usb_interface *interface, usb_set_intfdata(interface, dev); - pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION); - pn533_tx_frame_finish(dev->out_frame); - - rc = pn533_send_cmd_frame_sync(dev, dev->out_frame, dev->in_frame, - PN533_NORMAL_FRAME_MAX_LEN); - if (rc) + memset(&fw_ver, 0, sizeof(fw_ver)); + rc = pn533_get_firmware_version(dev, &fw_ver); + if (rc < 0) goto destroy_wq; - fw_ver = (struct pn533_fw_version *) - PN533_FRAME_CMD_PARAMS_PTR(dev->in_frame); - nfc_dev_info(&dev->interface->dev, "NXP PN533 firmware ver %d.%d now" - " attached", fw_ver->ver, fw_ver->rev); + nfc_dev_info(&dev->interface->dev, + "NXP PN533 firmware ver %d.%d now attached", + fw_ver.ver, fw_ver.rev); dev->device_type = id->driver_info; switch (dev->device_type) { From 2593c2c6ec236c6aedbb8182d103185ab6580285 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 7 Dec 2012 15:55:19 +0100 Subject: [PATCH 280/478] NFC: llcp: Remove the tx backlog queue Not only it was improperly use to queue backlogged RX skbuffs, but it was also not processed at all. If the socket receive queue is full we simply drop the incoming packets. Signed-off-by: Samuel Ortiz --- net/nfc/llcp/llcp.c | 5 ++--- net/nfc/llcp/llcp.h | 1 - net/nfc/llcp/sock.c | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 6c5971437826..003c82fe8bd7 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -54,7 +54,6 @@ static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock) skb_queue_purge(&sock->tx_queue); skb_queue_purge(&sock->tx_pending_queue); - skb_queue_purge(&sock->tx_backlog_queue); if (local == NULL) return; @@ -785,7 +784,7 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local, skb_pull(skb, LLCP_HEADER_SIZE); if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { pr_err("receive queue is full\n"); - skb_queue_head(&llcp_sock->tx_backlog_queue, skb); + kfree_skb(skb); } nfc_llcp_sock_put(llcp_sock); @@ -980,7 +979,7 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local, skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE); if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { pr_err("receive queue is full\n"); - skb_queue_head(&llcp_sock->tx_backlog_queue, skb); + kfree_skb(skb); } } diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index 0d62366f8cc3..0eae5c509504 100644 --- a/net/nfc/llcp/llcp.h +++ b/net/nfc/llcp/llcp.h @@ -121,7 +121,6 @@ struct nfc_llcp_sock { struct sk_buff_head tx_queue; struct sk_buff_head tx_pending_queue; - struct sk_buff_head tx_backlog_queue; struct list_head accept_queue; struct sock *parent; diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index ba1900700958..12ec4879b4ba 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -808,7 +808,6 @@ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp) llcp_sock->reserved_ssap = LLCP_SAP_MAX; skb_queue_head_init(&llcp_sock->tx_queue); skb_queue_head_init(&llcp_sock->tx_pending_queue); - skb_queue_head_init(&llcp_sock->tx_backlog_queue); INIT_LIST_HEAD(&llcp_sock->accept_queue); if (sock != NULL) @@ -823,7 +822,6 @@ void nfc_llcp_sock_free(struct nfc_llcp_sock *sock) skb_queue_purge(&sock->tx_queue); skb_queue_purge(&sock->tx_pending_queue); - skb_queue_purge(&sock->tx_backlog_queue); list_del_init(&sock->accept_queue); From 1727cf937401945a590ebf25c7fa65a4853fe9bb Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 7 Dec 2012 16:37:30 +0100 Subject: [PATCH 281/478] NFC: llcp: Fix Rx memory leak The reference count bump on the llcp Rx path is leading to a memory leak whenever we're not receiving an I frame. We fix that by removing the refcount bump (drivers must not free their received skb) and using it only in the I frame path, when the frame is actually queued. In that case, the skb will only be freed when someone fetches it from userspace. in all other cases, LLCP received frames will be freed when leaving the Rx work queue. Reported-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- net/nfc/llcp/llcp.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 003c82fe8bd7..85bc75c38dea 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -782,8 +782,14 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local, /* There is no sequence with UI frames */ skb_pull(skb, LLCP_HEADER_SIZE); - if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { - pr_err("receive queue is full\n"); + if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) { + /* + * UI frames will be freed from the socket layer, so we + * need to keep them alive until someone receives them. + */ + skb_get(skb); + } else { + pr_err("Receive queue is full\n"); kfree_skb(skb); } @@ -977,8 +983,14 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local, pr_err("Received out of sequence I PDU\n"); skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE); - if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { - pr_err("receive queue is full\n"); + if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) { + /* + * I frames will be freed from the socket layer, so we + * need to keep them alive until someone receives them. + */ + skb_get(skb); + } else { + pr_err("Receive queue is full\n"); kfree_skb(skb); } } @@ -1299,6 +1311,13 @@ static void nfc_llcp_rx_work(struct work_struct *work) local->rx_pending = NULL; } +static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb) +{ + local->rx_pending = skb; + del_timer(&local->link_timer); + schedule_work(&local->rx_work); +} + void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) { struct nfc_llcp_local *local = (struct nfc_llcp_local *) data; @@ -1309,9 +1328,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) return; } - local->rx_pending = skb_get(skb); - del_timer(&local->link_timer); - schedule_work(&local->rx_work); + __nfc_llcp_recv(local, skb); } int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) @@ -1322,9 +1339,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) if (local == NULL) return -ENODEV; - local->rx_pending = skb_get(skb); - del_timer(&local->link_timer); - schedule_work(&local->rx_work); + __nfc_llcp_recv(local, skb); return 0; } From e487882345a80a17b534182ab1f259f2321e4fed Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:44 +0100 Subject: [PATCH 282/478] NFC: pn533: Del frame logic from TgGet/SetData cmd Remove frame logic from TgSetData and TgGetData commands using the new iface for async send. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 97 +++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 64 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index e5615f538aaa..9912824030f6 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1450,65 +1450,46 @@ static int pn533_init_target_frame(struct pn533_frame *frame, #define PN533_CMD_DATAEXCH_HEAD_LEN 1 #define PN533_CMD_DATAEXCH_DATA_MAXLEN 262 static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg, - u8 *params, int params_len) + struct sk_buff *resp) { - struct sk_buff *skb_resp = arg; - struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data; + u8 status; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (params_len < 0) { - nfc_dev_err(&dev->interface->dev, - "Error %d when starting as a target", - params_len); + if (IS_ERR(resp)) + return PTR_ERR(resp); - return params_len; - } + status = resp->data[0]; + skb_pull(resp, sizeof(status)); - if (params_len > 0 && params[0] != 0) { + if (status != 0) { nfc_tm_deactivated(dev->nfc_dev); - dev->tgt_mode = 0; - - kfree_skb(skb_resp); + dev_kfree_skb(resp); return 0; } - skb_put(skb_resp, PN533_FRAME_SIZE(in_frame)); - skb_pull(skb_resp, - PN533_FRAME_HEADER_LEN + PN533_CMD_DATAEXCH_HEAD_LEN); - skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_LEN); - - return nfc_tm_data_received(dev->nfc_dev, skb_resp); + return nfc_tm_data_received(dev->nfc_dev, resp); } static void pn533_wq_tg_get_data(struct work_struct *work) { struct pn533 *dev = container_of(work, struct pn533, tg_work); - struct pn533_frame *in_frame; - struct sk_buff *skb_resp; - size_t skb_resp_len; + + struct sk_buff *skb; + int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - skb_resp_len = PN533_FRAME_HEADER_LEN + - PN533_CMD_DATAEXCH_HEAD_LEN + - PN533_CMD_DATAEXCH_DATA_MAXLEN + - PN533_FRAME_TAIL_LEN; - - skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL); - if (!skb_resp) + skb = pn533_alloc_skb(0); + if (!skb) return; - in_frame = (struct pn533_frame *)skb_resp->data; + rc = pn533_send_data_async(dev, PN533_CMD_TG_GET_DATA, skb, + pn533_tm_get_data_complete, NULL); - pn533_tx_frame_init(dev->out_frame, PN533_CMD_TG_GET_DATA); - pn533_tx_frame_finish(dev->out_frame); - - pn533_send_cmd_frame_async(dev, dev->out_frame, in_frame, - skb_resp_len, - pn533_tm_get_data_complete, - skb_resp); + if (rc < 0) + dev_kfree_skb(skb); return; } @@ -2280,23 +2261,20 @@ error: } static int pn533_tm_send_complete(struct pn533 *dev, void *arg, - u8 *params, int params_len) + struct sk_buff *resp) { - struct sk_buff *skb_out = arg; + u8 status; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - dev_kfree_skb(skb_out); + if (IS_ERR(resp)) + return PTR_ERR(resp); - if (params_len < 0) { - nfc_dev_err(&dev->interface->dev, - "Error %d when sending data", - params_len); + status = resp->data[0]; - return params_len; - } + dev_kfree_skb(resp); - if (params_len > 0 && params[0] != 0) { + if (status != 0) { nfc_tm_deactivated(dev->nfc_dev); dev->tgt_mode = 0; @@ -2312,30 +2290,21 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg, static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); - struct pn533_frame *out_frame; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - rc = pn533_build_tx_frame(dev, skb, false); - if (rc) - goto error; - - out_frame = (struct pn533_frame *) skb->data; - - rc = pn533_send_cmd_frame_async(dev, out_frame, dev->in_frame, - PN533_NORMAL_FRAME_MAX_LEN, - pn533_tm_send_complete, skb); - if (rc) { + if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) { nfc_dev_err(&dev->interface->dev, - "Error %d when trying to send data", rc); - goto error; + "Data length greater than the max allowed: %d", + PN533_CMD_DATAEXCH_DATA_MAXLEN); + return -ENOSYS; } - return 0; - -error: - kfree_skb(skb); + rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb, + pn533_tm_send_complete, NULL); + if (rc < 0) + dev_kfree_skb(skb); return rc; } From 13003649b1ebb4ea51341a9405a2ec5b10cf4f7e Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:45 +0100 Subject: [PATCH 283/478] NFC: pn533: Del frame logic from InJumForDep cmd Remove frame logic from InJmumpForDEP command using the new iface for async send. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 117 +++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 9912824030f6..aa7803f16773 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -298,14 +298,6 @@ struct pn533_cmd_activate_response { u8 gt[]; } __packed; -/* PN533_CMD_IN_JUMP_FOR_DEP */ -struct pn533_cmd_jump_dep { - u8 active; - u8 baud; - u8 next; - u8 data[]; -} __packed; - struct pn533_cmd_jump_dep_response { u8 status; u8 tg; @@ -1861,53 +1853,46 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, - u8 *params, int params_len) + struct sk_buff *resp) { - struct pn533_cmd_jump_dep_response *resp; - struct nfc_target nfc_target; + struct pn533_cmd_jump_dep_response *rsp; u8 target_gt_len; int rc; - struct pn533_cmd_jump_dep *cmd = (struct pn533_cmd_jump_dep *)arg; - u8 active = cmd->active; + u8 active = *(u8 *)arg; kfree(arg); - if (params_len == -ENOENT) { - nfc_dev_dbg(&dev->interface->dev, ""); - return 0; - } - - if (params_len < 0) { - nfc_dev_err(&dev->interface->dev, - "Error %d when bringing DEP link up", - params_len); - return 0; - } + if (IS_ERR(resp)) + return PTR_ERR(resp); if (dev->tgt_available_prots && !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) { nfc_dev_err(&dev->interface->dev, "The target does not support DEP"); - return -EINVAL; + rc = -EINVAL; + goto error; } - resp = (struct pn533_cmd_jump_dep_response *) params; - rc = resp->status & PN533_CMD_RET_MASK; + rsp = (struct pn533_cmd_jump_dep_response *)resp->data; + + rc = rsp->status & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) { nfc_dev_err(&dev->interface->dev, "Bringing DEP link up failed %d", rc); - return 0; + goto error; } if (!dev->tgt_available_prots) { + struct nfc_target nfc_target; + nfc_dev_dbg(&dev->interface->dev, "Creating new target"); nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK; nfc_target.nfcid1_len = 10; - memcpy(nfc_target.nfcid1, resp->nfcid3t, nfc_target.nfcid1_len); + memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len); rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1); if (rc) - return 0; + goto error; dev->tgt_available_prots = 0; } @@ -1915,15 +1900,17 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, dev->tgt_active_prot = NFC_PROTO_NFC_DEP; /* ATR_RES general bytes are located at offset 17 */ - target_gt_len = PN533_FRAME_CMD_PARAMS_LEN(dev->in_frame) - 17; + target_gt_len = resp->len - 17; rc = nfc_set_remote_general_bytes(dev->nfc_dev, - resp->gt, target_gt_len); + rsp->gt, target_gt_len); if (rc == 0) rc = nfc_dep_link_is_up(dev->nfc_dev, - dev->nfc_dev->targets[0].idx, - !active, NFC_RF_INITIATOR); + dev->nfc_dev->targets[0].idx, + !active, NFC_RF_INITIATOR); - return 0; +error: + dev_kfree_skb(resp); + return rc; } static int pn533_mod_to_baud(struct pn533 *dev) @@ -1945,10 +1932,11 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, u8 comm_mode, u8* gb, size_t gb_len) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); - struct pn533_cmd_jump_dep *cmd; - u8 cmd_len, *data_ptr; + struct sk_buff *skb; + int rc, baud, skb_len; + u8 *next, *arg; + u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3}; - int rc, baud; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); @@ -1971,43 +1959,48 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, return baud; } - cmd_len = sizeof(struct pn533_cmd_jump_dep) + gb_len; + skb_len = 3 + gb_len; /* ActPass + BR + Next */ if (comm_mode == NFC_COMM_PASSIVE) - cmd_len += PASSIVE_DATA_LEN; + skb_len += PASSIVE_DATA_LEN; - cmd = kzalloc(cmd_len, GFP_KERNEL); - if (cmd == NULL) + skb = pn533_alloc_skb(skb_len); + if (!skb) return -ENOMEM; - pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_JUMP_FOR_DEP); + *skb_put(skb, 1) = !comm_mode; /* ActPass */ + *skb_put(skb, 1) = baud; /* Baud rate */ - cmd->active = !comm_mode; - cmd->next = 0; - cmd->baud = baud; - data_ptr = cmd->data; - if (comm_mode == NFC_COMM_PASSIVE && cmd->baud > 0) { - memcpy(data_ptr, passive_data, PASSIVE_DATA_LEN); - cmd->next |= 1; - data_ptr += PASSIVE_DATA_LEN; + next = skb_put(skb, 1); /* Next */ + *next = 0; + + if (comm_mode == NFC_COMM_PASSIVE && baud > 0) { + memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, + PASSIVE_DATA_LEN); + *next |= 1; } if (gb != NULL && gb_len > 0) { - cmd->next |= 4; /* We have some Gi */ - memcpy(data_ptr, gb, gb_len); + memcpy(skb_put(skb, gb_len), gb, gb_len); + *next |= 4; /* We have some Gi */ } else { - cmd->next = 0; + *next = 0; } - memcpy(PN533_FRAME_CMD_PARAMS_PTR(dev->out_frame), cmd, cmd_len); - dev->out_frame->datalen += cmd_len; + arg = kmalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) { + dev_kfree_skb(skb); + return -ENOMEM; + } - pn533_tx_frame_finish(dev->out_frame); + *arg = !comm_mode; - rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, - PN533_NORMAL_FRAME_MAX_LEN, - pn533_in_dep_link_up_complete, cmd); - if (rc < 0) - kfree(cmd); + rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb, + pn533_in_dep_link_up_complete, arg); + + if (rc < 0) { + dev_kfree_skb(skb); + kfree(arg); + } return rc; } From b1e666f503b5479efe9adf79fb46f09fe4f04af5 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:46 +0100 Subject: [PATCH 284/478] NFC: pn533: Del frame logic from Data Exchange cmd Remove frame logic from transceive cb using new iface for async send. For pn533_wq_mi_recv() use pn533_send_cmd_direct_async which sends the cmd directly to the hardware, skipping cmd queue. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 313 ++++++++++++++++++++------------------------ 1 file changed, 143 insertions(+), 170 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index aa7803f16773..91e1594dfd13 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -360,6 +360,7 @@ struct pn533 { pn533_cmd_complete_t cmd_complete; void *cmd_complete_arg; + void *cmd_complete_mi_arg; struct mutex cmd_lock; u8 cmd; @@ -848,6 +849,57 @@ static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code, return rc; } +/* + * pn533_send_cmd_direct_async + * + * The function sends a piority cmd directly to the chip omiting the cmd + * queue. It's intended to be used by chaining mechanism of received responses + * where the host has to request every single chunk of data before scheduling + * next cmd from the queue. + */ +static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code, + struct sk_buff *req, + pn533_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct pn533_send_async_complete_arg *arg; + struct sk_buff *resp; + int rc; + int resp_len = PN533_FRAME_HEADER_LEN + + PN533_FRAME_MAX_PAYLOAD_LEN + + PN533_FRAME_TAIL_LEN; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + resp = alloc_skb(resp_len, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + arg = kzalloc(sizeof(arg), GFP_KERNEL); + if (!arg) { + dev_kfree_skb(resp); + return -ENOMEM; + } + + arg->complete_cb = complete_cb; + arg->complete_cb_context = complete_cb_context; + arg->resp = resp; + arg->req = req; + + pn533_build_cmd_frame(cmd_code, req); + + rc = __pn533_send_cmd_frame_async(dev, (struct pn533_frame *)req->data, + (struct pn533_frame *)resp->data, + resp_len, pn533_send_async_complete, + arg); + if (rc < 0) { + dev_kfree_skb(resp); + kfree(arg); + } + + return rc; +} + static void pn533_wq_cmd(struct work_struct *work) { struct pn533 *dev = container_of(work, struct pn533, cmd_work); @@ -2024,69 +2076,7 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev) return 0; } -static int pn533_build_tx_frame(struct pn533 *dev, struct sk_buff *skb, - bool target) -{ - int payload_len = skb->len; - struct pn533_frame *out_frame; - u8 tg; - - nfc_dev_dbg(&dev->interface->dev, "%s - Sending %d bytes", __func__, - payload_len); - - if (payload_len > PN533_CMD_DATAEXCH_DATA_MAXLEN) { - /* TODO: Implement support to multi-part data exchange */ - nfc_dev_err(&dev->interface->dev, "Data length greater than the" - " max allowed: %d", - PN533_CMD_DATAEXCH_DATA_MAXLEN); - return -ENOSYS; - } - - skb_push(skb, PN533_FRAME_HEADER_LEN); - - if (target == true) { - switch (dev->device_type) { - case PN533_DEVICE_PASORI: - if (dev->tgt_active_prot == NFC_PROTO_FELICA) { - out_frame = (struct pn533_frame *) skb->data; - pn533_tx_frame_init(out_frame, - PN533_CMD_IN_COMM_THRU); - - break; - } - - default: - skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN); - out_frame = (struct pn533_frame *) skb->data; - pn533_tx_frame_init(out_frame, - PN533_CMD_IN_DATA_EXCHANGE); - tg = 1; - memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame), - &tg, sizeof(u8)); - out_frame->datalen += sizeof(u8); - - break; - } - - } else { - skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1); - out_frame = (struct pn533_frame *) skb->data; - pn533_tx_frame_init(out_frame, PN533_CMD_TG_SET_DATA); - } - - - /* The data is already in the out_frame, just update the datalen */ - out_frame->datalen += payload_len; - - pn533_tx_frame_finish(out_frame); - skb_put(skb, PN533_FRAME_TAIL_LEN); - - return 0; -} - struct pn533_data_exchange_arg { - struct sk_buff *skb_resp; - struct sk_buff *skb_out; data_exchange_cb_t cb; void *cb_context; }; @@ -2130,47 +2120,44 @@ out: } static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, - u8 *params, int params_len) + struct sk_buff *resp) { struct pn533_data_exchange_arg *arg = _arg; - struct sk_buff *skb = NULL, *skb_resp = arg->skb_resp; - struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data; - int err = 0; - u8 status; - u8 cmd_ret; + struct sk_buff *skb; + int rc = 0; + u8 status, ret, mi; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - dev_kfree_skb(arg->skb_out); + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + goto _error; + } - if (params_len < 0) { /* error */ - err = params_len; + status = resp->data[0]; + ret = status & PN533_CMD_RET_MASK; + mi = status & PN533_CMD_MI_MASK; + + skb_pull(resp, sizeof(status)); + + if (ret != PN533_CMD_RET_SUCCESS) { + nfc_dev_err(&dev->interface->dev, + "PN533 reported error %d when exchanging data", + ret); + rc = -EIO; goto error; } - status = params[0]; + skb_queue_tail(&dev->resp_q, resp); - cmd_ret = status & PN533_CMD_RET_MASK; - if (cmd_ret != PN533_CMD_RET_SUCCESS) { - nfc_dev_err(&dev->interface->dev, "PN533 reported error %d when" - " exchanging data", cmd_ret); - err = -EIO; - goto error; - } - - skb_put(skb_resp, PN533_FRAME_SIZE(in_frame)); - skb_pull(skb_resp, PN533_FRAME_HEADER_LEN); - skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN); - skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_LEN); - skb_queue_tail(&dev->resp_q, skb_resp); - - if (status & PN533_CMD_MI_MASK) { + if (mi) { + dev->cmd_complete_mi_arg = arg; queue_work(dev->wq, &dev->mi_work); return -EINPROGRESS; } skb = pn533_build_response(dev); - if (skb == NULL) + if (!skb) goto error; arg->cb(arg->cb_context, skb, 0); @@ -2178,11 +2165,12 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, return 0; error: + dev_kfree_skb(resp); +_error: skb_queue_purge(&dev->resp_q); - dev_kfree_skb(skb_resp); - arg->cb(arg->cb_context, NULL, err); + arg->cb(arg->cb_context, NULL, rc); kfree(arg); - return 0; + return rc; } static int pn533_transceive(struct nfc_dev *nfc_dev, @@ -2190,14 +2178,20 @@ static int pn533_transceive(struct nfc_dev *nfc_dev, data_exchange_cb_t cb, void *cb_context) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); - struct pn533_frame *out_frame, *in_frame; - struct pn533_data_exchange_arg *arg; - struct sk_buff *skb_resp; - int skb_resp_len; + struct pn533_data_exchange_arg *arg = NULL; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) { + /* TODO: Implement support to multi-part data exchange */ + nfc_dev_err(&dev->interface->dev, + "Data length greater than the max allowed: %d", + PN533_CMD_DATAEXCH_DATA_MAXLEN); + rc = -ENOSYS; + goto error; + } + if (!dev->tgt_active_prot) { nfc_dev_err(&dev->interface->dev, "Cannot exchange data if" " there is no active target"); @@ -2205,51 +2199,43 @@ static int pn533_transceive(struct nfc_dev *nfc_dev, goto error; } - rc = pn533_build_tx_frame(dev, skb, true); - if (rc) - goto error; - - skb_resp_len = PN533_FRAME_HEADER_LEN + - PN533_CMD_DATAEXCH_HEAD_LEN + - PN533_CMD_DATAEXCH_DATA_MAXLEN + - PN533_FRAME_TAIL_LEN; - - skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL); - if (!skb_resp) { - rc = -ENOMEM; - goto error; - } - - in_frame = (struct pn533_frame *) skb_resp->data; - out_frame = (struct pn533_frame *) skb->data; - - arg = kmalloc(sizeof(struct pn533_data_exchange_arg), GFP_KERNEL); + arg = kmalloc(sizeof(*arg), GFP_KERNEL); if (!arg) { rc = -ENOMEM; - goto free_skb_resp; + goto error; } - arg->skb_resp = skb_resp; - arg->skb_out = skb; arg->cb = cb; arg->cb_context = cb_context; - rc = pn533_send_cmd_frame_async(dev, out_frame, in_frame, skb_resp_len, - pn533_data_exchange_complete, arg); - if (rc) { - nfc_dev_err(&dev->interface->dev, "Error %d when trying to" - " perform data_exchange", rc); - goto free_arg; + switch (dev->device_type) { + case PN533_DEVICE_PASORI: + if (dev->tgt_active_prot == NFC_PROTO_FELICA) { + rc = pn533_send_data_async(dev, PN533_CMD_IN_COMM_THRU, + skb, + pn533_data_exchange_complete, + arg); + + break; + } + default: + *skb_push(skb, sizeof(u8)) = 1; /*TG*/ + + rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE, + skb, pn533_data_exchange_complete, + arg); + + break; } + if (rc < 0) /* rc from send_async */ + goto error; + return 0; -free_arg: - kfree(arg); -free_skb_resp: - kfree_skb(skb_resp); error: - kfree_skb(skb); + kfree(arg); + dev_kfree_skb(skb); return rc; } @@ -2305,63 +2291,50 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) static void pn533_wq_mi_recv(struct work_struct *work) { struct pn533 *dev = container_of(work, struct pn533, mi_work); - struct sk_buff *skb_cmd; - struct pn533_data_exchange_arg *arg = dev->cmd_complete_arg; - struct pn533_frame *out_frame, *in_frame; - struct sk_buff *skb_resp; - int skb_resp_len; + + struct sk_buff *skb; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - /* This is a zero payload size skb */ - skb_cmd = pn533_alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN); - if (skb_cmd == NULL) - goto error_cmd; + skb = pn533_alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN); + if (!skb) + goto error; - skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN); + switch (dev->device_type) { + case PN533_DEVICE_PASORI: + if (dev->tgt_active_prot == NFC_PROTO_FELICA) { + rc = pn533_send_cmd_direct_async(dev, + PN533_CMD_IN_COMM_THRU, + skb, + pn533_data_exchange_complete, + dev->cmd_complete_mi_arg); - rc = pn533_build_tx_frame(dev, skb_cmd, true); - if (rc) - goto error_frame; + break; + } + default: + *skb_put(skb, sizeof(u8)) = 1; /*TG*/ - skb_resp_len = PN533_FRAME_HEADER_LEN + - PN533_CMD_DATAEXCH_HEAD_LEN + - PN533_CMD_DATAEXCH_DATA_MAXLEN + - PN533_FRAME_TAIL_LEN; + rc = pn533_send_cmd_direct_async(dev, + PN533_CMD_IN_DATA_EXCHANGE, + skb, + pn533_data_exchange_complete, + dev->cmd_complete_mi_arg); - skb_resp = alloc_skb(skb_resp_len, GFP_KERNEL); - if (!skb_resp) { - rc = -ENOMEM; - goto error_frame; + break; } - in_frame = (struct pn533_frame *) skb_resp->data; - out_frame = (struct pn533_frame *) skb_cmd->data; - - arg->skb_resp = skb_resp; - arg->skb_out = skb_cmd; - - rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, - skb_resp_len, - pn533_data_exchange_complete, - dev->cmd_complete_arg); - if (!rc) + if (rc == 0) /* success */ return; - nfc_dev_err(&dev->interface->dev, "Error %d when trying to" - " perform data_exchange", rc); + nfc_dev_err(&dev->interface->dev, + "Error %d when trying to perform data_exchange", rc); - kfree_skb(skb_resp); + dev_kfree_skb(skb); + kfree(dev->cmd_complete_arg); -error_frame: - kfree_skb(skb_cmd); - -error_cmd: +error: pn533_send_ack(dev, GFP_KERNEL); - - kfree(arg); - queue_work(dev->wq, &dev->cmd_work); } From 99e591be3d95e55412585729624b05862bb5463c Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:47 +0100 Subject: [PATCH 285/478] NFC: pn533: Dump tx and rx data in debug mode Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 91e1594dfd13..b445f0339e03 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -542,6 +542,9 @@ static void pn533_recv_response(struct urb *urb) in_frame = dev->in_urb->transfer_buffer; + print_hex_dump(KERN_DEBUG, "PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, + in_frame, PN533_FRAME_SIZE(in_frame), false); + if (!pn533_rx_frame_is_valid(in_frame)) { nfc_dev_err(&dev->interface->dev, "Received an invalid frame"); dev->wq_in_error = -EIO; @@ -664,6 +667,9 @@ static int __pn533_send_cmd_frame_async(struct pn533 *dev, dev->in_urb->transfer_buffer = in_frame; dev->in_urb->transfer_buffer_length = in_frame_len; + print_hex_dump(KERN_DEBUG, "PN533 TX: ", DUMP_PREFIX_NONE, 16, 1, + out_frame, PN533_FRAME_SIZE(out_frame), false); + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); if (rc) return rc; From b5193e5da24fb278c68fc0493c0a1073a1b27ab9 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:48 +0100 Subject: [PATCH 286/478] NFC: pn533: Remove frame logic from poll cmds Remove frame logic from start_pool cb using the new iface for async send. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 274 +++++++++++++++++++++----------------------- 1 file changed, 128 insertions(+), 146 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index b445f0339e03..1016e0962068 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -320,20 +320,6 @@ struct pn533_cmd_jump_dep_response { #define PN533_INIT_TARGET_RESP_ACTIVE 0x1 #define PN533_INIT_TARGET_RESP_DEP 0x4 -struct pn533_cmd_init_target { - u8 mode; - u8 mifare[6]; - u8 felica[18]; - u8 nfcid3[10]; - u8 gb_len; - u8 gb[]; -} __packed; - -struct pn533_cmd_init_target_response { - u8 mode; - u8 cmd[]; -} __packed; - struct pn533 { struct usb_device *udev; struct usb_interface *interface; @@ -1315,50 +1301,37 @@ static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data, return 0; } -struct pn533_poll_response { - u8 nbtg; - u8 tg; - u8 target_data[]; -} __packed; - -static int pn533_target_found(struct pn533 *dev, - struct pn533_poll_response *resp, int resp_len) +static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata, + int tgdata_len) { - int target_data_len; struct nfc_target nfc_tgt; int rc; nfc_dev_dbg(&dev->interface->dev, "%s - modulation=%d", __func__, - dev->poll_mod_curr); + dev->poll_mod_curr); - if (resp->tg != 1) + if (tg != 1) return -EPROTO; memset(&nfc_tgt, 0, sizeof(struct nfc_target)); - target_data_len = resp_len - sizeof(struct pn533_poll_response); - switch (dev->poll_mod_curr) { case PN533_POLL_MOD_106KBPS_A: - rc = pn533_target_found_type_a(&nfc_tgt, resp->target_data, - target_data_len); + rc = pn533_target_found_type_a(&nfc_tgt, tgdata, tgdata_len); break; case PN533_POLL_MOD_212KBPS_FELICA: case PN533_POLL_MOD_424KBPS_FELICA: - rc = pn533_target_found_felica(&nfc_tgt, resp->target_data, - target_data_len); + rc = pn533_target_found_felica(&nfc_tgt, tgdata, tgdata_len); break; case PN533_POLL_MOD_106KBPS_JEWEL: - rc = pn533_target_found_jewel(&nfc_tgt, resp->target_data, - target_data_len); + rc = pn533_target_found_jewel(&nfc_tgt, tgdata, tgdata_len); break; case PN533_POLL_MOD_847KBPS_B: - rc = pn533_target_found_type_b(&nfc_tgt, resp->target_data, - target_data_len); + rc = pn533_target_found_type_b(&nfc_tgt, tgdata, tgdata_len); break; default: - nfc_dev_err(&dev->interface->dev, "Unknown current poll" - " modulation"); + nfc_dev_err(&dev->interface->dev, + "Unknown current poll modulation"); return -EPROTO; } @@ -1366,13 +1339,14 @@ static int pn533_target_found(struct pn533 *dev, return rc; if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) { - nfc_dev_dbg(&dev->interface->dev, "The target found does not" - " have the desired protocol"); + nfc_dev_dbg(&dev->interface->dev, + "The Tg found doesn't have the desired protocol"); return -EAGAIN; } - nfc_dev_dbg(&dev->interface->dev, "Target found - supported protocols: " - "0x%x", nfc_tgt.supported_protocols); + nfc_dev_dbg(&dev->interface->dev, + "Target found - supported protocols: 0x%x", + nfc_tgt.supported_protocols); dev->tgt_available_prots = nfc_tgt.supported_protocols; @@ -1424,16 +1398,20 @@ static void pn533_poll_create_mod_list(struct pn533 *dev, pn533_poll_add_mod(dev, PN533_LISTEN_MOD); } -static int pn533_start_poll_complete(struct pn533 *dev, u8 *params, int params_len) +static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp) { - struct pn533_poll_response *resp; - int rc; + u8 nbtg, tg, *tgdata; + int rc, tgdata_len; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - resp = (struct pn533_poll_response *) params; - if (resp->nbtg) { - rc = pn533_target_found(dev, resp, params_len); + nbtg = resp->data[0]; + tg = resp->data[1]; + tgdata = &resp->data[2]; + tgdata_len = resp->len - 2; /* nbtg + tg */ + + if (nbtg) { + rc = pn533_target_found(dev, tg, tgdata, tgdata_len); /* We must stop the poll after a valid target found */ if (rc == 0) { @@ -1445,56 +1423,55 @@ static int pn533_start_poll_complete(struct pn533 *dev, u8 *params, int params_l return -EAGAIN; } -static int pn533_init_target_frame(struct pn533_frame *frame, - u8 *gb, size_t gb_len) +static struct sk_buff *pn533_alloc_poll_tg_frame(u8 *gbytes, size_t gbytes_len) { - struct pn533_cmd_init_target *cmd; - size_t cmd_len; + struct sk_buff *skb; + u8 *felica, *nfcid3, *gb; + u8 felica_params[18] = {0x1, 0xfe, /* DEP */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff}; /* System code */ + u8 mifare_params[6] = {0x1, 0x1, /* SENS_RES */ 0x0, 0x0, 0x0, 0x40}; /* SEL_RES for DEP */ - cmd_len = sizeof(struct pn533_cmd_init_target) + gb_len + 1; - cmd = kzalloc(cmd_len, GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; + unsigned int skb_len = 36 + /* mode (1), mifare (6), + felica (18), nfcid3 (10), gb_len (1) */ + gbytes_len + + 1; /* len Tk*/ - pn533_tx_frame_init(frame, PN533_CMD_TG_INIT_AS_TARGET); + skb = pn533_alloc_skb(skb_len); + if (!skb) + return NULL; /* DEP support only */ - cmd->mode |= PN533_INIT_TARGET_DEP; - - /* Felica params */ - memcpy(cmd->felica, felica_params, 18); - get_random_bytes(cmd->felica + 2, 6); - - /* NFCID3 */ - memset(cmd->nfcid3, 0, 10); - memcpy(cmd->nfcid3, cmd->felica, 8); + *skb_put(skb, 1) |= PN533_INIT_TARGET_DEP; /* MIFARE params */ - memcpy(cmd->mifare, mifare_params, 6); + memcpy(skb_put(skb, 6), mifare_params, 6); + + /* Felica params */ + felica = skb_put(skb, 18); + memcpy(felica, felica_params, 18); + get_random_bytes(felica + 2, 6); + + /* NFCID3 */ + nfcid3 = skb_put(skb, 10); + memset(nfcid3, 0, 10); + memcpy(nfcid3, felica, 8); /* General bytes */ - cmd->gb_len = gb_len; - memcpy(cmd->gb, gb, gb_len); + *skb_put(skb, 1) = gbytes_len; + + gb = skb_put(skb, gbytes_len); + memcpy(gb, gbytes, gbytes_len); /* Len Tk */ - cmd->gb[gb_len] = 0; + *skb_put(skb, 1) = 0; - memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), cmd, cmd_len); - - frame->datalen += cmd_len; - - pn533_tx_frame_finish(frame); - - kfree(cmd); - - return 0; + return skb; } #define PN533_CMD_DATAEXCH_HEAD_LEN 1 @@ -1545,41 +1522,32 @@ static void pn533_wq_tg_get_data(struct work_struct *work) } #define ATR_REQ_GB_OFFSET 17 -static int pn533_init_target_complete(struct pn533 *dev, u8 *params, int params_len) +static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp) { - struct pn533_cmd_init_target_response *resp; - u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb; + u8 mode, *cmd, comm_mode = NFC_COMM_PASSIVE, *gb; size_t gb_len; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (params_len < 0) { - nfc_dev_err(&dev->interface->dev, - "Error %d when starting as a target", - params_len); - - return params_len; - } - - if (params_len < ATR_REQ_GB_OFFSET + 1) + if (resp->len < ATR_REQ_GB_OFFSET + 1) return -EINVAL; - resp = (struct pn533_cmd_init_target_response *) params; + mode = resp->data[0]; + cmd = &resp->data[1]; - nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x param len %d\n", - resp->mode, params_len); + nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n", + mode, resp->len); - frame = resp->mode & PN533_INIT_TARGET_RESP_FRAME_MASK; - if (frame == PN533_INIT_TARGET_RESP_ACTIVE) + if ((mode & PN533_INIT_TARGET_RESP_FRAME_MASK) == + PN533_INIT_TARGET_RESP_ACTIVE) comm_mode = NFC_COMM_ACTIVE; - /* Again, only DEP */ - if ((resp->mode & PN533_INIT_TARGET_RESP_DEP) == 0) + if ((mode & PN533_INIT_TARGET_RESP_DEP) == 0) /* Only DEP supported */ return -EOPNOTSUPP; - gb = resp->cmd + ATR_REQ_GB_OFFSET; - gb_len = params_len - (ATR_REQ_GB_OFFSET + 1); + gb = cmd + ATR_REQ_GB_OFFSET; + gb_len = resp->len - (ATR_REQ_GB_OFFSET + 1); rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, comm_mode, gb, gb_len); @@ -1590,7 +1558,6 @@ static int pn533_init_target_complete(struct pn533 *dev, u8 *params, int params_ } dev->tgt_mode = 1; - queue_work(dev->wq, &dev->tg_work); return 0; @@ -1613,89 +1580,104 @@ static void pn533_listen_mode_timer(unsigned long data) } static int pn533_poll_complete(struct pn533 *dev, void *arg, - u8 *params, int params_len) + struct sk_buff *resp) { struct pn533_poll_modulations *cur_mod; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (params_len == -ENOENT) { - if (dev->poll_mod_count != 0) - return 0; + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); - nfc_dev_err(&dev->interface->dev, - "Polling operation has been stopped"); + nfc_dev_err(&dev->interface->dev, "%s Poll complete error %d", + __func__, rc); - goto stop_poll; - } - - if (params_len < 0) { - nfc_dev_err(&dev->interface->dev, - "Error %d when running poll", params_len); - - goto stop_poll; + if (rc == -ENOENT) { + if (dev->poll_mod_count != 0) + return rc; + else + goto stop_poll; + } else if (rc < 0) { + nfc_dev_err(&dev->interface->dev, + "Error %d when running poll", rc); + goto stop_poll; + } } cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; - if (cur_mod->len == 0) { + if (cur_mod->len == 0) { /* Target mode */ del_timer(&dev->listen_timer); - - return pn533_init_target_complete(dev, params, params_len); - } else { - rc = pn533_start_poll_complete(dev, params, params_len); - if (!rc) - return rc; + rc = pn533_init_target_complete(dev, resp); + goto done; } + /* Initiator mode */ + rc = pn533_start_poll_complete(dev, resp); + if (!rc) + goto done; + pn533_poll_next_mod(dev); - queue_work(dev->wq, &dev->poll_work); - return 0; +done: + dev_kfree_skb(resp); + return rc; stop_poll: + nfc_dev_err(&dev->interface->dev, "Polling operation has been stopped"); + pn533_poll_reset_mod_list(dev); dev->poll_protocols = 0; - return 0; + return rc; } -static void pn533_build_poll_frame(struct pn533 *dev, - struct pn533_frame *frame, - struct pn533_poll_modulations *mod) +static struct sk_buff *pn533_alloc_poll_in_frame(struct pn533_poll_modulations + *mod) { - nfc_dev_dbg(&dev->interface->dev, "mod len %d\n", mod->len); + struct sk_buff *skb; - if (mod->len == 0) { - /* Listen mode */ - pn533_init_target_frame(frame, dev->gb, dev->gb_len); - } else { - /* Polling mode */ - pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET); + skb = pn533_alloc_skb(mod->len); + if (!skb) + return NULL; - memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len); - frame->datalen += mod->len; + memcpy(skb_put(skb, mod->len), &mod->data, mod->len); - pn533_tx_frame_finish(frame); - } + return skb; } static int pn533_send_poll_frame(struct pn533 *dev) { - struct pn533_poll_modulations *cur_mod; + struct pn533_poll_modulations *mod; + struct sk_buff *skb; int rc; + u8 cmd_code; - cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; + mod = dev->poll_mod_active[dev->poll_mod_curr]; - pn533_build_poll_frame(dev, dev->out_frame, cur_mod); + nfc_dev_dbg(&dev->interface->dev, "%s mod len %d\n", + __func__, mod->len); - rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, - PN533_NORMAL_FRAME_MAX_LEN, - pn533_poll_complete, - NULL); - if (rc) + if (mod->len == 0) { /* Listen mode */ + cmd_code = PN533_CMD_TG_INIT_AS_TARGET; + skb = pn533_alloc_poll_tg_frame(dev->gb, dev->gb_len); + } else { /* Polling mode */ + cmd_code = PN533_CMD_IN_LIST_PASSIVE_TARGET; + skb = pn533_alloc_poll_in_frame(mod); + } + + if (!skb) { + nfc_dev_err(&dev->interface->dev, "Failed to allocate skb."); + return -ENOMEM; + } + + rc = pn533_send_cmd_async(dev, cmd_code, skb, pn533_poll_complete, + NULL); + if (rc < 0) { + dev_kfree_skb(skb); nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc); + } return rc; } From 6ca55372bf55885e02d17c58cd29ceef045ee699 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:49 +0100 Subject: [PATCH 287/478] NFC: pn533: Fix quoted strings split across lines Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 70 ++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 1016e0962068..43970610c710 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -515,13 +515,13 @@ static void pn533_recv_response(struct urb *urb) case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with" - " status: %d", urb->status); + nfc_dev_dbg(&dev->interface->dev, + "Urb shutting down with status: %d", urb->status); dev->wq_in_error = urb->status; goto sched_wq; default: - nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:" - " %d", urb->status); + nfc_dev_err(&dev->interface->dev, + "Nonzero urb status received: %d", urb->status); dev->wq_in_error = urb->status; goto sched_wq; } @@ -538,8 +538,8 @@ static void pn533_recv_response(struct urb *urb) } if (!pn533_rx_frame_is_cmd_response(in_frame, dev->cmd)) { - nfc_dev_err(&dev->interface->dev, "The received frame is not " - "response to the last command"); + nfc_dev_err(&dev->interface->dev, + "It it not the response to the last command"); dev->wq_in_error = -EIO; goto sched_wq; } @@ -572,13 +572,13 @@ static void pn533_recv_ack(struct urb *urb) case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with" - " status: %d", urb->status); + nfc_dev_dbg(&dev->interface->dev, + "Urb shutting down with status: %d", urb->status); dev->wq_in_error = urb->status; goto sched_wq; default: - nfc_dev_err(&dev->interface->dev, "Nonzero urb status received:" - " %d", urb->status); + nfc_dev_err(&dev->interface->dev, + "Nonzero urb status received: %d", urb->status); dev->wq_in_error = urb->status; goto sched_wq; } @@ -595,8 +595,8 @@ static void pn533_recv_ack(struct urb *urb) rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC); if (rc) { - nfc_dev_err(&dev->interface->dev, "usb_submit_urb failed with" - " result %d", rc); + nfc_dev_err(&dev->interface->dev, + "usb_submit_urb failed with result %d", rc); dev->wq_in_error = rc; goto sched_wq; } @@ -1044,12 +1044,12 @@ static void pn533_send_complete(struct urb *urb) case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - nfc_dev_dbg(&dev->interface->dev, "Urb shutting down with" - " status: %d", urb->status); + nfc_dev_dbg(&dev->interface->dev, + "Urb shutting down with status: %d", urb->status); break; default: - nfc_dev_dbg(&dev->interface->dev, "Nonzero urb status received:" - " %d", urb->status); + nfc_dev_dbg(&dev->interface->dev, + "Nonzero urb status received: %d", urb->status); } } @@ -1753,8 +1753,8 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev) del_timer(&dev->listen_timer); if (!dev->poll_mod_count) { - nfc_dev_dbg(&dev->interface->dev, "Polling operation was not" - " running"); + nfc_dev_dbg(&dev->interface->dev, + "Polling operation was not running"); return; } @@ -1813,35 +1813,35 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev, protocol); if (dev->poll_mod_count) { - nfc_dev_err(&dev->interface->dev, "Cannot activate while" - " polling"); + nfc_dev_err(&dev->interface->dev, + "Cannot activate while polling"); return -EBUSY; } if (dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, "There is already an active" - " target"); + nfc_dev_err(&dev->interface->dev, + "There is already an active target"); return -EBUSY; } if (!dev->tgt_available_prots) { - nfc_dev_err(&dev->interface->dev, "There is no available target" - " to activate"); + nfc_dev_err(&dev->interface->dev, + "There is no available target to activate"); return -EINVAL; } if (!(dev->tgt_available_prots & (1 << protocol))) { - nfc_dev_err(&dev->interface->dev, "The target does not support" - " the requested protocol %u", protocol); + nfc_dev_err(&dev->interface->dev, + "Target doesn't support requested proto %u", + protocol); return -EINVAL; } if (protocol == NFC_PROTO_NFC_DEP) { rc = pn533_activate_target_nfcdep(dev); if (rc) { - nfc_dev_err(&dev->interface->dev, "Error %d when" - " activating target with" - " NFC_DEP protocol", rc); + nfc_dev_err(&dev->interface->dev, + "Activating target with DEP failed %d", rc); return rc; } } @@ -1884,8 +1884,8 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, rc = resp->data[0] & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) - nfc_dev_err(&dev->interface->dev, "Error 0x%x when releasing" - " the target", rc); + nfc_dev_err(&dev->interface->dev, + "Error 0x%x when releasing the target", rc); dev_kfree_skb(resp); return; @@ -2181,8 +2181,8 @@ static int pn533_transceive(struct nfc_dev *nfc_dev, } if (!dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, "Cannot exchange data if" - " there is no active target"); + nfc_dev_err(&dev->interface->dev, + "Can't exchange data if there is no active target"); rc = -EINVAL; goto error; } @@ -2522,8 +2522,8 @@ static int pn533_probe(struct usb_interface *interface, } if (!in_endpoint || !out_endpoint) { - nfc_dev_err(&interface->dev, "Could not find bulk-in or" - " bulk-out endpoint"); + nfc_dev_err(&interface->dev, + "Could not find bulk-in or bulk-out endpoint"); rc = -ENODEV; goto error; } From 37cf4fc6c98f230ca026763ae03cb88ff786eeb9 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:50 +0100 Subject: [PATCH 288/478] NFC: pn533: Fix spacing issues Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 43970610c710..19fecdba611b 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1124,7 +1124,7 @@ static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data, { struct pn533_target_type_a *tgt_type_a; - tgt_type_a = (struct pn533_target_type_a *) tgt_data; + tgt_type_a = (struct pn533_target_type_a *)tgt_data; if (!pn533_target_type_a_is_valid(tgt_type_a, tgt_data_len)) return -EPROTO; @@ -1182,7 +1182,7 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data, { struct pn533_target_felica *tgt_felica; - tgt_felica = (struct pn533_target_felica *) tgt_data; + tgt_felica = (struct pn533_target_felica *)tgt_data; if (!pn533_target_felica_is_valid(tgt_felica, tgt_data_len)) return -EPROTO; @@ -1232,7 +1232,7 @@ static int pn533_target_found_jewel(struct nfc_target *nfc_tgt, u8 *tgt_data, { struct pn533_target_jewel *tgt_jewel; - tgt_jewel = (struct pn533_target_jewel *) tgt_data; + tgt_jewel = (struct pn533_target_jewel *)tgt_data; if (!pn533_target_jewel_is_valid(tgt_jewel, tgt_data_len)) return -EPROTO; @@ -1291,7 +1291,7 @@ static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data, { struct pn533_target_type_b *tgt_type_b; - tgt_type_b = (struct pn533_target_type_b *) tgt_data; + tgt_type_b = (struct pn533_target_type_b *)tgt_data; if (!pn533_target_type_b_is_valid(tgt_type_b, tgt_data_len)) return -EPROTO; @@ -1368,7 +1368,7 @@ static void pn533_poll_reset_mod_list(struct pn533 *dev) static void pn533_poll_add_mod(struct pn533 *dev, u8 mod_index) { dev->poll_mod_active[dev->poll_mod_count] = - (struct pn533_poll_modulations *) &poll_mod[mod_index]; + (struct pn533_poll_modulations *)&poll_mod[mod_index]; dev->poll_mod_count++; } @@ -1565,7 +1565,7 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp) static void pn533_listen_mode_timer(unsigned long data) { - struct pn533 *dev = (struct pn533 *) data; + struct pn533 *dev = (struct pn533 *)data; nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout"); @@ -1789,7 +1789,7 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev) if (IS_ERR(resp)) return PTR_ERR(resp); - rsp = (struct pn533_cmd_activate_response *) resp->data; + rsp = (struct pn533_cmd_activate_response *)resp->data; rc = rsp->status & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) dev_kfree_skb(resp); @@ -1969,7 +1969,7 @@ static int pn533_mod_to_baud(struct pn533 *dev) #define PASSIVE_DATA_LEN 5 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, - u8 comm_mode, u8* gb, size_t gb_len) + u8 comm_mode, u8 *gb, size_t gb_len) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct sk_buff *skb; From 5d467742bc53eff6233bd437c13dcefb449246f8 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:51 +0100 Subject: [PATCH 289/478] NFC: pn533: Fix open parenthesis alignment Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 19fecdba611b..02043dc430b9 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -640,7 +640,7 @@ static int __pn533_send_cmd_frame_async(struct pn533 *dev, int rc; nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", - PN533_FRAME_CMD(out_frame)); + PN533_FRAME_CMD(out_frame)); dev->cmd = PN533_FRAME_CMD(out_frame); dev->cmd_complete = cmd_complete; @@ -1107,9 +1107,9 @@ static bool pn533_target_type_a_is_valid(struct pn533_target_type_a *type_a, platconf = PN533_TYPE_A_SENS_RES_PLATCONF(type_a->sens_res); if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL && - platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) || - (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL && - platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL)) + platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) || + (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL && + platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL)) return false; /* Requirements 4.8.2.1, 4.8.2.3, 4.8.2.5 and 4.8.2.7 from NFC Forum */ @@ -1187,9 +1187,8 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data, if (!pn533_target_felica_is_valid(tgt_felica, tgt_data_len)) return -EPROTO; - if (tgt_felica->nfcid2[0] == PN533_FELICA_SENSF_NFCID2_DEP_B1 && - tgt_felica->nfcid2[1] == - PN533_FELICA_SENSF_NFCID2_DEP_B2) + if ((tgt_felica->nfcid2[0] == PN533_FELICA_SENSF_NFCID2_DEP_B1) && + (tgt_felica->nfcid2[1] == PN533_FELICA_SENSF_NFCID2_DEP_B2)) nfc_tgt->supported_protocols = NFC_PROTO_NFC_DEP_MASK; else nfc_tgt->supported_protocols = NFC_PROTO_FELICA_MASK; @@ -1219,9 +1218,9 @@ static bool pn533_target_jewel_is_valid(struct pn533_target_jewel *jewel, platconf = PN533_TYPE_A_SENS_RES_PLATCONF(jewel->sens_res); if ((ssd == PN533_TYPE_A_SENS_RES_SSD_JEWEL && - platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) || - (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL && - platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL)) + platconf != PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL) || + (ssd != PN533_TYPE_A_SENS_RES_SSD_JEWEL && + platconf == PN533_TYPE_A_SENS_RES_PLATCONF_JEWEL)) return false; return true; @@ -1810,7 +1809,7 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev, int rc; nfc_dev_dbg(&dev->interface->dev, "%s - protocol=%u", __func__, - protocol); + protocol); if (dev->poll_mod_count) { nfc_dev_err(&dev->interface->dev, @@ -1908,7 +1907,7 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, if (dev->tgt_available_prots && !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) { nfc_dev_err(&dev->interface->dev, - "The target does not support DEP"); + "The target does not support DEP"); rc = -EINVAL; goto error; } @@ -1918,7 +1917,7 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, rc = rsp->status & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) { nfc_dev_err(&dev->interface->dev, - "Bringing DEP link up failed %d", rc); + "Bringing DEP link up failed %d", rc); goto error; } @@ -1982,13 +1981,13 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, if (dev->poll_mod_count) { nfc_dev_err(&dev->interface->dev, - "Cannot bring the DEP link up while polling"); + "Cannot bring the DEP link up while polling"); return -EBUSY; } if (dev->tgt_active_prot) { nfc_dev_err(&dev->interface->dev, - "There is already an active target"); + "There is already an active target"); return -EBUSY; } @@ -2533,17 +2532,15 @@ static int pn533_probe(struct usb_interface *interface, dev->out_frame = kmalloc(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); dev->out_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->in_frame || !dev->out_frame || - !dev->in_urb || !dev->out_urb) + if (!dev->in_frame || !dev->out_frame || !dev->in_urb || !dev->out_urb) goto error; usb_fill_bulk_urb(dev->in_urb, dev->udev, - usb_rcvbulkpipe(dev->udev, in_endpoint), - NULL, 0, NULL, dev); + usb_rcvbulkpipe(dev->udev, in_endpoint), + NULL, 0, NULL, dev); usb_fill_bulk_urb(dev->out_urb, dev->udev, - usb_sndbulkpipe(dev->udev, out_endpoint), - NULL, 0, - pn533_send_complete, dev); + usb_sndbulkpipe(dev->udev, out_endpoint), + NULL, 0, pn533_send_complete, dev); INIT_WORK(&dev->cmd_work, pn533_wq_cmd); INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete); From b08e860355ba6f67b769c9061f468f7a8856bad9 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:52 +0100 Subject: [PATCH 290/478] NFC: pn533: Fix minor style issues Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 02043dc430b9..2cb8ceb9be8d 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1376,13 +1376,13 @@ static void pn533_poll_create_mod_list(struct pn533 *dev, { pn533_poll_reset_mod_list(dev); - if (im_protocols & NFC_PROTO_MIFARE_MASK - || im_protocols & NFC_PROTO_ISO14443_MASK - || im_protocols & NFC_PROTO_NFC_DEP_MASK) + if ((im_protocols & NFC_PROTO_MIFARE_MASK) || + (im_protocols & NFC_PROTO_ISO14443_MASK) || + (im_protocols & NFC_PROTO_NFC_DEP_MASK)) pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A); - if (im_protocols & NFC_PROTO_FELICA_MASK - || im_protocols & NFC_PROTO_NFC_DEP_MASK) { + if (im_protocols & NFC_PROTO_FELICA_MASK || + im_protocols & NFC_PROTO_NFC_DEP_MASK) { pn533_poll_add_mod(dev, PN533_POLL_MOD_212KBPS_FELICA); pn533_poll_add_mod(dev, PN533_POLL_MOD_424KBPS_FELICA); } From 89fb20256725e735601ea6439680956fa2ec4741 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:53 +0100 Subject: [PATCH 291/478] NFC: pn533: Remove unused pn533_send_cmd_frame_asy Remove obsolete send async api as it's no longer used. Remove global dev->in_frame as well, as each packet is kept is a seperate skb struct now, so that's not used anymore. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 75 +++++---------------------------------------- 1 file changed, 7 insertions(+), 68 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 2cb8ceb9be8d..d17ec6a664a0 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -329,7 +329,6 @@ struct pn533 { struct pn533_frame *out_frame; struct urb *in_urb; - struct pn533_frame *in_frame; struct sk_buff_head resp_q; @@ -371,13 +370,9 @@ struct pn533 { struct pn533_cmd { struct list_head queue; - struct pn533_frame *out_frame; - struct pn533_frame *in_frame; - int in_frame_len; u8 cmd_code; struct sk_buff *req; struct sk_buff *resp; - pn533_cmd_complete_t cmd_complete; void *arg; }; @@ -911,69 +906,16 @@ static void pn533_wq_cmd(struct work_struct *work) mutex_unlock(&dev->cmd_lock); - if (cmd->cmd_code != PN533_CMD_UNDEF) - __pn533_send_cmd_frame_async(dev, - (struct pn533_frame *)cmd->req->data, - (struct pn533_frame *)cmd->resp->data, - PN533_NORMAL_FRAME_MAX_LEN, - pn533_send_async_complete, - cmd->arg); - else - __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame, - cmd->in_frame_len, - cmd->cmd_complete, cmd->arg); + __pn533_send_cmd_frame_async(dev, + (struct pn533_frame *)cmd->req->data, + (struct pn533_frame *)cmd->resp->data, + PN533_NORMAL_FRAME_MAX_LEN, + pn533_send_async_complete, + cmd->arg); kfree(cmd); } -static int pn533_send_cmd_frame_async(struct pn533 *dev, - struct pn533_frame *out_frame, - struct pn533_frame *in_frame, - int in_frame_len, - pn533_cmd_complete_t cmd_complete, - void *arg) -{ - struct pn533_cmd *cmd; - int rc = 0; - - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - - mutex_lock(&dev->cmd_lock); - - if (!dev->cmd_pending) { - rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, - in_frame_len, cmd_complete, - arg); - if (!rc) - dev->cmd_pending = 1; - - goto unlock; - } - - nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__); - - cmd = kzalloc(sizeof(struct pn533_cmd), GFP_KERNEL); - if (!cmd) { - rc = -ENOMEM; - goto unlock; - } - - INIT_LIST_HEAD(&cmd->queue); - cmd->out_frame = out_frame; - cmd->in_frame = in_frame; - cmd->in_frame_len = in_frame_len; - cmd->cmd_code = PN533_CMD_UNDEF; - cmd->cmd_complete = cmd_complete; - cmd->arg = arg; - - list_add_tail(&cmd->queue, &dev->cmd_queue); - -unlock: - mutex_unlock(&dev->cmd_lock); - - return rc; -} - struct pn533_sync_cmd_response { struct sk_buff *resp; struct completion done; @@ -2527,12 +2469,11 @@ static int pn533_probe(struct usb_interface *interface, goto error; } - dev->in_frame = kmalloc(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); dev->in_urb = usb_alloc_urb(0, GFP_KERNEL); dev->out_frame = kmalloc(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); dev->out_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->in_frame || !dev->out_frame || !dev->in_urb || !dev->out_urb) + if (!dev->out_frame || !dev->in_urb || !dev->out_urb) goto error; usb_fill_bulk_urb(dev->in_urb, dev->udev, @@ -2616,7 +2557,6 @@ free_nfc_dev: destroy_wq: destroy_workqueue(dev->wq); error: - kfree(dev->in_frame); usb_free_urb(dev->in_urb); kfree(dev->out_frame); usb_free_urb(dev->out_urb); @@ -2649,7 +2589,6 @@ static void pn533_disconnect(struct usb_interface *interface) kfree(cmd); } - kfree(dev->in_frame); usb_free_urb(dev->in_urb); kfree(dev->out_frame); usb_free_urb(dev->out_urb); From f8f991710b580878c88cfe49d918cfb0c9e15236 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:54 +0100 Subject: [PATCH 292/478] NFC: pn533: Fix urb->status handling Fix text message to be more suitable for the error code and treat ESHUTDOWN as an error not debug msg. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index d17ec6a664a0..898375d95f36 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -505,18 +505,19 @@ static void pn533_recv_response(struct urb *urb) switch (urb->status) { case 0: - /* success */ - break; + break; /* success */ case -ECONNRESET: case -ENOENT: - case -ESHUTDOWN: nfc_dev_dbg(&dev->interface->dev, - "Urb shutting down with status: %d", urb->status); + "The urb has been canceled (status %d)", + urb->status); dev->wq_in_error = urb->status; goto sched_wq; + break; + case -ESHUTDOWN: default: nfc_dev_err(&dev->interface->dev, - "Nonzero urb status received: %d", urb->status); + "Urb failure (status %d)", urb->status); dev->wq_in_error = urb->status; goto sched_wq; } @@ -562,18 +563,19 @@ static void pn533_recv_ack(struct urb *urb) switch (urb->status) { case 0: - /* success */ - break; + break; /* success */ case -ECONNRESET: case -ENOENT: - case -ESHUTDOWN: nfc_dev_dbg(&dev->interface->dev, - "Urb shutting down with status: %d", urb->status); + "The urb has been stopped (status %d)", + urb->status); dev->wq_in_error = urb->status; goto sched_wq; + break; + case -ESHUTDOWN: default: nfc_dev_err(&dev->interface->dev, - "Nonzero urb status received: %d", urb->status); + "Urb failure (status %d)", urb->status); dev->wq_in_error = urb->status; goto sched_wq; } @@ -981,17 +983,17 @@ static void pn533_send_complete(struct urb *urb) switch (urb->status) { case 0: - /* success */ - break; + break; /* success */ case -ECONNRESET: case -ENOENT: - case -ESHUTDOWN: nfc_dev_dbg(&dev->interface->dev, - "Urb shutting down with status: %d", urb->status); + "The urb has been stopped (status %d)", + urb->status); break; + case -ESHUTDOWN: default: - nfc_dev_dbg(&dev->interface->dev, - "Nonzero urb status received: %d", urb->status); + nfc_dev_err(&dev->interface->dev, + "Urb failure (status %d)", urb->status); } } From fcfafc7690164d08dfd8133a860f25db8019ca2e Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:55 +0100 Subject: [PATCH 293/478] NFC: pn533: Cleanup debug messages Remove debug messages which do not include valueable informations in debug mode. Add some new ones for better tracking or reword when if necessary. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 898375d95f36..5fb5b8f1ef3f 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -524,6 +524,7 @@ static void pn533_recv_response(struct urb *urb) in_frame = dev->in_urb->transfer_buffer; + nfc_dev_dbg(&dev->interface->dev, "Received a frame."); print_hex_dump(KERN_DEBUG, "PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame, PN533_FRAME_SIZE(in_frame), false); @@ -540,7 +541,6 @@ static void pn533_recv_response(struct urb *urb) goto sched_wq; } - nfc_dev_dbg(&dev->interface->dev, "Received a valid frame"); dev->wq_in_error = 0; dev->wq_in_frame = in_frame; @@ -588,8 +588,6 @@ static void pn533_recv_ack(struct urb *urb) goto sched_wq; } - nfc_dev_dbg(&dev->interface->dev, "Received a valid ack"); - rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC); if (rc) { nfc_dev_err(&dev->interface->dev, @@ -636,9 +634,6 @@ static int __pn533_send_cmd_frame_async(struct pn533 *dev, { int rc; - nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", - PN533_FRAME_CMD(out_frame)); - dev->cmd = PN533_FRAME_CMD(out_frame); dev->cmd_complete = cmd_complete; dev->cmd_complete_arg = arg; @@ -705,10 +700,6 @@ static int pn533_send_async_complete(struct pn533 *dev, void *_arg, u8 *params, dev_kfree_skb(req); if (params_len < 0) { - nfc_dev_err(&dev->interface->dev, - "Error %d when starting as a target", - params_len); - arg->complete_cb(dev, arg->complete_cb_context, ERR_PTR(params_len)); rc = params_len; @@ -737,7 +728,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, struct pn533_send_async_complete_arg *arg; int rc = 0; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", cmd_code); arg = kzalloc(sizeof(arg), GFP_KERNEL); if (!arg) @@ -765,7 +756,8 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, goto unlock; } - nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__); + nfc_dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x", __func__, + cmd_code); cmd = kzalloc(sizeof(struct pn533_cmd), GFP_KERNEL); if (!cmd) { @@ -801,8 +793,6 @@ static int pn533_send_data_async(struct pn533 *dev, u8 cmd_code, PN533_FRAME_MAX_PAYLOAD_LEN + PN533_FRAME_TAIL_LEN; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - resp = nfc_alloc_recv_skb(resp_len, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -823,8 +813,6 @@ static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code, struct sk_buff *resp; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - resp = alloc_skb(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -858,8 +846,6 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code, PN533_FRAME_MAX_PAYLOAD_LEN + PN533_FRAME_TAIL_LEN; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - resp = alloc_skb(resp_len, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -928,8 +914,6 @@ static int pn533_send_sync_complete(struct pn533 *dev, void *_arg, { struct pn533_sync_cmd_response *arg = _arg; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - arg->resp = resp; complete(&arg->done); @@ -959,8 +943,6 @@ static struct sk_buff *pn533_send_cmd_sync(struct pn533 *dev, u8 cmd_code, int rc; struct pn533_sync_cmd_response arg; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - init_completion(&arg.done); rc = pn533_send_cmd_async(dev, cmd_code, req, @@ -979,8 +961,6 @@ static void pn533_send_complete(struct urb *urb) { struct pn533 *dev = urb->context; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - switch (urb->status) { case 0: break; /* success */ @@ -1992,6 +1972,8 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + pn533_poll_reset_mod_list(dev); if (dev->tgt_mode || dev->tgt_active_prot) { @@ -2017,7 +1999,7 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev) struct sk_buff *skb, *tmp, *t; unsigned int skb_len = 0, tmp_len = 0; - nfc_dev_dbg(&dev->interface->dev, "%s\n", __func__); + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); if (skb_queue_empty(&dev->resp_q)) return NULL; From 5b5a4437ae078cbe35020e74eb0b66ebedc2a2c0 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:56 +0100 Subject: [PATCH 294/478] NFC: pn533: Don't use out_frame in pn533_send_ack dev->out_frame buffer is much bigger for ACK frame needs. Use local buffer instead. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 5fb5b8f1ef3f..5bc0c7cfea9a 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -94,7 +94,8 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \ PN533_FRAME_TAIL_LEN) -#define PN533_FRAME_ACK_SIZE (sizeof(struct pn533_frame) + 1) +#define PN533_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2), + Postamble (1) */ #define PN533_FRAME_CHECKSUM(f) (f->data[f->datalen]) #define PN533_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1]) @@ -402,26 +403,6 @@ static u8 pn533_data_checksum(u8 *data, int datalen) return pn533_checksum(sum); } -/** - * pn533_tx_frame_ack - create a ack frame - * @frame: The frame to be set as ack - * - * Ack is different type of standard frame. As a standard frame, it has - * preamble and start_frame. However the checksum of this frame must fail, - * i.e. datalen + datalen_checksum must NOT be zero. When the checksum test - * fails and datalen = 0 and datalen_checksum = 0xFF, the frame is a ack. - * After datalen_checksum field, the postamble is placed. - */ -static void pn533_tx_frame_ack(struct pn533_frame *frame) -{ - frame->preamble = 0; - frame->start_frame = cpu_to_be16(PN533_SOF); - frame->datalen = 0; - frame->datalen_checksum = 0xFF; - /* data[0] is used as postamble */ - frame->data[0] = 0; -} - static void pn533_tx_frame_init(struct pn533_frame *frame, u8 cmd) { frame->preamble = 0; @@ -612,14 +593,14 @@ static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags) static int pn533_send_ack(struct pn533 *dev, gfp_t flags) { + u8 ack[PN533_FRAME_ACK_SIZE] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00}; + /* spec 7.1.1.3: Preamble, SoPC (2), ACK Code (2), Postamble */ int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - pn533_tx_frame_ack(dev->out_frame); - - dev->out_urb->transfer_buffer = dev->out_frame; - dev->out_urb->transfer_buffer_length = PN533_FRAME_ACK_SIZE; + dev->out_urb->transfer_buffer = ack; + dev->out_urb->transfer_buffer_length = sizeof(ack); rc = usb_submit_urb(dev->out_urb, flags); return rc; From e8f40531113c7e8c6266886803d5bf3a9e5c8d1b Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:57 +0100 Subject: [PATCH 295/478] NFC: pn533: Use skb in __pn533_send_cmd_frame_asyn __pn533_send_cmd_frame_async() should be frame type independent. So, don't use pn533_frame type params and instead use skb for req and resp pointers. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 5bc0c7cfea9a..cfbfc1183926 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -606,28 +606,27 @@ static int pn533_send_ack(struct pn533 *dev, gfp_t flags) return rc; } -static int __pn533_send_cmd_frame_async(struct pn533 *dev, - struct pn533_frame *out_frame, - struct pn533_frame *in_frame, - int in_frame_len, +static int __pn533_send_frame_async(struct pn533 *dev, + struct sk_buff *out, + struct sk_buff *in, + int in_len, pn533_cmd_complete_t cmd_complete, void *arg) { int rc; - dev->cmd = PN533_FRAME_CMD(out_frame); + dev->cmd = PN533_FRAME_CMD(((struct pn533_frame *)out->data)); dev->cmd_complete = cmd_complete; dev->cmd_complete_arg = arg; - dev->out_urb->transfer_buffer = out_frame; - dev->out_urb->transfer_buffer_length = - PN533_FRAME_SIZE(out_frame); + dev->out_urb->transfer_buffer = out->data; + dev->out_urb->transfer_buffer_length = out->len; - dev->in_urb->transfer_buffer = in_frame; - dev->in_urb->transfer_buffer_length = in_frame_len; + dev->in_urb->transfer_buffer = in->data; + dev->in_urb->transfer_buffer_length = in_len; print_hex_dump(KERN_DEBUG, "PN533 TX: ", DUMP_PREFIX_NONE, 16, 1, - out_frame, PN533_FRAME_SIZE(out_frame), false); + out->data, out->len, false); rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); if (rc) @@ -725,11 +724,8 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, mutex_lock(&dev->cmd_lock); if (!dev->cmd_pending) { - rc = __pn533_send_cmd_frame_async(dev, - (struct pn533_frame *)req->data, - (struct pn533_frame *)resp->data, - resp_len, pn533_send_async_complete, - arg); + rc = __pn533_send_frame_async(dev, req, resp, resp_len, + pn533_send_async_complete, arg); if (rc) goto error; @@ -844,10 +840,8 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code, pn533_build_cmd_frame(cmd_code, req); - rc = __pn533_send_cmd_frame_async(dev, (struct pn533_frame *)req->data, - (struct pn533_frame *)resp->data, - resp_len, pn533_send_async_complete, - arg); + rc = __pn533_send_frame_async(dev, req, resp, resp_len, + pn533_send_async_complete, arg); if (rc < 0) { dev_kfree_skb(resp); kfree(arg); @@ -875,12 +869,10 @@ static void pn533_wq_cmd(struct work_struct *work) mutex_unlock(&dev->cmd_lock); - __pn533_send_cmd_frame_async(dev, - (struct pn533_frame *)cmd->req->data, - (struct pn533_frame *)cmd->resp->data, - PN533_NORMAL_FRAME_MAX_LEN, - pn533_send_async_complete, - cmd->arg); + __pn533_send_frame_async(dev, cmd->req, cmd->resp, + PN533_NORMAL_FRAME_MAX_LEN, + pn533_send_async_complete, + cmd->arg); kfree(cmd); } From a5798094e927847f0d503b756a997175d7ce01ec Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:58 +0100 Subject: [PATCH 296/478] NFC: pn533: Remove deprecated dev->out_frame buff As it's not used anymore get rid of that buffer. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index cfbfc1183926..82867a036ccc 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -327,8 +327,6 @@ struct pn533 { struct nfc_dev *nfc_dev; struct urb *out_urb; - struct pn533_frame *out_frame; - struct urb *in_urb; struct sk_buff_head resp_q; @@ -2427,10 +2425,9 @@ static int pn533_probe(struct usb_interface *interface, } dev->in_urb = usb_alloc_urb(0, GFP_KERNEL); - dev->out_frame = kmalloc(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); dev->out_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->out_frame || !dev->in_urb || !dev->out_urb) + if (!dev->in_urb || !dev->out_urb) goto error; usb_fill_bulk_urb(dev->in_urb, dev->udev, @@ -2515,7 +2512,6 @@ destroy_wq: destroy_workqueue(dev->wq); error: usb_free_urb(dev->in_urb); - kfree(dev->out_frame); usb_free_urb(dev->out_urb); kfree(dev); return rc; @@ -2547,7 +2543,6 @@ static void pn533_disconnect(struct usb_interface *interface) } usb_free_urb(dev->in_urb); - kfree(dev->out_frame); usb_free_urb(dev->out_urb); kfree(dev); From 0c33d2626d651f3f52d6a1e8b612e2830a4ab71f Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:42:59 +0100 Subject: [PATCH 297/478] NFC: pn533: Cleanup pn533_cmd_complete_t 'params' arg in pn533_cmd_complete_t definition has been deprecated and currently is not in use (resp skb is pass in arg ptr), so remove it. Also 'params_len' arg is used as a transfer status indicator, so simply reword it appropriately. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 82867a036ccc..db13cd798149 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -109,8 +109,6 @@ MODULE_DEVICE_TABLE(usb, pn533_table); /* PN533 Commands */ #define PN533_FRAME_CMD(f) (f->data[1]) -#define PN533_FRAME_CMD_PARAMS_PTR(f) (&f->data[2]) -#define PN533_FRAME_CMD_PARAMS_LEN(f) (f->datalen - 2) #define PN533_CMD_GET_FIRMWARE_VERSION 0x02 #define PN533_CMD_RF_CONFIGURATION 0x32 @@ -135,8 +133,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table); struct pn533; -typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg, - u8 *params, int params_len); +typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg, int status); typedef int (*pn533_send_async_complete_t) (struct pn533 *dev, void *arg, struct sk_buff *resp); @@ -458,19 +455,9 @@ static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd) static void pn533_wq_cmd_complete(struct work_struct *work) { struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work); - struct pn533_frame *in_frame; int rc; - in_frame = dev->wq_in_frame; - - if (dev->wq_in_error) - rc = dev->cmd_complete(dev, dev->cmd_complete_arg, NULL, - dev->wq_in_error); - else - rc = dev->cmd_complete(dev, dev->cmd_complete_arg, - PN533_FRAME_CMD_PARAMS_PTR(in_frame), - PN533_FRAME_CMD_PARAMS_LEN(in_frame)); - + rc = dev->cmd_complete(dev, dev->cmd_complete_arg, dev->wq_in_error); if (rc != -EINPROGRESS) queue_work(dev->wq, &dev->cmd_work); } @@ -664,8 +651,7 @@ struct pn533_send_async_complete_arg { struct sk_buff *req; }; -static int pn533_send_async_complete(struct pn533 *dev, void *_arg, u8 *params, - int params_len) +static int pn533_send_async_complete(struct pn533 *dev, void *_arg, int status) { struct pn533_send_async_complete_arg *arg = _arg; @@ -677,12 +663,12 @@ static int pn533_send_async_complete(struct pn533 *dev, void *_arg, u8 *params, dev_kfree_skb(req); - if (params_len < 0) { + if (status < 0) { arg->complete_cb(dev, arg->complete_cb_context, - ERR_PTR(params_len)); - rc = params_len; + ERR_PTR(status)); dev_kfree_skb(resp); - goto out; + kfree(arg); + return status; } skb_put(resp, PN533_FRAME_SIZE(frame)); @@ -691,7 +677,6 @@ static int pn533_send_async_complete(struct pn533 *dev, void *_arg, u8 *params, rc = arg->complete_cb(dev, arg->complete_cb_context, resp); -out: kfree(arg); return rc; } From 16f7541914e8bee0e76c510c4d8bc5ad8a5edf00 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:43:00 +0100 Subject: [PATCH 298/478] NFC: pn533: Remove unused dev->wq_in_frame Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index db13cd798149..58f7f2cc6ae9 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -335,7 +335,6 @@ struct pn533 { struct work_struct mi_work; struct work_struct tg_work; struct timer_list listen_timer; - struct pn533_frame *wq_in_frame; int wq_in_error; int cancel_listen; @@ -467,8 +466,6 @@ static void pn533_recv_response(struct urb *urb) struct pn533 *dev = urb->context; struct pn533_frame *in_frame; - dev->wq_in_frame = NULL; - switch (urb->status) { case 0: break; /* success */ @@ -508,7 +505,6 @@ static void pn533_recv_response(struct urb *urb) } dev->wq_in_error = 0; - dev->wq_in_frame = in_frame; sched_wq: queue_work(dev->wq, &dev->cmd_complete_work); @@ -565,7 +561,6 @@ static void pn533_recv_ack(struct urb *urb) return; sched_wq: - dev->wq_in_frame = NULL; queue_work(dev->wq, &dev->cmd_complete_work); } From 9e2d493e74aca105aa86de56dbf80a0a654c4c53 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Mon, 10 Dec 2012 14:43:01 +0100 Subject: [PATCH 299/478] NFC: pn533: Introduce ops for frame logic Encapsulate whole frame logic (tx/rx frame structure and size) inside the ops structure to make the core driver generic for devices which handle frames in non standard menner (different then pn533 spec say). Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 208 ++++++++++++++++++++++++++++---------------- 1 file changed, 135 insertions(+), 73 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 58f7f2cc6ae9..e8c083203b33 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -79,9 +79,6 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_LISTEN_TIME 2 /* frame definitions */ -#define PN533_NORMAL_FRAME_MAX_LEN 262 /* 6 (PREAMBLE, SOF, LEN, LCS, TFI) - 254 (DATA) - 2 (DCS, postamble) */ #define PN533_FRAME_HEADER_LEN (sizeof(struct pn533_frame) \ + 2) /* data[0] TFI, data[1] CC */ #define PN533_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/ @@ -92,8 +89,6 @@ MODULE_DEVICE_TABLE(usb, pn533_table); */ #define PN533_FRAME_MAX_PAYLOAD_LEN 263 -#define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \ - PN533_FRAME_TAIL_LEN) #define PN533_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2), Postamble (1) */ #define PN533_FRAME_CHECKSUM(f) (f->data[f->datalen]) @@ -361,6 +356,8 @@ struct pn533 { struct list_head cmd_queue; u8 cmd_pending; + + struct pn533_frame_ops *ops; }; struct pn533_cmd { @@ -368,6 +365,7 @@ struct pn533_cmd { u8 cmd_code; struct sk_buff *req; struct sk_buff *resp; + int resp_len; void *arg; }; @@ -379,6 +377,22 @@ struct pn533_frame { u8 data[]; } __packed; +struct pn533_frame_ops { + void (*tx_frame_init)(void *frame, u8 cmd_code); + void (*tx_frame_finish)(void *frame); + void (*tx_update_payload_len)(void *frame, int len); + int tx_header_len; + int tx_tail_len; + + bool (*rx_is_frame_valid)(void *frame); + int (*rx_frame_size)(void *frame); + int rx_header_len; + int rx_tail_len; + + int max_payload_len; + u8 (*get_cmd_code)(void *frame); +}; + /* The rule: value + checksum = 0 */ static inline u8 pn533_checksum(u8 value) { @@ -397,17 +411,21 @@ static u8 pn533_data_checksum(u8 *data, int datalen) return pn533_checksum(sum); } -static void pn533_tx_frame_init(struct pn533_frame *frame, u8 cmd) +static void pn533_tx_frame_init(void *_frame, u8 cmd_code) { + struct pn533_frame *frame = _frame; + frame->preamble = 0; frame->start_frame = cpu_to_be16(PN533_SOF); PN533_FRAME_IDENTIFIER(frame) = PN533_DIR_OUT; - PN533_FRAME_CMD(frame) = cmd; + PN533_FRAME_CMD(frame) = cmd_code; frame->datalen = 2; } -static void pn533_tx_frame_finish(struct pn533_frame *frame) +static void pn533_tx_frame_finish(void *_frame) { + struct pn533_frame *frame = _frame; + frame->datalen_checksum = pn533_checksum(frame->datalen); PN533_FRAME_CHECKSUM(frame) = @@ -416,9 +434,17 @@ static void pn533_tx_frame_finish(struct pn533_frame *frame) PN533_FRAME_POSTAMBLE(frame) = 0; } -static bool pn533_rx_frame_is_valid(struct pn533_frame *frame) +static void pn533_tx_update_payload_len(void *_frame, int len) +{ + struct pn533_frame *frame = _frame; + + frame->datalen += len; +} + +static bool pn533_rx_frame_is_valid(void *_frame) { u8 checksum; + struct pn533_frame *frame = _frame; if (frame->start_frame != cpu_to_be16(PN533_SOF)) return false; @@ -445,9 +471,39 @@ static bool pn533_rx_frame_is_ack(struct pn533_frame *frame) return true; } -static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd) +static inline int pn533_rx_frame_size(void *frame) { - return (PN533_FRAME_CMD(frame) == PN533_CMD_RESPONSE(cmd)); + struct pn533_frame *f = frame; + + return sizeof(struct pn533_frame) + f->datalen + PN533_FRAME_TAIL_LEN; +} + +static u8 pn533_get_cmd_code(void *frame) +{ + struct pn533_frame *f = frame; + + return PN533_FRAME_CMD(f); +} + +struct pn533_frame_ops pn533_std_frame_ops = { + .tx_frame_init = pn533_tx_frame_init, + .tx_frame_finish = pn533_tx_frame_finish, + .tx_update_payload_len = pn533_tx_update_payload_len, + .tx_header_len = PN533_FRAME_HEADER_LEN, + .tx_tail_len = PN533_FRAME_TAIL_LEN, + + .rx_is_frame_valid = pn533_rx_frame_is_valid, + .rx_frame_size = pn533_rx_frame_size, + .rx_header_len = PN533_FRAME_HEADER_LEN, + .rx_tail_len = PN533_FRAME_TAIL_LEN, + + .max_payload_len = PN533_FRAME_MAX_PAYLOAD_LEN, + .get_cmd_code = pn533_get_cmd_code, +}; + +static bool pn533_rx_frame_is_cmd_response(struct pn533 *dev, void *frame) +{ + return (dev->ops->get_cmd_code(frame) == PN533_CMD_RESPONSE(dev->cmd)); } @@ -464,7 +520,7 @@ static void pn533_wq_cmd_complete(struct work_struct *work) static void pn533_recv_response(struct urb *urb) { struct pn533 *dev = urb->context; - struct pn533_frame *in_frame; + u8 *in_frame; switch (urb->status) { case 0: @@ -489,15 +545,15 @@ static void pn533_recv_response(struct urb *urb) nfc_dev_dbg(&dev->interface->dev, "Received a frame."); print_hex_dump(KERN_DEBUG, "PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, - in_frame, PN533_FRAME_SIZE(in_frame), false); + in_frame, dev->ops->rx_frame_size(in_frame), false); - if (!pn533_rx_frame_is_valid(in_frame)) { + if (!dev->ops->rx_is_frame_valid(in_frame)) { nfc_dev_err(&dev->interface->dev, "Received an invalid frame"); dev->wq_in_error = -EIO; goto sched_wq; } - if (!pn533_rx_frame_is_cmd_response(in_frame, dev->cmd)) { + if (!pn533_rx_frame_is_cmd_response(dev, in_frame)) { nfc_dev_err(&dev->interface->dev, "It it not the response to the last command"); dev->wq_in_error = -EIO; @@ -595,7 +651,7 @@ static int __pn533_send_frame_async(struct pn533 *dev, { int rc; - dev->cmd = PN533_FRAME_CMD(((struct pn533_frame *)out->data)); + dev->cmd = dev->ops->get_cmd_code(out->data); dev->cmd_complete = cmd_complete; dev->cmd_complete_arg = arg; @@ -623,20 +679,20 @@ error: return rc; } -static void pn533_build_cmd_frame(u8 cmd_code, struct sk_buff *skb) +static void pn533_build_cmd_frame(struct pn533 *dev, u8 cmd_code, + struct sk_buff *skb) { - struct pn533_frame *frame; /* payload is already there, just update datalen */ int payload_len = skb->len; + struct pn533_frame_ops *ops = dev->ops; - skb_push(skb, PN533_FRAME_HEADER_LEN); - skb_put(skb, PN533_FRAME_TAIL_LEN); - frame = (struct pn533_frame *)skb->data; + skb_push(skb, ops->tx_header_len); + skb_put(skb, ops->tx_tail_len); - pn533_tx_frame_init(frame, cmd_code); - frame->datalen += payload_len; - pn533_tx_frame_finish(frame); + ops->tx_frame_init(skb->data, cmd_code); + ops->tx_update_payload_len(skb->data, payload_len); + ops->tx_frame_finish(skb->data); } struct pn533_send_async_complete_arg { @@ -653,7 +709,6 @@ static int pn533_send_async_complete(struct pn533 *dev, void *_arg, int status) struct sk_buff *req = arg->req; struct sk_buff *resp = arg->resp; - struct pn533_frame *frame = (struct pn533_frame *)resp->data; int rc; dev_kfree_skb(req); @@ -666,9 +721,9 @@ static int pn533_send_async_complete(struct pn533 *dev, void *_arg, int status) return status; } - skb_put(resp, PN533_FRAME_SIZE(frame)); - skb_pull(resp, PN533_FRAME_HEADER_LEN); - skb_trim(resp, resp->len - PN533_FRAME_TAIL_LEN); + skb_put(resp, dev->ops->rx_frame_size(resp->data)); + skb_pull(resp, dev->ops->rx_header_len); + skb_trim(resp, resp->len - dev->ops->rx_tail_len); rc = arg->complete_cb(dev, arg->complete_cb_context, resp); @@ -697,7 +752,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, arg->resp = resp; arg->req = req; - pn533_build_cmd_frame(cmd_code, req); + pn533_build_cmd_frame(dev, cmd_code, req); mutex_lock(&dev->cmd_lock); @@ -724,6 +779,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, cmd->cmd_code = cmd_code; cmd->req = req; cmd->resp = resp; + cmd->resp_len = resp_len; cmd->arg = arg; list_add_tail(&cmd->queue, &dev->cmd_queue); @@ -744,9 +800,9 @@ static int pn533_send_data_async(struct pn533 *dev, u8 cmd_code, { struct sk_buff *resp; int rc; - int resp_len = PN533_FRAME_HEADER_LEN + - PN533_FRAME_MAX_PAYLOAD_LEN + - PN533_FRAME_TAIL_LEN; + int resp_len = dev->ops->rx_header_len + + dev->ops->max_payload_len + + dev->ops->rx_tail_len; resp = nfc_alloc_recv_skb(resp_len, GFP_KERNEL); if (!resp) @@ -767,14 +823,16 @@ static int pn533_send_cmd_async(struct pn533 *dev, u8 cmd_code, { struct sk_buff *resp; int rc; + int resp_len = dev->ops->rx_header_len + + dev->ops->max_payload_len + + dev->ops->rx_tail_len; - resp = alloc_skb(PN533_NORMAL_FRAME_MAX_LEN, GFP_KERNEL); + resp = alloc_skb(resp_len, GFP_KERNEL); if (!resp) return -ENOMEM; - rc = __pn533_send_async(dev, cmd_code, req, resp, - PN533_NORMAL_FRAME_MAX_LEN, - complete_cb, complete_cb_context); + rc = __pn533_send_async(dev, cmd_code, req, resp, resp_len, complete_cb, + complete_cb_context); if (rc) dev_kfree_skb(resp); @@ -797,9 +855,9 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code, struct pn533_send_async_complete_arg *arg; struct sk_buff *resp; int rc; - int resp_len = PN533_FRAME_HEADER_LEN + - PN533_FRAME_MAX_PAYLOAD_LEN + - PN533_FRAME_TAIL_LEN; + int resp_len = dev->ops->rx_header_len + + dev->ops->max_payload_len + + dev->ops->rx_tail_len; resp = alloc_skb(resp_len, GFP_KERNEL); if (!resp) @@ -816,7 +874,7 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code, arg->resp = resp; arg->req = req; - pn533_build_cmd_frame(cmd_code, req); + pn533_build_cmd_frame(dev, cmd_code, req); rc = __pn533_send_frame_async(dev, req, resp, resp_len, pn533_send_async_complete, arg); @@ -847,10 +905,8 @@ static void pn533_wq_cmd(struct work_struct *work) mutex_unlock(&dev->cmd_lock); - __pn533_send_frame_async(dev, cmd->req, cmd->resp, - PN533_NORMAL_FRAME_MAX_LEN, - pn533_send_async_complete, - cmd->arg); + __pn533_send_frame_async(dev, cmd->req, cmd->resp, cmd->resp_len, + pn533_send_async_complete, cmd->arg); kfree(cmd); } @@ -928,16 +984,16 @@ static void pn533_send_complete(struct urb *urb) } } -static struct sk_buff *pn533_alloc_skb(unsigned int size) +static struct sk_buff *pn533_alloc_skb(struct pn533 *dev, unsigned int size) { struct sk_buff *skb; - skb = alloc_skb(PN533_FRAME_HEADER_LEN + + skb = alloc_skb(dev->ops->tx_header_len + size + - PN533_FRAME_TAIL_LEN, GFP_KERNEL); + dev->ops->tx_tail_len, GFP_KERNEL); if (skb) - skb_reserve(skb, PN533_FRAME_HEADER_LEN); + skb_reserve(skb, dev->ops->tx_header_len); return skb; } @@ -1297,11 +1353,14 @@ static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp) return -EAGAIN; } -static struct sk_buff *pn533_alloc_poll_tg_frame(u8 *gbytes, size_t gbytes_len) +static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev) { struct sk_buff *skb; u8 *felica, *nfcid3, *gb; + u8 *gbytes = dev->gb; + size_t gbytes_len = dev->gb_len; + u8 felica_params[18] = {0x1, 0xfe, /* DEP */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @@ -1316,7 +1375,7 @@ static struct sk_buff *pn533_alloc_poll_tg_frame(u8 *gbytes, size_t gbytes_len) gbytes_len + 1; /* len Tk*/ - skb = pn533_alloc_skb(skb_len); + skb = pn533_alloc_skb(dev, skb_len); if (!skb) return NULL; @@ -1382,7 +1441,7 @@ static void pn533_wq_tg_get_data(struct work_struct *work) nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - skb = pn533_alloc_skb(0); + skb = pn533_alloc_skb(dev, 0); if (!skb) return; @@ -1507,12 +1566,12 @@ stop_poll: return rc; } -static struct sk_buff *pn533_alloc_poll_in_frame(struct pn533_poll_modulations - *mod) +static struct sk_buff *pn533_alloc_poll_in_frame(struct pn533 *dev, + struct pn533_poll_modulations *mod) { struct sk_buff *skb; - skb = pn533_alloc_skb(mod->len); + skb = pn533_alloc_skb(dev, mod->len); if (!skb) return NULL; @@ -1535,10 +1594,10 @@ static int pn533_send_poll_frame(struct pn533 *dev) if (mod->len == 0) { /* Listen mode */ cmd_code = PN533_CMD_TG_INIT_AS_TARGET; - skb = pn533_alloc_poll_tg_frame(dev->gb, dev->gb_len); + skb = pn533_alloc_poll_tg_frame(dev); } else { /* Polling mode */ cmd_code = PN533_CMD_IN_LIST_PASSIVE_TARGET; - skb = pn533_alloc_poll_in_frame(mod); + skb = pn533_alloc_poll_in_frame(dev, mod); } if (!skb) { @@ -1652,7 +1711,7 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev) nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - skb = pn533_alloc_skb(sizeof(u8) * 2); /*TG + Next*/ + skb = pn533_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/ if (!skb) return -ENOMEM; @@ -1746,7 +1805,7 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, dev->tgt_active_prot = 0; skb_queue_purge(&dev->resp_q); - skb = pn533_alloc_skb(sizeof(u8)); + skb = pn533_alloc_skb(dev, sizeof(u8)); if (!skb) return; @@ -1877,7 +1936,7 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, if (comm_mode == NFC_COMM_PASSIVE) skb_len += PASSIVE_DATA_LEN; - skb = pn533_alloc_skb(skb_len); + skb = pn533_alloc_skb(dev, skb_len); if (!skb) return -ENOMEM; @@ -2161,7 +2220,7 @@ static void pn533_wq_mi_recv(struct work_struct *work) nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - skb = pn533_alloc_skb(PN533_CMD_DATAEXCH_HEAD_LEN); + skb = pn533_alloc_skb(dev, PN533_CMD_DATAEXCH_HEAD_LEN); if (!skb) goto error; @@ -2214,7 +2273,7 @@ static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */ - skb = pn533_alloc_skb(skb_len); + skb = pn533_alloc_skb(dev, skb_len); if (!skb) return -ENOMEM; @@ -2235,7 +2294,7 @@ static int pn533_get_firmware_version(struct pn533 *dev, struct sk_buff *skb; struct sk_buff *resp; - skb = pn533_alloc_skb(0); + skb = pn533_alloc_skb(dev, 0); if (!skb) return -ENOMEM; @@ -2259,7 +2318,7 @@ static int pn533_fw_reset(struct pn533 *dev) nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - skb = pn533_alloc_skb(sizeof(u8)); + skb = pn533_alloc_skb(dev, sizeof(u8)); if (!skb) return -ENOMEM; @@ -2436,14 +2495,7 @@ static int pn533_probe(struct usb_interface *interface, usb_set_intfdata(interface, dev); - memset(&fw_ver, 0, sizeof(fw_ver)); - rc = pn533_get_firmware_version(dev, &fw_ver); - if (rc < 0) - goto destroy_wq; - - nfc_dev_info(&dev->interface->dev, - "NXP PN533 firmware ver %d.%d now attached", - fw_ver.ver, fw_ver.rev); + dev->ops = &pn533_std_frame_ops; dev->device_type = id->driver_info; switch (dev->device_type) { @@ -2462,10 +2514,20 @@ static int pn533_probe(struct usb_interface *interface, goto destroy_wq; } + memset(&fw_ver, 0, sizeof(fw_ver)); + rc = pn533_get_firmware_version(dev, &fw_ver); + if (rc < 0) + goto destroy_wq; + + nfc_dev_info(&dev->interface->dev, + "NXP PN533 firmware ver %d.%d now attached", + fw_ver.ver, fw_ver.rev); + + dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols, - PN533_FRAME_HEADER_LEN + + dev->ops->tx_header_len + PN533_CMD_DATAEXCH_HEAD_LEN, - PN533_FRAME_TAIL_LEN); + dev->ops->tx_tail_len); if (!dev->nfc_dev) goto destroy_wq; From 5f4d6214ef5e9b1ff6a72ddfa387c1d72adfac98 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 6 Dec 2012 23:10:40 +0100 Subject: [PATCH 300/478] NFC: nfcwilink: Use devm_kzalloc devm_kzalloc allocates memory that is released when a driver detaches. This patch uses devm_kzalloc for data that is allocated in the probe function of a platform device and is only freed in the remove function. Signed-off-by: Julia Lawall Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcwilink.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index 50b1ee41afc6..c7c182d2b7df 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -526,7 +526,7 @@ static int nfcwilink_probe(struct platform_device *pdev) nfc_dev_dbg(&pdev->dev, "probe entry"); - drv = kzalloc(sizeof(struct nfcwilink), GFP_KERNEL); + drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL); if (!drv) { rc = -ENOMEM; goto exit; @@ -547,7 +547,7 @@ static int nfcwilink_probe(struct platform_device *pdev) if (!drv->ndev) { nfc_dev_err(&pdev->dev, "nci_allocate_device failed"); rc = -ENOMEM; - goto free_exit; + goto exit; } nci_set_parent_dev(drv->ndev, &pdev->dev); @@ -566,9 +566,6 @@ static int nfcwilink_probe(struct platform_device *pdev) free_dev_exit: nci_free_device(drv->ndev); -free_exit: - kfree(drv); - exit: return rc; } @@ -588,8 +585,6 @@ static int nfcwilink_remove(struct platform_device *pdev) nci_unregister_device(ndev); nci_free_device(ndev); - kfree(drv); - dev_set_drvdata(&pdev->dev, NULL); return 0; From f0c9103813b3045bd5b43220b6a78c9908a45d24 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Mon, 26 Nov 2012 18:06:27 +0100 Subject: [PATCH 301/478] NFC: Fixed nfc core and hci unregistration and cleanup When an adapter is removed, it will unregister itself from hci and/or nfc core. In order to do that safely, work tasks must first be canceled and prevented to be scheduled again, before the hci or nfc device can be destroyed. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 2 ++ include/net/nfc/nfc.h | 2 ++ net/nfc/core.c | 49 ++++++++++++++++++++----------------------- net/nfc/hci/core.c | 31 ++++++++++++++++++++------- net/nfc/hci/hcp.c | 7 +++++++ 5 files changed, 58 insertions(+), 33 deletions(-) diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 671953e11575..e6224571e5e6 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -87,6 +87,8 @@ struct nfc_hci_dev { u32 max_data_link_payload; + bool shutting_down; + struct mutex msg_tx_mutex; struct list_head msg_tx_queue; diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index fce80b2f9be7..1665674e86b2 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -115,6 +115,8 @@ struct nfc_dev { struct timer_list check_pres_timer; struct work_struct check_pres_work; + bool shutting_down; + struct nfc_ops *ops; }; #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev) diff --git a/net/nfc/core.c b/net/nfc/core.c index aa64ea441676..7d7b4ee34015 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -338,7 +338,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol) dev->active_target = target; dev->rf_mode = NFC_RF_INITIATOR; - if (dev->ops->check_presence) + if (dev->ops->check_presence && !dev->shutting_down) mod_timer(&dev->check_pres_timer, jiffies + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); } @@ -429,7 +429,7 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb, rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb, cb_context); - if (!rc && dev->ops->check_presence) + if (!rc && dev->ops->check_presence && !dev->shutting_down) mod_timer(&dev->check_pres_timer, jiffies + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) { @@ -684,11 +684,6 @@ static void nfc_release(struct device *d) pr_debug("dev_name=%s\n", dev_name(&dev->dev)); - if (dev->ops->check_presence) { - del_timer_sync(&dev->check_pres_timer); - cancel_work_sync(&dev->check_pres_work); - } - nfc_genl_data_exit(&dev->genl_data); kfree(dev->targets); kfree(dev); @@ -706,15 +701,16 @@ static void nfc_check_pres_work(struct work_struct *work) rc = dev->ops->check_presence(dev, dev->active_target); if (rc == -EOPNOTSUPP) goto exit; - if (!rc) { - mod_timer(&dev->check_pres_timer, jiffies + - msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); - } else { + if (rc) { u32 active_target_idx = dev->active_target->idx; device_unlock(&dev->dev); nfc_target_lost(dev, active_target_idx); return; } + + if (!dev->shutting_down) + mod_timer(&dev->check_pres_timer, jiffies + + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); } exit: @@ -853,26 +849,27 @@ void nfc_unregister_device(struct nfc_dev *dev) id = dev->idx; - mutex_lock(&nfc_devlist_mutex); - nfc_devlist_generation++; - - /* lock to avoid unregistering a device while an operation - is in progress */ - device_lock(&dev->dev); - device_del(&dev->dev); - device_unlock(&dev->dev); - - mutex_unlock(&nfc_devlist_mutex); - - nfc_llcp_unregister_device(dev); + if (dev->ops->check_presence) { + device_lock(&dev->dev); + dev->shutting_down = true; + device_unlock(&dev->dev); + del_timer_sync(&dev->check_pres_timer); + cancel_work_sync(&dev->check_pres_work); + } rc = nfc_genl_device_removed(dev); if (rc) - pr_debug("The userspace won't be notified that the device %s was removed\n", - dev_name(&dev->dev)); + pr_debug("The userspace won't be notified that the device %s " + "was removed\n", dev_name(&dev->dev)); + + nfc_llcp_unregister_device(dev); + + mutex_lock(&nfc_devlist_mutex); + nfc_devlist_generation++; + device_del(&dev->dev); + mutex_unlock(&nfc_devlist_mutex); ida_simple_remove(&nfc_index_ida, id); - } EXPORT_SYMBOL(nfc_unregister_device); diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 7bea574d5934..b4b84268653d 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -57,6 +57,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work) int r = 0; mutex_lock(&hdev->msg_tx_mutex); + if (hdev->shutting_down) + goto exit; if (hdev->cmd_pending_msg) { if (timer_pending(&hdev->cmd_timer) == 0) { @@ -868,6 +870,28 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) { struct hci_msg *msg, *n; + mutex_lock(&hdev->msg_tx_mutex); + + if (hdev->cmd_pending_msg) { + if (hdev->cmd_pending_msg->cb) + hdev->cmd_pending_msg->cb( + hdev->cmd_pending_msg->cb_context, + NULL, -ESHUTDOWN); + kfree(hdev->cmd_pending_msg); + hdev->cmd_pending_msg = NULL; + } + + hdev->shutting_down = true; + + mutex_unlock(&hdev->msg_tx_mutex); + + del_timer_sync(&hdev->cmd_timer); + cancel_work_sync(&hdev->msg_tx_work); + + cancel_work_sync(&hdev->msg_rx_work); + + nfc_unregister_device(hdev->ndev); + skb_queue_purge(&hdev->rx_hcp_frags); skb_queue_purge(&hdev->msg_rx_queue); @@ -876,13 +900,6 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) skb_queue_purge(&msg->msg_frags); kfree(msg); } - - del_timer_sync(&hdev->cmd_timer); - - nfc_unregister_device(hdev->ndev); - - cancel_work_sync(&hdev->msg_tx_work); - cancel_work_sync(&hdev->msg_rx_work); } EXPORT_SYMBOL(nfc_hci_unregister_device); diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c index bc308a7ca609..b6b4109f2343 100644 --- a/net/nfc/hci/hcp.c +++ b/net/nfc/hci/hcp.c @@ -105,6 +105,13 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, } mutex_lock(&hdev->msg_tx_mutex); + + if (hdev->shutting_down) { + err = -ESHUTDOWN; + mutex_unlock(&hdev->msg_tx_mutex); + goto out_skb_err; + } + list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue); mutex_unlock(&hdev->msg_tx_mutex); From 27c31191b3d7ff32c266a5dbea344b9aa96ebf14 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Wed, 28 Nov 2012 15:48:44 +0100 Subject: [PATCH 302/478] NFC: Added error handling in event_received hci ops There is no use to return an error if the caller doesn't get it. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 27 +++++++++++++-------------- include/net/nfc/hci.h | 4 ++-- net/nfc/hci/core.c | 8 +++++--- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index cc666de3b8e5..4af70f9c01aa 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -714,8 +714,8 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, return 0; } -static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, - u8 event, struct sk_buff *skb) +static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb) { struct sk_buff *rgb_skb = NULL; int r = 0; @@ -724,25 +724,23 @@ static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, switch (event) { case PN544_HCI_EVT_ACTIVATED: if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) - nfc_hci_target_discovered(hdev, gate); + r = nfc_hci_target_discovered(hdev, gate); else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, - &rgb_skb); - + &rgb_skb); if (r < 0) goto exit; - nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, - NFC_COMM_PASSIVE, rgb_skb->data, - rgb_skb->len); + r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, rgb_skb->data, + rgb_skb->len); kfree_skb(rgb_skb); } - break; case PN544_HCI_EVT_DEACTIVATED: - nfc_hci_send_event(hdev, gate, - NFC_HCI_EVT_END_OPERATION, NULL, 0); + r = nfc_hci_send_event(hdev, gate, NFC_HCI_EVT_END_OPERATION, + NULL, 0); break; case PN544_HCI_EVT_RCV_DATA: if (skb->len < 2) { @@ -757,15 +755,16 @@ static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, } skb_pull(skb, 2); - nfc_tm_data_received(hdev->ndev, skb); - - return; + return nfc_tm_data_received(hdev->ndev, skb); default: + pr_err("Discarded unknown event %x to gate %x\n", event, gate); break; } exit: kfree_skb(skb); + + return r; } static struct nfc_hci_ops pn544_hci_ops = { diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index e6224571e5e6..834e36481aff 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -57,8 +57,8 @@ struct nfc_hci_ops { int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb); int (*check_presence)(struct nfc_hci_dev *hdev, struct nfc_target *target); - void (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, - struct sk_buff *skb); + int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb); }; /* Pipes */ diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index b4b84268653d..f30f6fe815b4 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -323,16 +323,18 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, break; default: if (hdev->ops->event_received) { - hdev->ops->event_received(hdev, gate, event, skb); - return; + r = hdev->ops->event_received(hdev, gate, event, skb); + goto exit_noskb; + } else { + r = -EINVAL; } - break; } exit: kfree_skb(skb); +exit_noskb: if (r) { /* TODO: There was an error dispatching the event, * how to propagate up to nfc core? From 40d06d3647ea872a7346be1f6859f18cd0fe08d3 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 4 Dec 2012 16:43:24 +0100 Subject: [PATCH 303/478] NFC: Changed event_received hci ops result semantic Some chips use a standard HCI event code, destined to a proprietary gate, with a different meaning. Therefore, the HCI driver must always have a chance to intercept the event before standard processing is attempted. The new semantic specifies that the result value "1" means that the driver doesn't especially handle the event. result <= 0 means it was handled. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 16 +++++++++++----- net/nfc/hci/core.c | 14 ++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 4af70f9c01aa..9349c548e8dc 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -714,18 +714,23 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, return 0; } +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, struct sk_buff *skb) { struct sk_buff *rgb_skb = NULL; - int r = 0; + int r; pr_debug("hci event %d", event); switch (event) { case PN544_HCI_EVT_ACTIVATED: - if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) + if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) { r = nfc_hci_target_discovered(hdev, gate); - else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { + } else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, &rgb_skb); if (r < 0) @@ -736,6 +741,8 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, rgb_skb->len); kfree_skb(rgb_skb); + } else { + r = -EINVAL; } break; case PN544_HCI_EVT_DEACTIVATED: @@ -757,8 +764,7 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, skb_pull(skb, 2); return nfc_tm_data_received(hdev->ndev, skb); default: - pr_err("Discarded unknown event %x to gate %x\n", event, gate); - break; + return 1; } exit: diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index f30f6fe815b4..0430f3086e41 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -297,6 +297,12 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, goto exit; } + if (hdev->ops->event_received) { + r = hdev->ops->event_received(hdev, gate, event, skb); + if (r <= 0) + goto exit_noskb; + } + switch (event) { case NFC_HCI_EVT_TARGET_DISCOVERED: if (skb->len < 1) { /* no status data? */ @@ -322,12 +328,8 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, r = nfc_hci_target_discovered(hdev, gate); break; default: - if (hdev->ops->event_received) { - r = hdev->ops->event_received(hdev, gate, event, skb); - goto exit_noskb; - } else { - r = -EINVAL; - } + pr_info("Discarded unknown event %x to gate %x\n", event, gate); + r = -EINVAL; break; } From 924d4a023ee6da2e40c78578829e68bcbabee2dd Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 4 Dec 2012 16:44:25 +0100 Subject: [PATCH 304/478] NFC: Fixed skb leak in tm_send() nfc and hci ops implementations Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 10 ++++++++-- net/nfc/hci/core.c | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 9349c548e8dc..cd8fb16f5416 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -675,11 +675,17 @@ static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev, static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) { + int r; + /* Set default false for multiple information chaining */ *skb_push(skb, 1) = 0; - return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, - PN544_HCI_EVT_SND_DATA, skb->data, skb->len); + r = nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_HCI_EVT_SND_DATA, skb->data, skb->len); + + kfree_skb(skb); + + return r; } static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 0430f3086e41..d9190da4a403 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -675,8 +675,10 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) if (hdev->ops->tm_send) return hdev->ops->tm_send(hdev, skb); - else - return -ENOTSUPP; + + kfree_skb(skb); + + return -ENOTSUPP; } static int hci_check_presence(struct nfc_dev *nfc_dev, From bf71ab8ba53081c28b960d48e0c4cd1c17588aa6 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 18 Dec 2012 14:15:49 +0100 Subject: [PATCH 305/478] NFC: Add HCI quirks to support driver (non)standard implementations Some chips diverge from the HCI spec in their implementation of standard features. This adds a new quirks parameter to nfc_hci_allocate_device() to let the driver indicate its divergence. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 2 +- include/net/nfc/hci.h | 13 +++++++++++++ net/nfc/hci/command.c | 7 ++++++- net/nfc/hci/core.c | 3 +++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index cd8fb16f5416..ece834239852 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -833,7 +833,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_NFC_DEP_MASK; - info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0, protocols, llc_name, phy_headroom + PN544_CMDS_HEADROOM, phy_tailroom, phy_payload); diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 834e36481aff..2ff71750c428 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -82,6 +82,16 @@ typedef int (*xmit) (struct sk_buff *skb, void *cb_data); #define NFC_HCI_MAX_GATES 256 +/* + * These values can be specified by a driver to indicate it requires some + * adaptation of the HCI standard. + * + * NFC_HCI_QUIRK_SHORT_CLEAR - send HCI_ADM_CLEAR_ALL_PIPE cmd with no params + */ +enum { + NFC_HCI_QUIRK_SHORT_CLEAR = 0, +}; + struct nfc_hci_dev { struct nfc_dev *ndev; @@ -131,11 +141,14 @@ struct nfc_hci_dev { u8 *gb; size_t gb_len; + + unsigned long quirks; }; /* hci device allocation */ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, + unsigned long quirks, u32 protocols, const char *llc_name, int tx_headroom, diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 7d99410e6c1a..64f922be9281 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -280,14 +280,19 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe) static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev) { u8 param[2]; + size_t param_len = 2; /* TODO: Find out what the identity reference data is * and fill param with it. HCI spec 6.1.3.5 */ pr_debug("\n"); + if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks)) + param_len = 0; + return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, - NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL); + NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len, + NULL); } int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index d9190da4a403..755a6b9774ab 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -795,6 +795,7 @@ static struct nfc_ops hci_nfc_ops = { struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, + unsigned long quirks, u32 protocols, const char *llc_name, int tx_headroom, @@ -838,6 +839,8 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe)); + hdev->quirks = quirks; + return hdev; } EXPORT_SYMBOL(nfc_hci_allocate_device); From aa74103071312fede1f4953fe3c027941add1256 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 18 Dec 2012 16:26:23 +0100 Subject: [PATCH 306/478] NFC: pn544: Separate the core code and the i2c one into different modules As we may need to support other physical layers, we can avoid linking the core part into each and every pn544 module. Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 15 ++------------- drivers/nfc/Makefile | 2 +- drivers/nfc/pn544/Kconfig | 23 +++++++++++++++++++++++ drivers/nfc/pn544/Makefile | 5 +++-- drivers/nfc/pn544/pn544.c | 6 ++++++ 5 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 drivers/nfc/pn544/Kconfig diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index ec857676c39f..80c728b28828 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -5,19 +5,6 @@ menu "Near Field Communication (NFC) devices" depends on NFC -config PN544_HCI_NFC - tristate "HCI PN544 NFC driver" - depends on I2C && NFC_HCI && NFC_SHDLC - select CRC_CCITT - default n - ---help--- - NXP PN544 i2c driver. - This is a driver based on the SHDLC and HCI NFC kernel layers and - will thus not work with NXP libnfc library. - - To compile this driver as a module, choose m here. The module will - be called pn544_hci. - config NFC_PN533 tristate "NXP PN533 USB driver" depends on USB @@ -39,4 +26,6 @@ config NFC_WILINK Say Y here to compile support for Texas Instrument's NFC WiLink driver into the kernel or say M to compile it as module. +source "drivers/nfc/pn544/Kconfig" + endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 36c359043f54..574bbc04d97a 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -2,7 +2,7 @@ # Makefile for nfc devices # -obj-$(CONFIG_PN544_HCI_NFC) += pn544/ +obj-$(CONFIG_NFC_PN544) += pn544/ obj-$(CONFIG_NFC_PN533) += pn533.o obj-$(CONFIG_NFC_WILINK) += nfcwilink.o diff --git a/drivers/nfc/pn544/Kconfig b/drivers/nfc/pn544/Kconfig new file mode 100644 index 000000000000..c277790ac71c --- /dev/null +++ b/drivers/nfc/pn544/Kconfig @@ -0,0 +1,23 @@ +config NFC_PN544 + tristate "NXP PN544 NFC driver" + depends on NFC_HCI + select CRC_CCITT + default n + ---help--- + NXP PN544 core driver. + This is a driver based on the HCI NFC kernel layers and + will thus not work with NXP libnfc library. + + To compile this driver as a module, choose m here. The module will + be called pn544. + Say N if unsure. + +config NFC_PN544_I2C + tristate "NFC PN544 i2c support" + depends on NFC_PN544 && I2C && NFC_SHDLC + ---help--- + This module adds support for the NXP pn544 i2c interface. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called pn544_i2c. + Say N if unsure. \ No newline at end of file diff --git a/drivers/nfc/pn544/Makefile b/drivers/nfc/pn544/Makefile index 725733881eb3..ac076793687d 100644 --- a/drivers/nfc/pn544/Makefile +++ b/drivers/nfc/pn544/Makefile @@ -2,6 +2,7 @@ # Makefile for PN544 HCI based NFC driver # -obj-$(CONFIG_PN544_HCI_NFC) += pn544_i2c.o +pn544_i2c-objs = i2c.o -pn544_i2c-y := pn544.o i2c.o +obj-$(CONFIG_NFC_PN544) += pn544.o +obj-$(CONFIG_NFC_PN544_I2C) += pn544_i2c.o diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index ece834239852..d108c794008d 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -862,6 +863,7 @@ err_alloc_hdev: err_info_alloc: return r; } +EXPORT_SYMBOL(pn544_hci_probe); void pn544_hci_remove(struct nfc_hci_dev *hdev) { @@ -871,3 +873,7 @@ void pn544_hci_remove(struct nfc_hci_dev *hdev) nfc_hci_free_device(hdev); kfree(info); } +EXPORT_SYMBOL(pn544_hci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); From 234d4d6b1038f004f233f14906ec9328bf53088b Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 18 Dec 2012 16:40:16 +0100 Subject: [PATCH 307/478] NFC: pn544: Use module_i2c_driver The pn544 init routine does nothing but adding the driver to the i2c bus. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 7da9071b68b6..ec79f355edc1 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -472,29 +472,7 @@ static struct i2c_driver pn544_hci_i2c_driver = { .remove = __devexit_p(pn544_hci_i2c_remove), }; -static int __init pn544_hci_i2c_init(void) -{ - int r; - - pr_debug(DRIVER_DESC ": %s\n", __func__); - - r = i2c_add_driver(&pn544_hci_i2c_driver); - if (r) { - pr_err(PN544_HCI_I2C_DRIVER_NAME - ": driver registration failed\n"); - return r; - } - - return 0; -} - -static void __exit pn544_hci_i2c_exit(void) -{ - i2c_del_driver(&pn544_hci_i2c_driver); -} - -module_init(pn544_hci_i2c_init); -module_exit(pn544_hci_i2c_exit); +module_i2c_driver(pn544_hci_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(DRIVER_DESC); From a0f36536ac2ec0f7a9cfb53c72d6d8c378815fdb Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 18 Dec 2012 18:07:37 +0100 Subject: [PATCH 308/478] NFC: pn544: Use devm_kzalloc API Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index ec79f355edc1..7f96ca2c46bd 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -376,12 +376,12 @@ static int __devinit pn544_hci_i2c_probe(struct i2c_client *client, return -ENODEV; } - phy = kzalloc(sizeof(struct pn544_i2c_phy), GFP_KERNEL); + phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy), + GFP_KERNEL); if (!phy) { dev_err(&client->dev, "Cannot allocate memory for pn544 i2c phy.\n"); - r = -ENOMEM; - goto err_phy_alloc; + return -ENOMEM; } phy->i2c_dev = client; @@ -390,20 +390,18 @@ static int __devinit pn544_hci_i2c_probe(struct i2c_client *client, pdata = client->dev.platform_data; if (pdata == NULL) { dev_err(&client->dev, "No platform data\n"); - r = -EINVAL; - goto err_pdata; + return -EINVAL; } if (pdata->request_resources == NULL) { dev_err(&client->dev, "request_resources() missing\n"); - r = -EINVAL; - goto err_pdata; + return -EINVAL; } r = pdata->request_resources(client); if (r) { dev_err(&client->dev, "Cannot get platform resources\n"); - goto err_pdata; + return r; } phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); @@ -435,10 +433,6 @@ err_rti: if (pdata->free_resources != NULL) pdata->free_resources(); -err_pdata: - kfree(phy); - -err_phy_alloc: return r; } @@ -458,8 +452,6 @@ static __devexit int pn544_hci_i2c_remove(struct i2c_client *client) if (pdata->free_resources) pdata->free_resources(); - kfree(phy); - return 0; } From 2ad554a502facd705ce6eb362d0f1ac3ca426508 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 18 Dec 2012 17:35:02 +0100 Subject: [PATCH 309/478] NFC: update HCI documentation Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- Documentation/nfc/nfc-hci.txt | 129 +++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 26 deletions(-) diff --git a/Documentation/nfc/nfc-hci.txt b/Documentation/nfc/nfc-hci.txt index 89a339c9b079..0686c9e211c2 100644 --- a/Documentation/nfc/nfc-hci.txt +++ b/Documentation/nfc/nfc-hci.txt @@ -17,10 +17,12 @@ HCI HCI registers as an nfc device with NFC Core. Requests coming from userspace are routed through netlink sockets to NFC Core and then to HCI. From this point, they are translated in a sequence of HCI commands sent to the HCI layer in the -host controller (the chip). The sending context blocks while waiting for the -response to arrive. +host controller (the chip). Commands can be executed synchronously (the sending +context blocks waiting for response) or asynchronously (the response is returned +from HCI Rx context). HCI events can also be received from the host controller. They will be handled -and a translation will be forwarded to NFC Core as needed. +and a translation will be forwarded to NFC Core as needed. There are hooks to +let the HCI driver handle proprietary events or override standard behavior. HCI uses 2 execution contexts: - one for executing commands : nfc_hci_msg_tx_work(). Only one command can be executing at any given moment. @@ -33,6 +35,8 @@ The Session initialization is an HCI standard which must unfortunately support proprietary gates. This is the reason why the driver will pass a list of proprietary gates that must be part of the session. HCI will ensure all those gates have pipes connected when the hci device is set up. +In case the chip supports pre-opened gates and pseudo-static pipes, the driver +can pass that information to HCI core. HCI Gates and Pipes ------------------- @@ -46,6 +50,13 @@ without knowing the pipe connected to it. Driver interface ---------------- +A driver is generally written in two parts : the physical link management and +the HCI management. This makes it easier to maintain a driver for a chip that +can be connected using various phy (i2c, spi, ...) + +HCI Management +-------------- + A driver would normally register itself with HCI and provide the following entry points: @@ -53,58 +64,113 @@ struct nfc_hci_ops { int (*open)(struct nfc_hci_dev *hdev); void (*close)(struct nfc_hci_dev *hdev); int (*hci_ready) (struct nfc_hci_dev *hdev); - int (*xmit)(struct nfc_hci_dev *hdev, struct sk_buff *skb); - int (*start_poll)(struct nfc_hci_dev *hdev, u32 protocols); - int (*target_from_gate)(struct nfc_hci_dev *hdev, u8 gate, - struct nfc_target *target); + int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb); + int (*start_poll) (struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols); + int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target, + u8 comm_mode, u8 *gb, size_t gb_len); + int (*dep_link_down)(struct nfc_hci_dev *hdev); + int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate, + struct nfc_target *target); int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target); - int (*data_exchange) (struct nfc_hci_dev *hdev, - struct nfc_target *target, - struct sk_buff *skb, struct sk_buff **res_skb); + int (*im_transceive) (struct nfc_hci_dev *hdev, + struct nfc_target *target, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context); + int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb); int (*check_presence)(struct nfc_hci_dev *hdev, struct nfc_target *target); + int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb); }; - open() and close() shall turn the hardware on and off. - hci_ready() is an optional entry point that is called right after the hci session has been set up. The driver can use it to do additional initialization that must be performed using HCI commands. -- xmit() shall simply write a frame to the chip. +- xmit() shall simply write a frame to the physical link. - start_poll() is an optional entrypoint that shall set the hardware in polling mode. This must be implemented only if the hardware uses proprietary gates or a mechanism slightly different from the HCI standard. +- dep_link_up() is called after a p2p target has been detected, to finish +the p2p connection setup with hardware parameters that need to be passed back +to nfc core. +- dep_link_down() is called to bring the p2p link down. - target_from_gate() is an optional entrypoint to return the nfc protocols corresponding to a proprietary gate. - complete_target_discovered() is an optional entry point to let the driver perform additional proprietary processing necessary to auto activate the discovered target. -- data_exchange() must be implemented by the driver if proprietary HCI commands +- im_transceive() must be implemented by the driver if proprietary HCI commands are required to send data to the tag. Some tag types will require custom commands, others can be written to using the standard HCI commands. The driver can check the tag type and either do proprietary processing, or return 1 to ask -for standard processing. +for standard processing. The data exchange command itself must be sent +asynchronously. +- tm_send() is called to send data in the case of a p2p connection - check_presence() is an optional entry point that will be called regularly by the core to check that an activated tag is still in the field. If this is not implemented, the core will not be able to push tag_lost events to the user space +- event_received() is called to handle an event coming from the chip. Driver +can handle the event or return 1 to let HCI attempt standard processing. On the rx path, the driver is responsible to push incoming HCP frames to HCI using nfc_hci_recv_frame(). HCI will take care of re-aggregation and handling This must be done from a context that can sleep. -SHDLC ------ +PHY Management +-------------- -Most chips use shdlc to ensure integrity and delivery ordering of the HCP -frames between the host controller (the chip) and hosts (entities connected -to the chip, like the cpu). In order to simplify writing the driver, an shdlc -layer is available for use by the driver. -When used, the driver actually registers with shdlc, and shdlc will register -with HCI. HCI sees shdlc as the driver and thus send its HCP frames -through shdlc->xmit. -SHDLC adds a new execution context (nfc_shdlc_sm_work()) to run its state -machine and handle both its rx and tx path. +The physical link (i2c, ...) management is defined by the following struture: + +struct nfc_phy_ops { + int (*write)(void *dev_id, struct sk_buff *skb); + int (*enable)(void *dev_id); + void (*disable)(void *dev_id); +}; + +enable(): turn the phy on (power on), make it ready to transfer data +disable(): turn the phy off +write(): Send a data frame to the chip. Note that to enable higher +layers such as an llc to store the frame for re-emission, this function must +not alter the skb. It must also not return a positive result (return 0 for +success, negative for failure). + +Data coming from the chip shall be sent directly to nfc_hci_recv_frame(). + +LLC +--- + +Communication between the CPU and the chip often requires some link layer +protocol. Those are isolated as modules managed by the HCI layer. There are +currently two modules : nop (raw transfert) and shdlc. +A new llc must implement the following functions: + +struct nfc_llc_ops { + void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv, + rcv_to_hci_t rcv_to_hci, int tx_headroom, + int tx_tailroom, int *rx_headroom, int *rx_tailroom, + llc_failure_t llc_failure); + void (*deinit) (struct nfc_llc *llc); + int (*start) (struct nfc_llc *llc); + int (*stop) (struct nfc_llc *llc); + void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb); + int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb); +}; + +- init() : allocate and init your private storage +- deinit() : cleanup +- start() : establish the logical connection +- stop () : terminate the logical connection +- rcv_from_drv() : handle data coming from the chip, going to HCI +- xmit_from_hci() : handle data sent by HCI, going to the chip + +The llc must be registered with nfc before it can be used. Do that by +calling nfc_llc_register(const char *name, struct nfc_llc_ops *ops); + +Again, note that the llc does not handle the physical link. It is thus very +easy to mix any physical link with any llc for a given chip driver. Included Drivers ---------------- @@ -117,10 +183,12 @@ Execution Contexts The execution contexts are the following: - IRQ handler (IRQH): -fast, cannot sleep. stores incoming frames into an shdlc rx queue +fast, cannot sleep. sends incoming frames to HCI where they are passed to +the current llc. In case of shdlc, the frame is queued in shdlc rx queue. - SHDLC State Machine worker (SMW) -handles shdlc rx & tx queues. Dispatches HCI cmd responses. +Only when llc_shdlc is used: handles shdlc rx & tx queues. +Dispatches HCI cmd responses. - HCI Tx Cmd worker (MSGTXWQ) Serializes execution of HCI commands. Completes execution in case of response @@ -166,6 +234,15 @@ waiting command execution. Response processing involves invoking the completion callback that was provided by nfc_hci_msg_tx_work() when it sent the command. The completion callback will then wake the syscall context. +It is also possible to execute the command asynchronously using this API: + +static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, + const u8 *param, size_t param_len, + data_exchange_cb_t cb, void *cb_context) + +The workflow is the same, except that the API call returns immediately, and +the callback will be called with the result from the SMW context. + Workflow receiving an HCI event or command ------------------------------------------ From 390a1bd8538132186ddb679cafe9e75b7ef7e2d2 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 19 Dec 2012 19:11:32 +0100 Subject: [PATCH 310/478] NFC: Initial Secure Element API Each NFC adapter can have several links to different secure elements and that property needs to be exported by the drivers. A secure element link can be enabled and disabled, and card emulation will be handled by the currently active one. Otherwise card emulation will be host implemented. Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcwilink.c | 1 + drivers/nfc/pn533.c | 1 + drivers/nfc/pn544/pn544.c | 6 ++++-- include/net/nfc/hci.h | 3 +++ include/net/nfc/nci_core.h | 1 + include/net/nfc/nfc.h | 6 ++++++ include/uapi/linux/nfc.h | 14 ++++++++++++++ net/nfc/core.c | 3 +++ net/nfc/hci/core.c | 3 ++- net/nfc/nci/core.c | 2 ++ net/nfc/netlink.c | 1 + 11 files changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index c7c182d2b7df..3b731acbc408 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -542,6 +542,7 @@ static int nfcwilink_probe(struct platform_device *pdev) drv->ndev = nci_allocate_device(&nfcwilink_ops, protocols, + NFC_SE_NONE, NFCWILINK_HDR_LEN, 0); if (!drv->ndev) { diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index e8c083203b33..31a5b3b53b2a 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -2525,6 +2525,7 @@ static int pn533_probe(struct usb_interface *interface, dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols, + NFC_SE_NONE, dev->ops->tx_header_len + PN533_CMD_DATAEXCH_HEAD_LEN, dev->ops->tx_tail_len); diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index d108c794008d..9c5f16e7baef 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -801,7 +801,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, struct nfc_hci_dev **hdev) { struct pn544_hci_info *info; - u32 protocols; + u32 protocols, se; struct nfc_hci_init_data init_data; int r; @@ -834,8 +834,10 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_NFC_DEP_MASK; + se = NFC_SE_UICC | NFC_SE_EMBEDDED; + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0, - protocols, llc_name, + protocols, se, llc_name, phy_headroom + PN544_CMDS_HEADROOM, phy_tailroom, phy_payload); if (!info->hdev) { diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 2ff71750c428..b87a1692b086 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -59,6 +59,8 @@ struct nfc_hci_ops { struct nfc_target *target); int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, struct sk_buff *skb); + int (*enable_se)(struct nfc_dev *dev, u32 secure_element); + int (*disable_se)(struct nfc_dev *dev, u32 secure_element); }; /* Pipes */ @@ -150,6 +152,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, unsigned long quirks, u32 protocols, + u32 supported_se, const char *llc_name, int tx_headroom, int tx_tailroom, diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index d705d8674949..5bc0c460edc0 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -147,6 +147,7 @@ struct nci_dev { /* ----- NCI Devices ----- */ struct nci_dev *nci_allocate_device(struct nci_ops *ops, __u32 supported_protocols, + __u32 supported_se, int tx_headroom, int tx_tailroom); void nci_free_device(struct nci_dev *ndev); diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 1665674e86b2..87a6417fc934 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -68,6 +68,8 @@ struct nfc_ops { void *cb_context); int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb); int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target); + int (*enable_se)(struct nfc_dev *dev, u32 secure_element); + int (*disable_se)(struct nfc_dev *dev, u32 secure_element); }; #define NFC_TARGET_IDX_ANY -1 @@ -109,6 +111,9 @@ struct nfc_dev { struct nfc_genl_data genl_data; u32 supported_protocols; + u32 supported_se; + u32 active_se; + int tx_headroom; int tx_tailroom; @@ -125,6 +130,7 @@ extern struct class nfc_class; struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, u32 supported_protocols, + u32 supported_se, int tx_headroom, int tx_tailroom); diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 0e63cee8d810..80e4ecd8c04c 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -67,6 +67,11 @@ * subsequent CONNECT and CC messages. * If one of the passed parameters is wrong none is set and -EINVAL is * returned. + * @NFC_CMD_ENABLE_SE: Enable the physical link to a specific secure element. + * Once enabled a secure element will handle card emulation mode, i.e. + * starting a poll from a device which has a secure element enabled means + * we want to do SE based card emulation. + * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element. */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -86,6 +91,8 @@ enum nfc_commands { NFC_EVENT_TM_DEACTIVATED, NFC_CMD_LLC_GET_PARAMS, NFC_CMD_LLC_SET_PARAMS, + NFC_CMD_ENABLE_SE, + NFC_CMD_DISABLE_SE, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -114,6 +121,7 @@ enum nfc_commands { * @NFC_ATTR_LLC_PARAM_LTO: Link TimeOut parameter * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter + * @NFC_ATTR_SE: Available Secure Elements */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -134,6 +142,7 @@ enum nfc_attrs { NFC_ATTR_LLC_PARAM_LTO, NFC_ATTR_LLC_PARAM_RW, NFC_ATTR_LLC_PARAM_MIUX, + NFC_ATTR_SE, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; @@ -172,6 +181,11 @@ enum nfc_attrs { #define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP) #define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B) +/* NFC Secure Elements */ +#define NFC_SE_NONE 0x0 +#define NFC_SE_UICC 0x1 +#define NFC_SE_EMBEDDED 0x2 + struct sockaddr_nfc { sa_family_t sa_family; __u32 dev_idx; diff --git a/net/nfc/core.c b/net/nfc/core.c index 7d7b4ee34015..25522e56d350 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -757,6 +757,7 @@ struct nfc_dev *nfc_get_device(unsigned int idx) */ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, u32 supported_protocols, + u32 supported_se, int tx_headroom, int tx_tailroom) { struct nfc_dev *dev; @@ -774,6 +775,8 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, dev->ops = ops; dev->supported_protocols = supported_protocols; + dev->supported_se = supported_se; + dev->active_se = NFC_SE_NONE; dev->tx_headroom = tx_headroom; dev->tx_tailroom = tx_tailroom; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 755a6b9774ab..91020b210d87 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -797,6 +797,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, unsigned long quirks, u32 protocols, + u32 supported_se, const char *llc_name, int tx_headroom, int tx_tailroom, @@ -822,7 +823,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, return NULL; } - hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, + hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se, tx_headroom + HCI_CMDS_HEADROOM, tx_tailroom); if (!hdev->ndev) { diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 5f98dc1bf039..48ada0ec749e 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -658,6 +658,7 @@ static struct nfc_ops nci_nfc_ops = { */ struct nci_dev *nci_allocate_device(struct nci_ops *ops, __u32 supported_protocols, + __u32 supported_se, int tx_headroom, int tx_tailroom) { struct nci_dev *ndev; @@ -680,6 +681,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops, ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, supported_protocols, + supported_se, tx_headroom + NCI_DATA_HDR_SIZE, tx_tailroom); if (!ndev->nfc_dev) diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 3568ae16786d..504b883439f1 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -366,6 +366,7 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) || nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) || + nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) || nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) || nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode)) goto nla_put_failure; From 8a0ecfe74b3692258311d084d31613516828905f Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Fri, 21 Dec 2012 10:39:01 +0100 Subject: [PATCH 311/478] NFC: pn533: Fix missing parenthesis This is a quite critical patch as it fixes potential reference to undefined general_bytes which were never set correctly on target activation due to missing parenthesis. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 31a5b3b53b2a..f1702f945701 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1724,9 +1724,10 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev) rsp = (struct pn533_cmd_activate_response *)resp->data; rc = rsp->status & PN533_CMD_RET_MASK; - if (rc != PN533_CMD_RET_SUCCESS) + if (rc != PN533_CMD_RET_SUCCESS) { dev_kfree_skb(resp); return -EIO; + } /* ATR_RES general bytes are located at offset 16 */ gt_len = resp->len - 16; From 1353a10c474d1c6895051d33eefeb93542015008 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 10 Jan 2013 01:25:04 +0100 Subject: [PATCH 312/478] NFC: Update pn544 documentation The pn544 driver no longer has a /dev/pn544 interface nor a sysfs one. Reported-by: Marcel Holtmann Signed-off-by: Samuel Ortiz --- Documentation/nfc/nfc-pn544.txt | 84 +-------------------------------- 1 file changed, 1 insertion(+), 83 deletions(-) diff --git a/Documentation/nfc/nfc-pn544.txt b/Documentation/nfc/nfc-pn544.txt index 2fcac9f5996e..b36ca14ca2d6 100644 --- a/Documentation/nfc/nfc-pn544.txt +++ b/Documentation/nfc/nfc-pn544.txt @@ -1,32 +1,15 @@ Kernel driver for the NXP Semiconductors PN544 Near Field Communication chip -Author: Jari Vanhala -Contact: Matti Aaltonen (matti.j.aaltonen at nokia.com) - General ------- The PN544 is an integrated transmission module for contactless communication. The driver goes under drives/nfc/ and is compiled as a -module named "pn544". It registers a misc device and creates a device -file named "/dev/pn544". +module named "pn544". Host Interfaces: I2C, SPI and HSU, this driver supports currently only I2C. -The Interface -------------- - -The driver offers a sysfs interface for a hardware test and an IOCTL -interface for selecting between two operating modes. There are read, -write and poll functions for transferring messages. The two operating -modes are the normal (HCI) mode and the firmware update mode. - -PN544 is controlled by sending messages from the userspace to the -chip. The main function of the driver is just to pass those messages -without caring about the message content. - - Protocols --------- @@ -47,68 +30,3 @@ and third (LSB) bytes of the message. The maximum FW message length is For the ETSI HCI specification see http://www.etsi.org/WebSite/Technologies/ProtocolSpecification.aspx - -The Hardware Test ------------------ - -The idea of the test is that it can performed by reading from the -corresponding sysfs file. The test is implemented in the board file -and it should test that PN544 can be put into the firmware update -mode. If the test is not implemented the sysfs file does not get -created. - -Example: -> cat /sys/module/pn544/drivers/i2c\:pn544/3-002b/nfc_test -1 - -Normal Operation ----------------- - -PN544 is powered up when the device file is opened, otherwise it's -turned off. Only one instance can use the device at a time. - -Userspace applications control PN544 with HCI messages. The hardware -sends an interrupt when data is available for reading. Data is -physically read when the read function is called by a userspace -application. Poll() checks the read interrupt state. Configuration and -self testing are also done from the userspace using read and write. - -Example platform data: - -static int rx71_pn544_nfc_request_resources(struct i2c_client *client) -{ - /* Get and setup the HW resources for the device */ -} - -static void rx71_pn544_nfc_free_resources(void) -{ - /* Release the HW resources */ -} - -static void rx71_pn544_nfc_enable(int fw) -{ - /* Turn the device on */ -} - -static int rx71_pn544_nfc_test(void) -{ - /* - * Put the device into the FW update mode - * and then back to the normal mode. - * Check the behavior and return one on success, - * zero on failure. - */ -} - -static void rx71_pn544_nfc_disable(void) -{ - /* turn the power off */ -} - -static struct pn544_nfc_platform_data rx71_nfc_data = { - .request_resources = rx71_pn544_nfc_request_resources, - .free_resources = rx71_pn544_nfc_free_resources, - .enable = rx71_pn544_nfc_enable, - .test = rx71_pn544_nfc_test, - .disable = rx71_pn544_nfc_disable, -}; From f4d6f7dce71a5da93da50272ff1670bf2f1146b1 Mon Sep 17 00:00:00 2001 From: Sergio Cambra Date: Thu, 10 Jan 2013 01:06:55 +0100 Subject: [PATCH 313/478] Bluetooth device 04ca:3008 should use ath3k Output of /sys/kernel/debug/usb/devices T: Bus=03 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 6 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=04ca ProdID=3008 Rev= 0.02 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=1ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Sergio Cambra Signed-off-by: Gustavo Padovan --- drivers/bluetooth/ath3k.c | 2 ++ drivers/bluetooth/btusb.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index be178949bc8b..33c9a44a9678 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -78,6 +78,7 @@ static struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x13d3, 0x3375) }, { USB_DEVICE(0x04CA, 0x3005) }, { USB_DEVICE(0x04CA, 0x3006) }, + { USB_DEVICE(0x04CA, 0x3008) }, { USB_DEVICE(0x13d3, 0x3362) }, { USB_DEVICE(0x0CF3, 0xE004) }, { USB_DEVICE(0x0930, 0x0219) }, @@ -109,6 +110,7 @@ static struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3f6a993539e8..7e351e345476 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -136,6 +136,7 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, From 7b064edae38d62d8587a8c574f93b53ce75ae749 Mon Sep 17 00:00:00 2001 From: Jaganath Kanakkassery Date: Thu, 10 Jan 2013 10:28:35 +0530 Subject: [PATCH 314/478] Bluetooth: Fix authentication if acl data comes before remote feature evt If remote device sends l2cap info request before read_remote_ext_feature completes then mgmt_connected will be sent in hci_acldata_packet() and remote name request wont be sent and eventually authentication wont happen Hcidump log of the issue < HCI Command: Create Connection (0x01|0x0005) plen 13 bdaddr BC:85:1F:74:7F:29 ptype 0xcc18 rswitch 0x01 clkoffset 0x4bf7 (valid) Packet type: DM1 DM3 DM5 DH1 DH3 DH5 > HCI Event: Command Status (0x0f) plen 4 Create Connection (0x01|0x0005) status 0x00 ncmd 1 > HCI Event: Connect Complete (0x03) plen 11 status 0x00 handle 12 bdaddr BC:85:1F:74:7F:29 type ACL encrypt 0x00 < HCI Command: Read Remote Supported Features (0x01|0x001b) plen 2 handle 12 > HCI Event: Command Status (0x0f) plen 4 Read Remote Supported Features (0x01|0x001b) status 0x00 ncmd 1 > HCI Event: Read Remote Supported Features (0x0b) plen 11 status 0x00 handle 12 Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87 > HCI Event: Max Slots Change (0x1b) plen 3 handle 12 slots 5 < HCI Command: Read Remote Extended Features (0x01|0x001c) plen 3 handle 12 page 1 > HCI Event: Command Status (0x0f) plen 4 Read Remote Extended Features (0x01|0x001c) status 0x00 ncmd 1 > ACL data: handle 12 flags 0x02 dlen 10 L2CAP(s): Info req: type 2 < ACL data: handle 12 flags 0x00 dlen 16 L2CAP(s): Info rsp: type 2 result 0 Extended feature mask 0x00b8 Enhanced Retransmission mode Streaming mode FCS Option Fixed Channels > HCI Event: Read Remote Extended Features (0x23) plen 13 status 0x00 handle 12 page 1 max 1 Features: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 > ACL data: handle 12 flags 0x02 dlen 10 L2CAP(s): Info req: type 3 < ACL data: handle 12 flags 0x00 dlen 20 L2CAP(s): Info rsp: type 3 result 0 Fixed channel list 0x00000002 L2CAP Signalling Channel > HCI Event: Number of Completed Packets (0x13) plen 5 handle 12 packets 2 This patch moves sending mgmt_connected from hci_acldata_packet() to l2cap_connect_req() since this code is to handle the scenario remote device sends l2cap connect req too fast Signed-off-by: Jaganath Kanakkassery Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 8 -------- net/bluetooth/l2cap_core.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 596660d37c5e..0f78e34220c9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2810,14 +2810,6 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) if (conn) { hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF); - hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags) && - !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); - hci_dev_unlock(hdev); - /* Send to upper protocol */ l2cap_recv_acldata(conn, skb, flags); return; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2c78208d793e..22e658322845 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3727,6 +3727,17 @@ sendresp: static int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { + struct hci_dev *hdev = conn->hcon->hdev; + struct hci_conn *hcon = conn->hcon; + + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->dev_flags) && + !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags)) + mgmt_device_connected(hdev, &hcon->dst, hcon->type, + hcon->dst_type, 0, NULL, 0, + hcon->dev_class); + hci_dev_unlock(hdev); + l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0); return 0; } From b7e98b5100aad9290d7f06fcb9d1e80f7f62f05f Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Thu, 3 Jan 2013 19:59:28 -0200 Subject: [PATCH 315/478] Bluetooth: Check if the hci connection exists in SCO shutdown Checking only for sco_conn seems to not be enough and lead to NULL dereferences in the code, check for hcon instead. <1>[11340.226404] BUG: unable to handle kernel NULL pointer dereference at 0000000 8 <4>[11340.226619] EIP is at __sco_sock_close+0xe8/0x1a0 <4>[11340.226629] EAX: f063a740 EBX: 00000000 ECX: f58f4544 EDX: 00000000 <4>[11340.226640] ESI: dec83e00 EDI: 5f9a081f EBP: e0fdff38 ESP: e0fdff1c <0>[11340.226674] Stack: <4>[11340.226682] c184db87 c1251028 dec83e00 e0fdff38 c1754aef dec83e00 00000000 e0fdff5c <4>[11340.226718] c184f587 e0fdff64 e0fdff68 5f9a081f e0fdff5c c1751852 d7813800 62262f10 <4>[11340.226752] e0fdff70 c1753c00 00000000 00000001 0000000d e0fdffac c175425c 00000041 <0>[11340.226793] Call Trace: <4>[11340.226813] [] ? sco_sock_clear_timer+0x27/0x60 <4>[11340.226831] [] ? local_bh_enable+0x68/0xd0 <4>[11340.226846] [] ? lock_sock_nested+0x4f/0x60 <4>[11340.226862] [] sco_sock_shutdown+0x67/0xb0 <4>[11340.226879] [] ? sockfd_lookup_light+0x22/0x80 <4>[11340.226897] [] sys_shutdown+0x30/0x60 <4>[11340.226912] [] sys_socketcall+0x1dc/0x2a0 <4>[11340.226929] [] ? trace_hardirqs_on_thunk+0xc/0x10 <4>[11340.226944] [] syscall_call+0x7/0xb <4>[11340.226960] [] ? restore_cur+0x5e/0xd7 <0>[11340.226969] Code: ff 4b 08 0f 94 c0 84 c0 74 20 80 7b 19 01 74 2f b8 0a 00 00 Reported-by: Chuansheng Liu Signed-off-by: Gustavo Padovan --- net/bluetooth/sco.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 531a93d613d4..57f250c20e39 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -352,7 +352,7 @@ static void __sco_sock_close(struct sock *sk) case BT_CONNECTED: case BT_CONFIG: - if (sco_pi(sk)->conn) { + if (sco_pi(sk)->conn->hcon) { sk->sk_state = BT_DISCONN; sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT); hci_conn_put(sco_pi(sk)->conn->hcon); From e384662b1c1004e6b1e9d2c4979945a383c07b7d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:33 +0200 Subject: [PATCH 316/478] Bluetooth: Fix missing command complete event for mgmt_confirm_name All management commands are expected to indicate successful completion through a command complete event however the confirm name command was missing it. This patch add the sending of the missing event. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e5502a56365b..577f316d87ad 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2493,7 +2493,8 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data, hci_inquiry_cache_update_resolve(hdev, e); } - err = 0; + err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 0, &cp->addr, + sizeof(cp->addr)); failed: hci_dev_unlock(hdev); From 715a5bf2db4df4a7df64f420d21fb49ba146b3fa Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:34 +0200 Subject: [PATCH 317/478] Bluetooth: Fix missing command complete for mgmt_load_long_term_keys All management events are expected to indicate successful completion through a command complete event, however the load long term keys command was missing this. This patch adds the missing event. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 577f316d87ad..1dd41d48eb66 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2648,7 +2648,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, { struct mgmt_cp_load_long_term_keys *cp = cp_data; u16 key_count, expected_len; - int i; + int i, err; key_count = __le16_to_cpu(cp->key_count); @@ -2682,9 +2682,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, key->enc_size, key->ediv, key->rand); } + err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0, + NULL, 0); + hci_dev_unlock(hdev); - return 0; + return err; } static const struct mgmt_handler { From 575b3a02e20a10bb8110378ef363a8a174018680 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:35 +0200 Subject: [PATCH 318/478] Bluetooth: Fix checking for valid device class values The two lowest bits of the minor device class value are reserved and should be zero, and the three highest bits of the major device class likewise. The management code should therefore test for this and return a proper "invalid params" error if the condition is not met. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1dd41d48eb66..f3fec4264dcf 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1430,6 +1430,12 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } + if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + hdev->major_class = cp->major; hdev->minor_class = cp->minor; From ee98f4738050bb93823ce9ba849f5d78f5b8c1a1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:36 +0200 Subject: [PATCH 319/478] Bluetooth: Fix accepting set_dev_class for non-BR/EDR controllers The concept of Class of Device only exists for BR/EDR controllers. The mgmt_set_dev_class command should therefore return a proper "not supported" error if it is attempted for a controller that doesn't support BR/EDR (e.g. a single mode LE-only one). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f3fec4264dcf..d78ce81d2cf4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1424,6 +1424,12 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); + if (!lmp_bredr_capable(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_NOT_SUPPORTED); + goto unlock; + } + if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, MGMT_STATUS_BUSY); From 13ecd8b6628c14c9a27832ce7c48315385272208 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:38 +0200 Subject: [PATCH 320/478] Bluetooth: Move non-critical sections outside of the dev lock This patch fixes sections of code that do not need hci_lock_dev to be outside of the lock. Such sections include code that do not touch the hdev at all as well as sections which just read a single byte from the supported_features value (i.e. all lmp_*_capable() macros). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 50 ++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d78ce81d2cf4..28e01f992231 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1133,13 +1133,11 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) BT_DBG("request for %s", hdev->name); - hci_dev_lock(hdev); + if (!lmp_ssp_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, + MGMT_STATUS_NOT_SUPPORTED); - if (!lmp_ssp_capable(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, - MGMT_STATUS_NOT_SUPPORTED); - goto failed; - } + hci_dev_lock(hdev); val = !!cp->val; @@ -1217,13 +1215,11 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) BT_DBG("request for %s", hdev->name); - hci_dev_lock(hdev); + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_LE, + MGMT_STATUS_NOT_SUPPORTED); - if (!lmp_le_capable(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, - MGMT_STATUS_NOT_SUPPORTED); - goto unlock; - } + hci_dev_lock(hdev); val = !!cp->val; enabled = lmp_host_le_capable(hdev); @@ -1422,26 +1418,20 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_NOT_SUPPORTED); + + if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_BUSY); + + if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); - if (!lmp_bredr_capable(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_NOT_SUPPORTED); - goto unlock; - } - - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_BUSY); - goto unlock; - } - - if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } - hdev->major_class = cp->major; hdev->minor_class = cp->minor; From a7e80f25ae2296d78163d75d753c796270464000 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 16:05:19 +0200 Subject: [PATCH 321/478] Bluetooth: Fix checking for exact values of boolean mgmt parameters All mgmt_set_* commands that take a boolean value encoded in the form of a byte should only accept the values 0x00 and 0x01. This patch adds the necessary checks for this and returns "invalid params" responses if anything else is provided as the value. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 28e01f992231..3959c471b2b4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -777,6 +777,10 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { @@ -872,6 +876,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_INVALID_PARAMS); + timeout = __le16_to_cpu(cp->timeout); if (!cp->val && timeout > 0) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, @@ -971,6 +979,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -1041,6 +1053,10 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_PAIRABLE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (cp->val) @@ -1073,6 +1089,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -1137,6 +1157,10 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); val = !!cp->val; @@ -1197,6 +1221,10 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, + MGMT_STATUS_INVALID_PARAMS); + if (cp->val) set_bit(HCI_HS_ENABLED, &hdev->dev_flags); else @@ -1219,6 +1247,10 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_LE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); val = !!cp->val; @@ -2598,6 +2630,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, + MGMT_STATUS_INVALID_PARAMS); + if (!hdev_is_powered(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_POWERED); From 04106755763f558886a631338d12546345bae6e9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jan 2013 14:54:09 +0200 Subject: [PATCH 322/478] Bluetooth: Fix returning proper command status for start_discovery Management commands should whenever possible fail with proper command status or command complete events. This patch fixes the mgmt_start_discovery command to do this for the failure cases where an incorrect parameter value was passed to it ("not supported" if the parameter value was valid but the controller doesn't support it and "invalid params" if it isn't valid at all). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 46 +++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3959c471b2b4..b1b0a36e1e80 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2382,31 +2382,45 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, switch (hdev->discovery.type) { case DISCOV_TYPE_BREDR: - if (lmp_bredr_capable(hdev)) - err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); - else - err = -ENOTSUPP; + if (!lmp_bredr_capable(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_SUPPORTED); + mgmt_pending_remove(cmd); + goto failed; + } + + err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); break; case DISCOV_TYPE_LE: - if (lmp_host_le_capable(hdev)) - err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); - else - err = -ENOTSUPP; + if (!lmp_host_le_capable(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_SUPPORTED); + mgmt_pending_remove(cmd); + goto failed; + } + + err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, + LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); break; case DISCOV_TYPE_INTERLEAVED: - if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev)) - err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, - LE_SCAN_TIMEOUT_BREDR_LE); - else - err = -ENOTSUPP; + if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_SUPPORTED); + mgmt_pending_remove(cmd); + goto failed; + } + + err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, LE_SCAN_WIN, + LE_SCAN_TIMEOUT_BREDR_LE); break; default: - err = -EINVAL; + err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS); + mgmt_pending_remove(cmd); + goto failed; } if (err < 0) From a1d704509d5b96756d3d4cfb7f10a555efeadb87 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:40 +0200 Subject: [PATCH 323/478] Bluetooth: Fix sending incorrect new_settings for mgmt_set_powered The socket from which a mgmt_set_powered command was received should only receive the command response but no new_settings event. The mgmt_powered() function which is used to handle the situation with the HCI_AUTO_OFF flag tries to check for a pending command to know which socket to skip the event for, but since the pending command hasn't been added this will not happen. This patch fixes the issue by adding the pending command for the HCI_AUTO_OFF case and thereby ensures that mgmt_powered() will skip the right socket when sending the new_settings event, but still send the proper response to the socket where the command came from. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b1b0a36e1e80..37add53ce613 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -787,8 +787,9 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, cancel_delayed_work(&hdev->power_off); if (cp->val) { - err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); - mgmt_powered(hdev, 1); + mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, + data, len); + err = mgmt_powered(hdev, 1); goto failed; } } From 52e0b011e29f36bc5c02ea3adbf4d864a38373de Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Thu, 10 Jan 2013 06:06:29 -0200 Subject: [PATCH 324/478] Bluetooth: Fix uuid output in debugfs The uuid should be printed in the CPU endianness and not in little-endian. Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_sysfs.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 55cceee02a84..23b4e242a31a 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -461,19 +462,18 @@ static const struct file_operations blacklist_fops = { static void print_bt_uuid(struct seq_file *f, u8 *uuid) { - __be32 data0, data4; - __be16 data1, data2, data3, data5; + u32 data0, data5; + u16 data1, data2, data3, data4; - memcpy(&data0, &uuid[0], 4); - memcpy(&data1, &uuid[4], 2); - memcpy(&data2, &uuid[6], 2); - memcpy(&data3, &uuid[8], 2); - memcpy(&data4, &uuid[10], 4); - memcpy(&data5, &uuid[14], 2); + data5 = get_unaligned_le32(uuid); + data4 = get_unaligned_le16(uuid + 4); + data3 = get_unaligned_le16(uuid + 6); + data2 = get_unaligned_le16(uuid + 8); + data1 = get_unaligned_le16(uuid + 10); + data0 = get_unaligned_le32(uuid + 12); - seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n", - ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3), - ntohl(data4), ntohs(data5)); + seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n", + data0, data1, data2, data3, data4, data5); } static int uuids_show(struct seq_file *f, void *p) From 75e6934a9e996ee7cfe7a488b7f7cc2baa86717a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 11 Jan 2013 12:32:37 +0100 Subject: [PATCH 325/478] mac80211: fix HT40 connections My commit 4bf88530be971bf95a7830ca61b4120980bf4347 ("mac80211: convert to channel definition struct") accidentally broke HT40 connections due to swapped channel flag checks -- fix that. Reported-by: Sujith Manoharan Tested-by: Sujith Manoharan Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d90c07b1795f..e930175771ff 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -199,11 +199,11 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, case NL80211_CHAN_WIDTH_40: if (sdata->vif.bss_conf.chandef.chan->center_freq > sdata->vif.bss_conf.chandef.center_freq1 && - chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + chan->flags & IEEE80211_CHAN_NO_HT40MINUS) disable_40 = true; if (sdata->vif.bss_conf.chandef.chan->center_freq < sdata->vif.bss_conf.chandef.center_freq1 && - chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + chan->flags & IEEE80211_CHAN_NO_HT40PLUS) disable_40 = true; break; default: From 55b183ad8635589ea731a31653de79a2626491c4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 11 Jan 2013 14:22:58 +0100 Subject: [PATCH 326/478] wireless: fix regulatory HT40 allowed check commit 1a9193185f462a51815fe48491f8a6fb6b942551 "regulatory: code cleanup" changed is_ht40_allowed without considering that IEEE80211_CHAN_NO_HT40 is not just one flag, but two. This is causing HT40- to be blocked completely. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 8c114e8a9135..6ea626b30a2a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1123,7 +1123,9 @@ static bool is_ht40_allowed(struct ieee80211_channel *chan) if (chan->flags & IEEE80211_CHAN_DISABLED) return false; /* This would happen when regulatory rules disallow HT40 completely */ - return !(chan->flags & IEEE80211_CHAN_NO_HT40); + if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40) + return false; + return true; } static void reg_process_ht_flags_channel(struct wiphy *wiphy, From fad2e371bb5a9f64dced90309fa7cb8bce09e92f Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 11 Jan 2013 14:48:48 +0100 Subject: [PATCH 327/478] NFC: Avoid memcpy on LLCP connection less Rx path We can cast msg_name to a sockaddr_nfc_llcp pointer directly. Signed-off-by: Samuel Ortiz --- net/nfc/llcp/sock.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index 12ec4879b4ba..463231bed8d4 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -682,17 +682,17 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, if (sk->sk_type == SOCK_DGRAM && msg->msg_name) { struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb); - struct sockaddr_nfc_llcp sockaddr; + struct sockaddr_nfc_llcp *sockaddr = + (struct sockaddr_nfc_llcp *) msg->msg_name; + + msg->msg_namelen = sizeof(struct sockaddr_nfc_llcp); pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap); - sockaddr.sa_family = AF_NFC; - sockaddr.nfc_protocol = NFC_PROTO_NFC_DEP; - sockaddr.dsap = ui_cb->dsap; - sockaddr.ssap = ui_cb->ssap; - - memcpy(msg->msg_name, &sockaddr, sizeof(sockaddr)); - msg->msg_namelen = sizeof(sockaddr); + sockaddr->sa_family = AF_NFC; + sockaddr->nfc_protocol = NFC_PROTO_NFC_DEP; + sockaddr->dsap = ui_cb->dsap; + sockaddr->ssap = ui_cb->ssap; } /* Mark read part of skb as used */ From fb97c3e80f99988079df4fb5f3c84aea4e7f806a Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 11 Jan 2013 14:54:22 +0100 Subject: [PATCH 328/478] NFC: Use skb_copy_datagram_iovec Safer and more robust than than memcpy_toiovec. Signed-off-by: Samuel Ortiz --- net/nfc/llcp/sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index 463231bed8d4..5332751943a9 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -672,7 +672,7 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, copied = min_t(unsigned int, rlen, len); cskb = skb; - if (memcpy_toiovec(msg->msg_iov, cskb->data, copied)) { + if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) { if (!(flags & MSG_PEEK)) skb_queue_head(&sk->sk_receive_queue, skb); return -EFAULT; From d7b9c5204e9c6810a20d509ee47bc70419096e59 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 8 Jan 2013 17:53:10 -0800 Subject: [PATCH 329/478] mwifiex: update config_bands during infra association Currently "adapter->config_bands" is updated during infra association only if channel is provided by user in "iw connect" command. config_bands is used while preparing association request to calculate supported rates by intersecting our rates with the rates advertised by AP. There is corner case in which we include zero rates in supported rates TLV based on previous IBSS network history, which leads to association failure. This patch fixes the problem by correctly updating config_bands. Cc: "3.7.y" Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 15 +-------------- drivers/net/wireless/mwifiex/sta_ioctl.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index efe525be27dd..e74e1b99419a 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1459,7 +1459,7 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, struct cfg80211_ssid req_ssid; int ret, auth_type = 0; struct cfg80211_bss *bss = NULL; - u8 is_scanning_required = 0, config_bands = 0; + u8 is_scanning_required = 0; memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); @@ -1478,19 +1478,6 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, /* disconnect before try to associate */ mwifiex_deauthenticate(priv, NULL); - if (channel) { - if (mode == NL80211_IFTYPE_STATION) { - if (channel->band == IEEE80211_BAND_2GHZ) - config_bands = BAND_B | BAND_G | BAND_GN; - else - config_bands = BAND_A | BAND_AN; - - if (!((config_bands | priv->adapter->fw_bands) & - ~priv->adapter->fw_bands)) - priv->adapter->config_bands = config_bands; - } - } - /* As this is new association, clear locally stored * keys and security related flags */ priv->sec_info.wpa_enabled = false; diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 60e88b58039d..f542bb8ccbc8 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -283,6 +283,20 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (ret) goto done; + if (bss_desc) { + u8 config_bands = 0; + + if (mwifiex_band_to_radio_type((u8) bss_desc->bss_band) + == HostCmd_SCAN_RADIO_TYPE_BG) + config_bands = BAND_B | BAND_G | BAND_GN; + else + config_bands = BAND_A | BAND_AN; + + if (!((config_bands | adapter->fw_bands) & + ~adapter->fw_bands)) + adapter->config_bands = config_bands; + } + ret = mwifiex_check_network_compatibility(priv, bss_desc); if (ret) goto done; From 3b86acb8088d0c7c4cddc340a13dec5cef110e30 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 8 Jan 2013 17:53:11 -0800 Subject: [PATCH 330/478] mwifiex: correct config_bands handling for ibss network BAND_G is implicit when BAND_GN is present. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e74e1b99419a..cdb11b3964e2 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1694,7 +1694,7 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, if (cfg80211_get_chandef_type(¶ms->chandef) != NL80211_CHAN_NO_HT) - config_bands |= BAND_GN; + config_bands |= BAND_G | BAND_GN; } else { if (cfg80211_get_chandef_type(¶ms->chandef) == NL80211_CHAN_NO_HT) From 0981c3b24ef664f5611008a6e6d0622fac6d892b Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 9 Jan 2013 16:07:48 +0530 Subject: [PATCH 331/478] ath9k_htc: Fix memory leak SKBs that are allocated in the HTC layer do not have callbacks registered and hence ended up not being freed, Fix this by freeing them properly in the TX completion routine. Cc: Reported-by: Larry Finger Signed-off-by: Sujith Manoharan Tested-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_hst.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index 4a9570dfba72..aac4a406a513 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -344,6 +344,8 @@ void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv, skb, htc_hdr->endpoint_id, txok); + } else { + kfree_skb(skb); } } From a3dc48e82bb146ef11cf75676c8410c1df29b0c4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:52 +0100 Subject: [PATCH 332/478] ath9k: do not link receive buffers during flush On AR9300 the rx FIFO needs to be empty during reset to ensure that no further DMA activity is generated, otherwise it might lead to memory corruption issues. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d4df98a938bf..01c3ba47a5b9 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -744,6 +744,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; } + list_del(&bf->list); if (!bf->bf_mpdu) return bf; @@ -1254,14 +1255,15 @@ requeue_drop_frag: sc->rx.frag = NULL; } requeue: + list_add_tail(&bf->list, &sc->rx.rxbuf); + if (flush) + continue; + if (edma) { - list_add_tail(&bf->list, &sc->rx.rxbuf); ath_rx_edma_buf_link(sc, qtype); } else { - list_move_tail(&bf->list, &sc->rx.rxbuf); ath_rx_buf_link(sc, bf); - if (!flush) - ath9k_hw_rxena(ah); + ath9k_hw_rxena(ah); } } while (1); From 1adb2e2b5f85023d17eb4f95386a57029df27c88 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:53 +0100 Subject: [PATCH 333/478] ath9k: fix double-free bug on beacon generate failure When the next beacon is sent, the ath_buf from the previous run is reused. If getting a new beacon from mac80211 fails, bf->bf_mpdu is not reset, yet the skb is freed, leading to a double-free on the next beacon tx attempt, resulting in a system crash. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 531fffd801a3..4eb269d6b974 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -147,6 +147,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_buf_addr = 0; + bf->bf_mpdu = NULL; } skb = ieee80211_beacon_get(hw, vif); From 3adcf20afb585993ffee24de36d1975f6b26b120 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:54 +0100 Subject: [PATCH 334/478] ath9k: remove the WARN_ON that triggers if generating a beacon fails During teardown, mac80211 will not return a new beacon. This is normal and handled properly in the driver, so there's no need to spam the user with a kernel warning here. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 4eb269d6b974..2ca355e94da6 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -360,7 +360,6 @@ void ath9k_beacon_tasklet(unsigned long data) return; bf = ath9k_beacon_generate(sc->hw, vif); - WARN_ON(!bf); if (sc->beacon.bmisscnt != 0) { ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n", From 7fc00a3054b70b1794c2d64db703eb467ad0365c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:55 +0100 Subject: [PATCH 335/478] ath9k: add a better fix for the rx tasklet vs rx flush race Ensure that the rx tasklet is no longer running when entering the reset path. Also remove the distinction between flush and no-flush frame processing. If a frame has been received and ACKed by the hardware, the stack needs to see it, so that the BA receive window does not go out of sync. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/debug.c | 1 - drivers/net/wireless/ath/ath9k/debug.h | 2 -- drivers/net/wireless/ath/ath9k/main.c | 4 ++++ drivers/net/wireless/ath/ath9k/recv.c | 15 --------------- 5 files changed, 4 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 86e26a19efda..7335e9ef0249 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -646,7 +646,6 @@ void ath_ant_comb_update(struct ath_softc *sc); enum sc_op_flags { SC_OP_INVALID, SC_OP_BEACONS, - SC_OP_RXFLUSH, SC_OP_ANI_RUN, SC_OP_PRIM_STA_VIF, SC_OP_HW_RESET, diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 13ff9edc2401..e585fc827c50 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -861,7 +861,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, RXS_ERR("RX-LENGTH-ERR", rx_len_err); RXS_ERR("RX-OOM-ERR", rx_oom_err); RXS_ERR("RX-RATE-ERR", rx_rate_err); - RXS_ERR("RX-DROP-RXFLUSH", rx_drop_rxflush); RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err); PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 375c3b46411e..6df2ab62dcb7 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -216,7 +216,6 @@ struct ath_tx_stats { * @rx_oom_err: No. of frames dropped due to OOM issues. * @rx_rate_err: No. of frames dropped due to rate errors. * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. - * @rx_drop_rxflush: No. of frames dropped due to RX-FLUSH. * @rx_beacons: No. of beacons received. * @rx_frags: No. of rx-fragements received. */ @@ -235,7 +234,6 @@ struct ath_rx_stats { u32 rx_oom_err; u32 rx_rate_err; u32 rx_too_many_frags_err; - u32 rx_drop_rxflush; u32 rx_beacons; u32 rx_frags; }; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index be30a9af1528..d3cf01ec2d15 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -196,6 +196,8 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); + tasklet_disable(&sc->intr_tq); + if (!ath_stoprecv(sc)) ret = false; @@ -210,6 +212,8 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) ath_flushrecv(sc); } + tasklet_enable(&sc->intr_tq); + return ret; } diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 01c3ba47a5b9..6b83b3bbfe8c 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -286,7 +286,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) spin_lock_init(&sc->sc_pcu_lock); spin_lock_init(&sc->rx.rxbuflock); - clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 + sc->sc_ah->caps.rx_status_len; @@ -501,11 +500,9 @@ bool ath_stoprecv(struct ath_softc *sc) void ath_flushrecv(struct ath_softc *sc) { - set_bit(SC_OP_RXFLUSH, &sc->sc_flags); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_rx_tasklet(sc, 1, true); ath_rx_tasklet(sc, 1, false); - clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); } static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) @@ -1067,9 +1064,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) do { bool decrypt_error = false; - /* If handling rx interrupt and flush is in progress => exit */ - if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0)) - break; memset(&rs, 0, sizeof(rs)); if (edma) @@ -1112,15 +1106,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ath_debug_stat_rx(sc, &rs); - /* - * If we're asked to flush receive queue, directly - * chain it back at the queue without processing it. - */ - if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) { - RX_STAT_INC(rx_drop_rxflush); - goto requeue_drop_frag; - } - memset(rxs, 0, sizeof(struct ieee80211_rx_status)); rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp; From 4b883f021b9ccf2df3d14425e6e610281fb6a35e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Jan 2013 16:16:56 +0100 Subject: [PATCH 336/478] ath9k: fix rx flush handling Right now the rx flush is not doing anything useful on AR9003+, as it only works if the buffers in the rx FIFO have not been purged yet, as is done by ath_stoprecv. To fix this, always call ath_flushrecv from within ath_stoprecv before the FIFO is emptied, but still after the hw receive path has been stopped. This ensures that frames received (and ACKed by the hardware) shortly before a reset will be seen by the software, which should improve A-MPDU session stability. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/main.c | 16 +++------------- drivers/net/wireless/ath/ath9k/recv.c | 16 +++++++++------- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 7335e9ef0249..32b2d5cd9a5a 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -328,7 +328,6 @@ struct ath_rx { int ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); -void ath_flushrecv(struct ath_softc *sc); u32 ath_calcrxfilter(struct ath_softc *sc); int ath_rx_init(struct ath_softc *sc, int nbufs); void ath_rx_cleanup(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d3cf01ec2d15..9844b758f81d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -182,7 +182,7 @@ static void ath_restart_work(struct ath_softc *sc) ath_start_ani(sc); } -static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) +static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; bool ret = true; @@ -204,14 +204,6 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) if (!ath_drain_all_txq(sc, retry_tx)) ret = false; - if (!flush) { - if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) - ath_rx_tasklet(sc, 1, true); - ath_rx_tasklet(sc, 1, false); - } else { - ath_flushrecv(sc); - } - tasklet_enable(&sc->intr_tq); return ret; @@ -266,7 +258,6 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = NULL; bool fastcc = true; - bool flush = false; int r; __ath_cancel_work(sc); @@ -280,11 +271,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, if (!hchan) { fastcc = false; - flush = true; hchan = ah->curchan; } - if (!ath_prepare_reset(sc, retry_tx, flush)) + if (!ath_prepare_reset(sc, retry_tx)) fastcc = false; ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", @@ -808,7 +798,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_hw_cfg_gpio_input(ah, ah->led_pin); } - ath_prepare_reset(sc, false, true); + ath_prepare_reset(sc, false); if (sc->rx.frag) { dev_kfree_skb_any(sc->rx.frag); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 6b83b3bbfe8c..67f58d4bb10e 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -472,6 +472,13 @@ start_recv: return 0; } +static void ath_flushrecv(struct ath_softc *sc) +{ + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) + ath_rx_tasklet(sc, 1, true); + ath_rx_tasklet(sc, 1, false); +} + bool ath_stoprecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; @@ -482,6 +489,8 @@ bool ath_stoprecv(struct ath_softc *sc) ath9k_hw_setrxfilter(ah, 0); stopped = ath9k_hw_stopdmarecv(ah, &reset); + ath_flushrecv(sc); + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_edma_stop_recv(sc); else @@ -498,13 +507,6 @@ bool ath_stoprecv(struct ath_softc *sc) return stopped && !reset; } -void ath_flushrecv(struct ath_softc *sc) -{ - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) - ath_rx_tasklet(sc, 1, true); - ath_rx_tasklet(sc, 1, false); -} - static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) { /* Check whether the Beacon frame has DTIM indicating buffered bc/mc */ From a1fe52801a992e590cdaee2fb47a94bac9b5da90 Mon Sep 17 00:00:00 2001 From: Piotr Haber Date: Thu, 10 Jan 2013 11:20:48 +0100 Subject: [PATCH 337/478] brcmsmac: increase timer reference count for new timers only On hardware reintialization reference count of already existing timers would be increased again. This leads to problems on module unloading. Cc: stable@vger.kernel.org Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Reviewed-by: Arend van Spriel Signed-off-by: Piotr Haber Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 1fbd8ecbe2ea..0f71d1d4339d 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -1407,9 +1407,10 @@ void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic) #endif t->ms = ms; t->periodic = (bool) periodic; - t->set = true; - - atomic_inc(&t->wl->callbacks); + if (!t->set) { + t->set = true; + atomic_inc(&t->wl->callbacks); + } ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms)); } From 29b7d9ad9112a95757c8030421dc3bd1b28ad8b5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 6 Dec 2012 23:10:40 +0100 Subject: [PATCH 338/478] drivers/nfc/nfcwilink.c: use devm_kzalloc devm_kzalloc allocates memory that is released when a driver detaches. This patch uses devm_kzalloc for data that is allocated in the probe function of a platform device and is only freed in the remove function. Signed-off-by: Julia Lawall Signed-off-by: John W. Linville --- drivers/nfc/nfcwilink.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index 50b1ee41afc6..c7c182d2b7df 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -526,7 +526,7 @@ static int nfcwilink_probe(struct platform_device *pdev) nfc_dev_dbg(&pdev->dev, "probe entry"); - drv = kzalloc(sizeof(struct nfcwilink), GFP_KERNEL); + drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL); if (!drv) { rc = -ENOMEM; goto exit; @@ -547,7 +547,7 @@ static int nfcwilink_probe(struct platform_device *pdev) if (!drv->ndev) { nfc_dev_err(&pdev->dev, "nci_allocate_device failed"); rc = -ENOMEM; - goto free_exit; + goto exit; } nci_set_parent_dev(drv->ndev, &pdev->dev); @@ -566,9 +566,6 @@ static int nfcwilink_probe(struct platform_device *pdev) free_dev_exit: nci_free_device(drv->ndev); -free_exit: - kfree(drv); - exit: return rc; } @@ -588,8 +585,6 @@ static int nfcwilink_remove(struct platform_device *pdev) nci_unregister_device(ndev); nci_free_device(ndev); - kfree(drv); - dev_set_drvdata(&pdev->dev, NULL); return 0; From b66851c3c89629e72a2eaa54d1b838fb74dad644 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 7 Jan 2013 14:48:07 -0800 Subject: [PATCH 339/478] mac80211_hwsim: fix beacon timestamp and mactime Set the beacon timestamp once during "transmission" so the monitor interface also gets a timestamped beacon. Also use a common base between TX timestamp and RX mactime. This eliminates "TX" path delay, which shows up as a constant error in Toffset. Get the global TSF once before iterating over all RX HWs, so they all set a mactime with the same time base. Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- drivers/net/wireless/mac80211_hwsim.c | 57 ++++++++++++++++++--------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index b6e2caa024a7..b73e497fe770 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -363,6 +363,8 @@ struct mac80211_hwsim_data { /* difference between this hw's clock and the real clock, in usecs */ s64 tsf_offset; s64 bcn_delta; + /* absolute beacon transmission time. Used to cover up "tx" delay. */ + u64 abs_bcn_ts; }; @@ -410,15 +412,19 @@ static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } +static inline u64 mac80211_hwsim_get_tsf_raw(void) +{ + return ktime_to_us(ktime_get_real()); +} + static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) { - struct timeval tv = ktime_to_timeval(ktime_get_real()); - u64 now = tv.tv_sec * USEC_PER_SEC + tv.tv_usec; + u64 now = mac80211_hwsim_get_tsf_raw(); return cpu_to_le64(now + data->tsf_offset); } static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif) { struct mac80211_hwsim_data *data = hw->priv; return le64_to_cpu(__mac80211_hwsim_get_tsf(data)); @@ -705,7 +711,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_rx_status rx_status; - struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); + u64 now; memset(&rx_status, 0, sizeof(rx_status)); rx_status.flag |= RX_FLAG_MACTIME_START; @@ -731,11 +737,23 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, secpath_reset(skb); nf_reset(skb); + /* + * Get absolute mactime here so all HWs RX at the "same time", and + * absolute TX time for beacon mactime so the timestamp matches. + * Giving beacons a different mactime than non-beacons looks messy, but + * it helps the Toffset be exact and a ~10us mactime discrepancy + * probably doesn't really matter. + */ + if (ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) + now = data->abs_bcn_ts; + else + now = mac80211_hwsim_get_tsf_raw(); + /* Copy skb to all enabled radios that are on the current frequency */ spin_lock(&hwsim_radio_lock); list_for_each_entry(data2, &hwsim_radios, list) { struct sk_buff *nskb; - struct ieee80211_mgmt *mgmt; struct tx_iter_data tx_iter_data = { .receive = false, .channel = chan, @@ -787,17 +805,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (mac80211_hwsim_addr_match(data2, hdr->addr1)) ack = true; - /* set bcn timestamp relative to receiver mactime */ - rx_status.mactime = - le64_to_cpu(__mac80211_hwsim_get_tsf(data2)); - mgmt = (struct ieee80211_mgmt *) nskb->data; - if (ieee80211_is_beacon(mgmt->frame_control) || - ieee80211_is_probe_resp(mgmt->frame_control)) - mgmt->u.beacon.timestamp = cpu_to_le64( - rx_status.mactime + - (data->tsf_offset - data2->tsf_offset) + - 24 * 8 * 10 / txrate->bitrate); - + rx_status.mactime = now + data2->tsf_offset; #if 0 /* * Don't enable this code by default as the OUI 00:00:00 @@ -987,7 +995,11 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, struct ieee80211_vif *vif) { - struct ieee80211_hw *hw = arg; + struct mac80211_hwsim_data *data = arg; + struct ieee80211_hw *hw = data->hw; + struct ieee80211_tx_info *info; + struct ieee80211_rate *txrate; + struct ieee80211_mgmt *mgmt; struct sk_buff *skb; hwsim_check_magic(vif); @@ -1000,6 +1012,15 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, skb = ieee80211_beacon_get(hw, vif); if (skb == NULL) return; + info = IEEE80211_SKB_CB(skb); + txrate = ieee80211_get_tx_rate(hw, info); + + mgmt = (struct ieee80211_mgmt *) skb->data; + /* fake header transmission time */ + data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw(); + mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts + + data->tsf_offset + + 24 * 8 * 10 / txrate->bitrate); mac80211_hwsim_tx_frame(hw, skb, rcu_dereference(vif->chanctx_conf)->def.chan); @@ -1020,7 +1041,7 @@ mac80211_hwsim_beacon(struct hrtimer *timer) ieee80211_iterate_active_interfaces_atomic( hw, IEEE80211_IFACE_ITER_NORMAL, - mac80211_hwsim_beacon_tx, hw); + mac80211_hwsim_beacon_tx, data); /* beacon at new TBTT + beacon interval */ if (data->bcn_delta) { From 590152789121640bfccb2d155458819951771363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 10 Jan 2013 10:11:37 +0100 Subject: [PATCH 340/478] ssb: add database of serial flash memories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville --- drivers/ssb/driver_chipcommon_sflash.c | 122 +++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/drivers/ssb/driver_chipcommon_sflash.c b/drivers/ssb/driver_chipcommon_sflash.c index 866269774e86..720665ca2bb1 100644 --- a/drivers/ssb/driver_chipcommon_sflash.c +++ b/drivers/ssb/driver_chipcommon_sflash.c @@ -9,9 +9,131 @@ #include "ssb_private.h" +struct ssb_sflash_tbl_e { + char *name; + u32 id; + u32 blocksize; + u16 numblocks; +}; + +static struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = { + { "M25P20", 0x11, 0x10000, 4, }, + { "M25P40", 0x12, 0x10000, 8, }, + + { "M25P16", 0x14, 0x10000, 32, }, + { "M25P32", 0x15, 0x10000, 64, }, + { "M25P64", 0x16, 0x10000, 128, }, + { "M25FL128", 0x17, 0x10000, 256, }, + { 0 }, +}; + +static struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = { + { "SST25WF512", 1, 0x1000, 16, }, + { "SST25VF512", 0x48, 0x1000, 16, }, + { "SST25WF010", 2, 0x1000, 32, }, + { "SST25VF010", 0x49, 0x1000, 32, }, + { "SST25WF020", 3, 0x1000, 64, }, + { "SST25VF020", 0x43, 0x1000, 64, }, + { "SST25WF040", 4, 0x1000, 128, }, + { "SST25VF040", 0x44, 0x1000, 128, }, + { "SST25VF040B", 0x8d, 0x1000, 128, }, + { "SST25WF080", 5, 0x1000, 256, }, + { "SST25VF080B", 0x8e, 0x1000, 256, }, + { "SST25VF016", 0x41, 0x1000, 512, }, + { "SST25VF032", 0x4a, 0x1000, 1024, }, + { "SST25VF064", 0x4b, 0x1000, 2048, }, + { 0 }, +}; + +static struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = { + { "AT45DB011", 0xc, 256, 512, }, + { "AT45DB021", 0x14, 256, 1024, }, + { "AT45DB041", 0x1c, 256, 2048, }, + { "AT45DB081", 0x24, 256, 4096, }, + { "AT45DB161", 0x2c, 512, 4096, }, + { "AT45DB321", 0x34, 512, 8192, }, + { "AT45DB642", 0x3c, 1024, 8192, }, + { 0 }, +}; + +static void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode) +{ + int i; + chipco_write32(cc, SSB_CHIPCO_FLASHCTL, + SSB_CHIPCO_FLASHCTL_START | opcode); + for (i = 0; i < 1000; i++) { + if (!(chipco_read32(cc, SSB_CHIPCO_FLASHCTL) & + SSB_CHIPCO_FLASHCTL_BUSY)) + return; + cpu_relax(); + } + pr_err("SFLASH control command failed (timeout)!\n"); +} + /* Initialize serial flash access */ int ssb_sflash_init(struct ssb_chipcommon *cc) { + struct ssb_sflash_tbl_e *e; + u32 id, id2; + + switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) { + case SSB_CHIPCO_FLASHT_STSER: + ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_DP); + + chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 0); + ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES); + id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA); + + chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 1); + ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES); + id2 = chipco_read32(cc, SSB_CHIPCO_FLASHDATA); + + switch (id) { + case 0xbf: + for (e = ssb_sflash_sst_tbl; e->name; e++) { + if (e->id == id2) + break; + } + break; + case 0x13: + return -ENOTSUPP; + default: + for (e = ssb_sflash_st_tbl; e->name; e++) { + if (e->id == id) + break; + } + break; + } + if (!e->name) { + pr_err("Unsupported ST serial flash (id: 0x%X, id2: 0x%X)\n", + id, id2); + return -ENOTSUPP; + } + + break; + case SSB_CHIPCO_FLASHT_ATSER: + ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS); + id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA) & 0x3c; + + for (e = ssb_sflash_at_tbl; e->name; e++) { + if (e->id == id) + break; + } + if (!e->name) { + pr_err("Unsupported Atmel serial flash (id: 0x%X)\n", + id); + return -ENOTSUPP; + } + + break; + default: + pr_err("Unsupported flash type\n"); + return -ENOTSUPP; + } + + pr_info("Found %s serial flash (blocksize: 0x%X, blocks: %d)\n", + e->name, e->blocksize, e->numblocks); + pr_err("Serial flash support is not implemented yet!\n"); return -ENOTSUPP; From aad12ede97d62b290ac03f0077d05b999bbddd05 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Thu, 10 Jan 2013 11:45:14 +0100 Subject: [PATCH 341/478] ath9k: fix spectral scan endless mode on AR9002 There was a copy+paste error in ar9002 for the endless spectral mode, fix that. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 51b7c8130ae4..2cb665e2ea22 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -581,7 +581,7 @@ void ar9002_hw_spectral_scan_config(struct ath_hw *ah, */ count = param->count; if (param->endless) - count = 0; + count = 0x80; else if (count & 0x80) count = 0x7f; From 1a2d50329832c4b5c77be569f20eec303a0d07b8 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 10 Jan 2013 17:52:38 +0100 Subject: [PATCH 342/478] bcma: fix compile error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This error was introduced in: commit e3f05a42faac627d8704c76c1927e09b22443b7b Author: Hauke Mehrtens Date: Fri Jan 4 00:51:21 2013 +0100 bcma: mips: explicit assign IRQ numbers CC drivers/bcma/driver_mips.o drivers/bcma/driver_mips.c: In function 'bcma_core_mips_init': drivers/bcma/driver_mips.c:302:4: error: implicit declaration of function 'bcma_core_irq' [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors make[6]: *** [drivers/bcma/driver_mips.o] Error 1 Reported-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_mips.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 8a51c7950536..90f20a247725 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -299,7 +299,7 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore) break; default: list_for_each_entry(core, &bus->cores, list) { - core->irq = bcma_core_irq(core); + core->irq = bcma_core_mips_irq(core) + 2; } bcma_err(bus, "Unknown device (0x%x) found, can not configure IRQs\n", From e2aa19fadd718d7dd920a3994118863861a4b61e Mon Sep 17 00:00:00 2001 From: Nathan Hintz Date: Thu, 10 Jan 2013 17:54:09 +0100 Subject: [PATCH 343/478] bcma: return the mips irq number in bcma_core_irq The irq signal numbers that are send by the cpu are increased by 2 from the number programmed into the mips core by bcma. Return the irq number on which the irqs are send in bcma_core_irq() now. Signed-off-by: Nathan Hintz Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- arch/mips/bcm47xx/serial.c | 2 +- drivers/bcma/driver_chipcommon.c | 2 +- drivers/bcma/driver_mips.c | 12 +++++++++--- drivers/bcma/driver_pci_host.c | 4 ++-- include/linux/bcma/bcma_driver_mips.h | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/arch/mips/bcm47xx/serial.c b/arch/mips/bcm47xx/serial.c index 57981e4fe2bc..b8ef965705cf 100644 --- a/arch/mips/bcm47xx/serial.c +++ b/arch/mips/bcm47xx/serial.c @@ -62,7 +62,7 @@ static int __init uart8250_init_bcma(void) p->mapbase = (unsigned int) bcma_port->regs; p->membase = (void *) bcma_port->regs; - p->irq = bcma_port->irq + 2; + p->irq = bcma_port->irq; p->uartclk = bcma_port->baud_base; p->regshift = bcma_port->reg_shift; p->iotype = UPIO_MEM; diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c index e461ad25fda4..28fa50ad87be 100644 --- a/drivers/bcma/driver_chipcommon.c +++ b/drivers/bcma/driver_chipcommon.c @@ -329,7 +329,7 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc) return; } - irq = bcma_core_mips_irq(cc->core); + irq = bcma_core_irq(cc->core); /* Determine the registers of the UARTs */ cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART); diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 90f20a247725..a808404db4b1 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -85,7 +85,7 @@ static u32 bcma_core_mips_irqflag(struct bcma_device *dev) * If disabled, 5 is returned. * If not supported, 6 is returned. */ -unsigned int bcma_core_mips_irq(struct bcma_device *dev) +static unsigned int bcma_core_mips_irq(struct bcma_device *dev) { struct bcma_device *mdev = dev->bus->drv_mips.core; u32 irqflag; @@ -102,7 +102,13 @@ unsigned int bcma_core_mips_irq(struct bcma_device *dev) return 5; } -EXPORT_SYMBOL(bcma_core_mips_irq); + +unsigned int bcma_core_irq(struct bcma_device *dev) +{ + unsigned int mips_irq = bcma_core_mips_irq(dev); + return mips_irq <= 4 ? mips_irq + 2 : 0; +} +EXPORT_SYMBOL(bcma_core_irq); static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) { @@ -299,7 +305,7 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore) break; default: list_for_each_entry(core, &bus->cores, list) { - core->irq = bcma_core_mips_irq(core) + 2; + core->irq = bcma_core_irq(core); } bcma_err(bus, "Unknown device (0x%x) found, can not configure IRQs\n", diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index e6b5c89469dc..ef9f0938da77 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -577,7 +577,7 @@ int bcma_core_pci_plat_dev_init(struct pci_dev *dev) pr_info("PCI: Fixing up device %s\n", pci_name(dev)); /* Fix up interrupt lines */ - dev->irq = bcma_core_mips_irq(pc_host->pdev->core) + 2; + dev->irq = bcma_core_irq(pc_host->pdev->core); pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); return 0; @@ -596,6 +596,6 @@ int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev) pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host, pci_ops); - return bcma_core_mips_irq(pc_host->pdev->core) + 2; + return bcma_core_irq(pc_host->pdev->core); } EXPORT_SYMBOL(bcma_core_pci_pcibios_map_irq); diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h index 6495579e3f35..73c7f4b882cc 100644 --- a/include/linux/bcma/bcma_driver_mips.h +++ b/include/linux/bcma/bcma_driver_mips.h @@ -48,6 +48,6 @@ static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { } extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore); -extern unsigned int bcma_core_mips_irq(struct bcma_device *dev); +extern unsigned int bcma_core_irq(struct bcma_device *core); #endif /* LINUX_BCMA_DRIVER_MIPS_H_ */ From eab6d7921d5704128c941d7cc647dacd4d50697c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 10 Jan 2013 19:41:52 +0100 Subject: [PATCH 344/478] ath9k_hw: add tx gain tables for newer devices Improves stability on affected devices and also fixes the Tx IQ calibration related regression on some AR9340 devices such as the TP-Link TL-WDR4300. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- .../wireless/ath/ath9k/ar9003_2p2_initvals.h | 180 ++++++++++++++++++ drivers/net/wireless/ath/ath9k/ar9003_hw.c | 67 +++++-- .../net/wireless/ath/ath9k/ar9340_initvals.h | 100 ++++++++++ .../net/wireless/ath/ath9k/ar9485_initvals.h | 146 ++++++++++++++ .../wireless/ath/ath9k/ar9580_1p0_initvals.h | 76 ++++++++ drivers/net/wireless/ath/ath9k/reg.h | 4 + 6 files changed, 555 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h index 262e1e036fd7..db5ffada2217 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h @@ -744,6 +744,186 @@ static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; +static const u32 ar9300Modes_mixed_ob_db_tx_gain_table_2p2[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x1c000223, 0x1c000223, 0x11000400, 0x11000400}, + {0x0000a518, 0x21002220, 0x21002220, 0x15000402, 0x15000402}, + {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, + {0x0000a520, 0x2b022220, 0x2b022220, 0x1b000603, 0x1b000603}, + {0x0000a524, 0x2f022222, 0x2f022222, 0x1f000a02, 0x1f000a02}, + {0x0000a528, 0x34022225, 0x34022225, 0x23000a04, 0x23000a04}, + {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x26000a20, 0x26000a20}, + {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2a000e20, 0x2a000e20}, + {0x0000a534, 0x4202242a, 0x4202242a, 0x2e000e22, 0x2e000e22}, + {0x0000a538, 0x4702244a, 0x4702244a, 0x31000e24, 0x31000e24}, + {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x34001640, 0x34001640}, + {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38001660, 0x38001660}, + {0x0000a544, 0x52022470, 0x52022470, 0x3b001861, 0x3b001861}, + {0x0000a548, 0x55022490, 0x55022490, 0x3e001a81, 0x3e001a81}, + {0x0000a54c, 0x59022492, 0x59022492, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x5d022692, 0x5d022692, 0x44001c84, 0x44001c84}, + {0x0000a554, 0x61022892, 0x61022892, 0x48001ce3, 0x48001ce3}, + {0x0000a558, 0x65024890, 0x65024890, 0x4c001ce5, 0x4c001ce5}, + {0x0000a55c, 0x69024892, 0x69024892, 0x50001ce9, 0x50001ce9}, + {0x0000a560, 0x6e024c92, 0x6e024c92, 0x54001ceb, 0x54001ceb}, + {0x0000a564, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec}, + {0x0000a568, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec}, + {0x0000a56c, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec}, + {0x0000a570, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec}, + {0x0000a574, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec}, + {0x0000a578, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec}, + {0x0000a57c, 0x74026e92, 0x74026e92, 0x56001eec, 0x56001eec}, + {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, + {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, + {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, + {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, + {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, + {0x0000a594, 0x1c800223, 0x1c800223, 0x11800400, 0x11800400}, + {0x0000a598, 0x21802220, 0x21802220, 0x15800402, 0x15800402}, + {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404}, + {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1b800603, 0x1b800603}, + {0x0000a5a4, 0x2f822222, 0x2f822222, 0x1f800a02, 0x1f800a02}, + {0x0000a5a8, 0x34822225, 0x34822225, 0x23800a04, 0x23800a04}, + {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x26800a20, 0x26800a20}, + {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2a800e20, 0x2a800e20}, + {0x0000a5b4, 0x4282242a, 0x4282242a, 0x2e800e22, 0x2e800e22}, + {0x0000a5b8, 0x4782244a, 0x4782244a, 0x31800e24, 0x31800e24}, + {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x34801640, 0x34801640}, + {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x38801660, 0x38801660}, + {0x0000a5c4, 0x52822470, 0x52822470, 0x3b801861, 0x3b801861}, + {0x0000a5c8, 0x55822490, 0x55822490, 0x3e801a81, 0x3e801a81}, + {0x0000a5cc, 0x59822492, 0x59822492, 0x42801a83, 0x42801a83}, + {0x0000a5d0, 0x5d822692, 0x5d822692, 0x44801c84, 0x44801c84}, + {0x0000a5d4, 0x61822892, 0x61822892, 0x48801ce3, 0x48801ce3}, + {0x0000a5d8, 0x65824890, 0x65824890, 0x4c801ce5, 0x4c801ce5}, + {0x0000a5dc, 0x69824892, 0x69824892, 0x50801ce9, 0x50801ce9}, + {0x0000a5e0, 0x6e824c92, 0x6e824c92, 0x54801ceb, 0x54801ceb}, + {0x0000a5e4, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec}, + {0x0000a5e8, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec}, + {0x0000a5ec, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec}, + {0x0000a5f0, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec}, + {0x0000a5f4, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec}, + {0x0000a5f8, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec}, + {0x0000a5fc, 0x74826e92, 0x74826e92, 0x56801eec, 0x56801eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a614, 0x02004000, 0x02004000, 0x01404000, 0x01404000}, + {0x0000a618, 0x02004801, 0x02004801, 0x01404501, 0x01404501}, + {0x0000a61c, 0x02808a02, 0x02808a02, 0x02008501, 0x02008501}, + {0x0000a620, 0x0380ce03, 0x0380ce03, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04411104, 0x04411104, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04411104, 0x04411104, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, + {0x0000a630, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, + {0x0000a634, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, + {0x0000a638, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04411104, 0x04411104, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000c2dc, 0x00033800, 0x00033800, 0x03aaa352, 0x03aaa352}, + {0x0000c2e0, 0x0003c000, 0x0003c000, 0x03ccc584, 0x03ccc584}, + {0x0000c2e4, 0x03fc0000, 0x03fc0000, 0x03f0f800, 0x03f0f800}, + {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4}, + {0x00016048, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001}, + {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016444, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4}, + {0x00016448, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001}, + {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016844, 0x012492d4, 0x012492d4, 0x056db2e4, 0x056db2e4}, + {0x00016848, 0x66480001, 0x66480001, 0x8e480001, 0x8e480001}, + {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, +}; + +static const u32 ar9300Modes_type5_tx_gain_table_2p2[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400}, + {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402}, + {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404}, + {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603}, + {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02}, + {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04}, + {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20}, + {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20}, + {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22}, + {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24}, + {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640}, + {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660}, + {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861}, + {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81}, + {0x0000a54c, 0x5e08442e, 0x5e08442e, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x620a4431, 0x620a4431, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000}, + {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501}, + {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501}, + {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016048, 0x65240001, 0x65240001, 0x66480001, 0x66480001}, + {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016448, 0x65240001, 0x65240001, 0x66480001, 0x66480001}, + {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016848, 0x65240001, 0x65240001, 0x66480001, 0x66480001}, + {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, +}; + static const u32 ar9300Common_rx_gain_table_2p2[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 74fd3977feeb..de8a56c92e1a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -507,28 +507,59 @@ static void ar9003_tx_gain_table_mode4(struct ath_hw *ah) else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_mixed_ob_db_tx_gain_table); + else + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9300Modes_mixed_ob_db_tx_gain_table_2p2); } +static void ar9003_tx_gain_table_mode5(struct ath_hw *ah) +{ + if (AR_SREV_9485_11(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9485Modes_green_ob_db_tx_gain_1_1); + else if (AR_SREV_9340(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9340Modes_ub124_tx_gain_table_1p0); + else if (AR_SREV_9580(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9580_1p0_type5_tx_gain_table); + else if (AR_SREV_9300_22(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9300Modes_type5_tx_gain_table_2p2); +} + +static void ar9003_tx_gain_table_mode6(struct ath_hw *ah) +{ + if (AR_SREV_9340(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9340Modes_low_ob_db_and_spur_tx_gain_table_1p0); + else if (AR_SREV_9485_11(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9485Modes_green_spur_ob_db_tx_gain_1_1); + else if (AR_SREV_9580(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9580_1p0_type6_tx_gain_table); +} + +typedef void (*ath_txgain_tab)(struct ath_hw *ah); + static void ar9003_tx_gain_table_apply(struct ath_hw *ah) { - switch (ar9003_hw_get_tx_gain_idx(ah)) { - case 0: - default: - ar9003_tx_gain_table_mode0(ah); - break; - case 1: - ar9003_tx_gain_table_mode1(ah); - break; - case 2: - ar9003_tx_gain_table_mode2(ah); - break; - case 3: - ar9003_tx_gain_table_mode3(ah); - break; - case 4: - ar9003_tx_gain_table_mode4(ah); - break; - } + static const ath_txgain_tab modes[] = { + ar9003_tx_gain_table_mode0, + ar9003_tx_gain_table_mode1, + ar9003_tx_gain_table_mode2, + ar9003_tx_gain_table_mode3, + ar9003_tx_gain_table_mode4, + ar9003_tx_gain_table_mode5, + ar9003_tx_gain_table_mode6, + }; + int idx = ar9003_hw_get_tx_gain_idx(ah); + + if (idx >= ARRAY_SIZE(modes)) + idx = 0; + + modes[idx](ah); } static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h index f69d292bdc02..25db9215985a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h @@ -1172,6 +1172,106 @@ static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925666, 0x24925666, 0x8e481266, 0x8e481266}, }; +static const u32 ar9340Modes_low_ob_db_and_spur_tx_gain_table_1p0[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03eaac5a, 0x03eaac5a}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03f330ac, 0x03f330ac}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03fc3f00, 0x03fc3f00}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ffc000, 0x03ffc000}, + {0x0000a394, 0x00000444, 0x00000444, 0x00000404, 0x00000404}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x02000001, 0x02000001}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x05000003, 0x05000003}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0a000005, 0x0a000005}, + {0x0000a510, 0x16000220, 0x16000220, 0x0e000201, 0x0e000201}, + {0x0000a514, 0x1c000223, 0x1c000223, 0x11000203, 0x11000203}, + {0x0000a518, 0x21002220, 0x21002220, 0x14000401, 0x14000401}, + {0x0000a51c, 0x27002223, 0x27002223, 0x18000403, 0x18000403}, + {0x0000a520, 0x2b022220, 0x2b022220, 0x1b000602, 0x1b000602}, + {0x0000a524, 0x2f022222, 0x2f022222, 0x1f000802, 0x1f000802}, + {0x0000a528, 0x34022225, 0x34022225, 0x21000620, 0x21000620}, + {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x25000820, 0x25000820}, + {0x0000a530, 0x3e02222c, 0x3e02222c, 0x29000822, 0x29000822}, + {0x0000a534, 0x4202242a, 0x4202242a, 0x2d000824, 0x2d000824}, + {0x0000a538, 0x4702244a, 0x4702244a, 0x30000828, 0x30000828}, + {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x3400082a, 0x3400082a}, + {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38000849, 0x38000849}, + {0x0000a544, 0x5302266c, 0x5302266c, 0x3b000a2c, 0x3b000a2c}, + {0x0000a548, 0x5702286c, 0x5702286c, 0x3e000e2b, 0x3e000e2b}, + {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x42000e2d, 0x42000e2d}, + {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4500124a, 0x4500124a}, + {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4900124c, 0x4900124c}, + {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x4c00126c, 0x4c00126c}, + {0x0000a55c, 0x7002708c, 0x7002708c, 0x4f00128c, 0x4f00128c}, + {0x0000a560, 0x7302b08a, 0x7302b08a, 0x52001290, 0x52001290}, + {0x0000a564, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292}, + {0x0000a568, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292}, + {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292}, + {0x0000a570, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292}, + {0x0000a574, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292}, + {0x0000a578, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292}, + {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x56001292, 0x56001292}, + {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, + {0x0000a584, 0x06800003, 0x06800003, 0x02800001, 0x02800001}, + {0x0000a588, 0x0a800020, 0x0a800020, 0x05800003, 0x05800003}, + {0x0000a58c, 0x10800023, 0x10800023, 0x0a800005, 0x0a800005}, + {0x0000a590, 0x16800220, 0x16800220, 0x0e800201, 0x0e800201}, + {0x0000a594, 0x1c800223, 0x1c800223, 0x11800203, 0x11800203}, + {0x0000a598, 0x21820220, 0x21820220, 0x14800401, 0x14800401}, + {0x0000a59c, 0x27820223, 0x27820223, 0x18800403, 0x18800403}, + {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1b800602, 0x1b800602}, + {0x0000a5a4, 0x2f822222, 0x2f822222, 0x1f800802, 0x1f800802}, + {0x0000a5a8, 0x34822225, 0x34822225, 0x21800620, 0x21800620}, + {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x25800820, 0x25800820}, + {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x29800822, 0x29800822}, + {0x0000a5b4, 0x4282242a, 0x4282242a, 0x2d800824, 0x2d800824}, + {0x0000a5b8, 0x4782244a, 0x4782244a, 0x30800828, 0x30800828}, + {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x3480082a, 0x3480082a}, + {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x38800849, 0x38800849}, + {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3b800a2c, 0x3b800a2c}, + {0x0000a5c8, 0x5782286c, 0x5782286c, 0x3e800e2b, 0x3e800e2b}, + {0x0000a5cc, 0x5c84286b, 0x5c84286b, 0x42800e2d, 0x42800e2d}, + {0x0000a5d0, 0x61842a6c, 0x61842a6c, 0x4580124a, 0x4580124a}, + {0x0000a5d4, 0x66862a6c, 0x66862a6c, 0x4980124c, 0x4980124c}, + {0x0000a5d8, 0x6b862e6c, 0x6b862e6c, 0x4c80126c, 0x4c80126c}, + {0x0000a5dc, 0x7086308c, 0x7086308c, 0x4f80128c, 0x4f80128c}, + {0x0000a5e0, 0x738a308a, 0x738a308a, 0x52801290, 0x52801290}, + {0x0000a5e4, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292}, + {0x0000a5e8, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292}, + {0x0000a5ec, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292}, + {0x0000a5f0, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292}, + {0x0000a5f4, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292}, + {0x0000a5f8, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292}, + {0x0000a5fc, 0x778a308c, 0x778a308c, 0x56801292, 0x56801292}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a614, 0x01404000, 0x01404000, 0x01404501, 0x01404501}, + {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, + {0x0000a61c, 0x02008802, 0x02008802, 0x01404501, 0x01404501}, + {0x0000a620, 0x0300cc03, 0x0300cc03, 0x03c0cf02, 0x03c0cf02}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03c0cf03, 0x03c0cf03}, + {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04011004, 0x04011004}, + {0x0000a62c, 0x03810c03, 0x03810c03, 0x05419405, 0x05419405}, + {0x0000a630, 0x03810e04, 0x03810e04, 0x05419506, 0x05419506}, + {0x0000a634, 0x03810e04, 0x03810e04, 0x05419506, 0x05419506}, + {0x0000a638, 0x03810e04, 0x03810e04, 0x05419506, 0x05419506}, + {0x0000a63c, 0x03810e04, 0x03810e04, 0x05419506, 0x05419506}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03eaac5a, 0x03eaac5a}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03f330ac, 0x03f330ac}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03fc3f00, 0x03fc3f00}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ffc000, 0x03ffc000}, + {0x00016044, 0x022492db, 0x022492db, 0x022492db, 0x022492db}, + {0x00016048, 0x24925666, 0x24925666, 0x24925266, 0x24925266}, + {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015}, + {0x00016288, 0xf0318000, 0xf0318000, 0xf0318000, 0xf0318000}, + {0x00016444, 0x022492db, 0x022492db, 0x022492db, 0x022492db}, + {0x00016448, 0x24925666, 0x24925666, 0x24925266, 0x24925266}, +}; + static const u32 ar9340_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h index a3710f3bb90c..712f415b8c08 100644 --- a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h @@ -260,6 +260,79 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; +static const u32 ar9485Modes_green_ob_db_tx_gain_1_1[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, + {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006}, + {0x0000a504, 0x05062002, 0x05062002, 0x03000201, 0x03000201}, + {0x0000a508, 0x0c002e00, 0x0c002e00, 0x06000203, 0x06000203}, + {0x0000a50c, 0x11062202, 0x11062202, 0x0a000401, 0x0a000401}, + {0x0000a510, 0x17022e00, 0x17022e00, 0x0e000403, 0x0e000403}, + {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x12000405, 0x12000405}, + {0x0000a518, 0x25020ec0, 0x25020ec0, 0x15000604, 0x15000604}, + {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x18000605, 0x18000605}, + {0x0000a520, 0x2f001f04, 0x2f001f04, 0x1c000a04, 0x1c000a04}, + {0x0000a524, 0x35001fc4, 0x35001fc4, 0x21000a06, 0x21000a06}, + {0x0000a528, 0x3c022f04, 0x3c022f04, 0x29000a24, 0x29000a24}, + {0x0000a52c, 0x41023e85, 0x41023e85, 0x2f000e21, 0x2f000e21}, + {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000e20, 0x31000e20}, + {0x0000a534, 0x4d023f01, 0x4d023f01, 0x33000e20, 0x33000e20}, + {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b50c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b510, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b514, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b518, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b51c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b520, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b524, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b528, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b52c, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a}, + {0x0000b530, 0x0000003a, 0x0000003a, 0x0000003a, 0x0000003a}, + {0x0000b534, 0x0000004a, 0x0000004a, 0x0000004a, 0x0000004a}, + {0x0000b538, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b53c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b540, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b544, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b548, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b54c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b550, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b554, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b558, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b55c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b560, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b564, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b568, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b56c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b570, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b574, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b578, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b57c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, + {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, +}; + static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, @@ -450,6 +523,79 @@ static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { #define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1 +static const u32 ar9485Modes_green_spur_ob_db_tx_gain_1_1[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, + {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006}, + {0x0000a504, 0x05062002, 0x05062002, 0x03000201, 0x03000201}, + {0x0000a508, 0x0c002e00, 0x0c002e00, 0x07000203, 0x07000203}, + {0x0000a50c, 0x11062202, 0x11062202, 0x0a000401, 0x0a000401}, + {0x0000a510, 0x17022e00, 0x17022e00, 0x0e000403, 0x0e000403}, + {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x12000405, 0x12000405}, + {0x0000a518, 0x25020ec0, 0x25020ec0, 0x14000406, 0x14000406}, + {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1800040a, 0x1800040a}, + {0x0000a520, 0x2f001f04, 0x2f001f04, 0x1c000460, 0x1c000460}, + {0x0000a524, 0x35001fc4, 0x35001fc4, 0x22000463, 0x22000463}, + {0x0000a528, 0x3c022f04, 0x3c022f04, 0x26000465, 0x26000465}, + {0x0000a52c, 0x41023e85, 0x41023e85, 0x2e0006e0, 0x2e0006e0}, + {0x0000a530, 0x48023ec6, 0x48023ec6, 0x310006e0, 0x310006e0}, + {0x0000a534, 0x4d023f01, 0x4d023f01, 0x330006e0, 0x330006e0}, + {0x0000a538, 0x53023f4b, 0x53023f4b, 0x3e0008e3, 0x3e0008e3}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x410008e5, 0x410008e5}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x430008e6, 0x430008e6}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4a0008ec, 0x4a0008ec}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4e0008f1, 0x4e0008f1}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x520008f3, 0x520008f3}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x54000eed, 0x54000eed}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x58000ef1, 0x58000ef1}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5c000ef3, 0x5c000ef3}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x60000ef5, 0x60000ef5}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x62000ef6, 0x62000ef6}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b50c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b510, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b514, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b518, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b51c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b520, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b524, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b528, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, + {0x0000b52c, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a}, + {0x0000b530, 0x0000003a, 0x0000003a, 0x0000003a, 0x0000003a}, + {0x0000b534, 0x0000004a, 0x0000004a, 0x0000004a, 0x0000004a}, + {0x0000b538, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b53c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b540, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b544, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b548, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b54c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b550, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b554, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b558, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b55c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b560, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b564, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b568, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b56c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b570, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b574, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b578, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x0000b57c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, + {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, + {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, +}; + static const u32 ar9485_1_1[][2] = { /* Addr allmodes */ {0x0000a580, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h index 6e1915aee712..28fd99203f64 100644 --- a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h @@ -685,6 +685,82 @@ static const u32 ar9580_1p0_mixed_ob_db_tx_gain_table[][5] = { #define ar9580_1p0_high_ob_db_tx_gain_table ar9300Modes_high_ob_db_tx_gain_table_2p2 +#define ar9580_1p0_type5_tx_gain_table ar9300Modes_type5_tx_gain_table_2p2 + +static const u32 ar9580_1p0_type6_tx_gain_table[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400}, + {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402}, + {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404}, + {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603}, + {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02}, + {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04}, + {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20}, + {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20}, + {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22}, + {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24}, + {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640}, + {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660}, + {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861}, + {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81}, + {0x0000a54c, 0x5e08442e, 0x5e08442e, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x620a4431, 0x620a4431, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000}, + {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501}, + {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501}, + {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016048, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, + {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016448, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, + {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016848, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, + {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, +}; + static const u32 ar9580_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x000040a4, 0x00a0c1c9}, diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 0ac205e4cd04..5929850649f0 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -789,6 +789,7 @@ #define AR_SREV_REVISION_9271_11 1 #define AR_SREV_VERSION_9300 0x1c0 #define AR_SREV_REVISION_9300_20 2 /* 2.0 and 2.1 */ +#define AR_SREV_REVISION_9300_22 3 #define AR_SREV_VERSION_9330 0x200 #define AR_SREV_REVISION_9330_10 0 #define AR_SREV_REVISION_9330_11 1 @@ -869,6 +870,9 @@ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9300)) #define AR_SREV_9300_20_OR_LATER(_ah) \ ((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9300) +#define AR_SREV_9300_22(_ah) \ + (AR_SREV_9300(ah) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9300_22)) #define AR_SREV_9330(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9330)) From 990debe2ca8379863709721926550a55f47f3880 Mon Sep 17 00:00:00 2001 From: Nathan Hintz Date: Thu, 10 Jan 2013 22:24:03 -0800 Subject: [PATCH 345/478] bcma: update pci configuration for bcm4706/bcm4716 Update the PCI configuration for BCM4706 and BCM4716 per the 2011 Broadcom SDK. Signed-off-by: Nathan Hintz Signed-off-by: John W. Linville --- drivers/bcma/driver_pci_host.c | 13 ++++++++++++- include/linux/bcma/bcma_driver_pci.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index ef9f0938da77..37d1777dcd47 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -427,7 +427,7 @@ void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) /* Reset RC */ usleep_range(3000, 5000); pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST_OE); - usleep_range(1000, 2000); + msleep(50); pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST | BCMA_CORE_PCI_CTL_RST_OE); @@ -489,6 +489,17 @@ void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) bcma_core_pci_enable_crs(pc); + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706 || + bus->chipinfo.id == BCMA_CHIP_ID_BCM4716) { + u16 val16; + bcma_extpci_read_config(pc, 0, 0, BCMA_CORE_PCI_CFG_DEVCTRL, + &val16, sizeof(val16)); + val16 |= (2 << 5); /* Max payload size of 512 */ + val16 |= (2 << 12); /* MRRS 512 */ + bcma_extpci_write_config(pc, 0, 0, BCMA_CORE_PCI_CFG_DEVCTRL, + &val16, sizeof(val16)); + } + /* Enable PCI bridge BAR0 memory & master access */ tmp = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; bcma_extpci_write_config(pc, 0, 0, PCI_COMMAND, &tmp, sizeof(tmp)); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 41da581e1612..31232247a1ee 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -179,6 +179,8 @@ struct pci_dev; #define BCMA_CORE_PCI_CFG_FUN_MASK 7 /* Function mask */ #define BCMA_CORE_PCI_CFG_OFF_MASK 0xfff /* Register mask */ +#define BCMA_CORE_PCI_CFG_DEVCTRL 0xd8 + /* PCIE Root Capability Register bits (Host mode only) */ #define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001 From 858ce0220922a52e009d17dcaaab5dfd76f9833b Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Sat, 12 Jan 2013 19:37:12 +0100 Subject: [PATCH 346/478] NFC: pn533: Fix bad allocation size Use dereferenced pointer in sizeof instead of pointer itself. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index f1702f945701..f696318cfb51 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -743,7 +743,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", cmd_code); - arg = kzalloc(sizeof(arg), GFP_KERNEL); + arg = kzalloc(sizeof(*arg), GFP_KERNEL); if (!arg) return -ENOMEM; @@ -863,7 +863,7 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code, if (!resp) return -ENOMEM; - arg = kzalloc(sizeof(arg), GFP_KERNEL); + arg = kzalloc(sizeof(*arg), GFP_KERNEL); if (!arg) { dev_kfree_skb(resp); return -ENOMEM; From 0c0280bd0ba410326eecdaeb1b936696eda6381d Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 11 Jan 2013 18:39:36 +0000 Subject: [PATCH 347/478] wireless: make the reg_notifier() void The reg_notifier()'s return value need not be checked as it is only supposed to do post regulatory work and that should never fail. Any behaviour to regulatory that needs to be considered before cfg80211 does work to a driver should be specified by using the already existing flags, the reg_notifier() just does post processing should it find it needs to. Also make lbs_reg_notifier static. Signed-off-by: Luis R. Rodriguez [move lbs_reg_notifier to not break compile] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath5k/base.c | 5 +-- drivers/net/wireless/ath/ath6kl/cfg80211.c | 16 +++------ drivers/net/wireless/ath/ath9k/htc_drv_init.c | 8 ++--- drivers/net/wireless/ath/ath9k/init.c | 9 ++--- drivers/net/wireless/ath/carl9170/main.c | 6 ++-- drivers/net/wireless/ath/regd.c | 18 +++++----- drivers/net/wireless/ath/regd.h | 10 +++--- .../net/wireless/brcm80211/brcmsmac/channel.c | 6 ++-- drivers/net/wireless/libertas/cfg.c | 33 +++++++++---------- drivers/net/wireless/libertas/cfg.h | 3 -- drivers/net/wireless/mwifiex/cfg80211.c | 6 ++-- drivers/net/wireless/rtlwifi/regd.c | 20 +++++------ drivers/net/wireless/rtlwifi/regd.h | 6 ++-- drivers/net/wireless/ti/wlcore/main.c | 6 ++-- include/net/cfg80211.h | 4 +-- 15 files changed, 66 insertions(+), 90 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 30ca0a60a64c..1d264c0f5a9b 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -240,13 +240,14 @@ static const struct ath_ops ath5k_common_ops = { * Driver Initialization * \***********************/ -static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) +static void ath5k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath5k_hw *ah = hw->priv; struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); - return ath_reg_notifier_apply(wiphy, request, regulatory); + ath_reg_notifier_apply(wiphy, request, regulatory); } /********************\ diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 5516a8ccc3c6..4225cca0f198 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3492,8 +3492,8 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) ath6kl_cfg80211_stop(vif); } -static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy, - struct regulatory_request *request) +static void ath6kl_cfg80211_reg_notify(struct wiphy *wiphy, + struct regulatory_request *request) { struct ath6kl *ar = wiphy_priv(wiphy); u32 rates[IEEE80211_NUM_BANDS]; @@ -3506,17 +3506,13 @@ static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy, request->processed ? " processed" : "", request->initiator, request->user_reg_hint_type); - /* - * As firmware is not able intersect regdoms, we can only listen to - * cellular hints. - */ if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) - return -EOPNOTSUPP; + return; ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2); if (ret) { ath6kl_err("failed to set regdomain: %d\n", ret); - return ret; + return; } /* @@ -3536,10 +3532,8 @@ static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy, if (ret) { ath6kl_err("failed to start scan for a regdomain change: %d\n", ret); - return ret; + return; } - - return 0; } static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 05d5ba66cac3..e5d7958ab948 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -280,14 +280,14 @@ err: return ret; } -static int ath9k_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) +static void ath9k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath9k_htc_priv *priv = hw->priv; - return ath_reg_notifier_apply(wiphy, request, - ath9k_hw_regulatory(priv->ah)); + ath_reg_notifier_apply(wiphy, request, + ath9k_hw_regulatory(priv->ah)); } static unsigned int ath9k_regread(void *hw_priv, u32 reg_offset) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index f69ef5d48c7b..315d6593e18e 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -302,16 +302,15 @@ static void setup_ht_cap(struct ath_softc *sc, ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; } -static int ath9k_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) +static void ath9k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_regulatory *reg = ath9k_hw_regulatory(ah); - int ret; - ret = ath_reg_notifier_apply(wiphy, request, reg); + ath_reg_notifier_apply(wiphy, request, reg); /* Set tx power */ if (ah->curchan) { @@ -321,8 +320,6 @@ static int ath9k_reg_notifier(struct wiphy *wiphy, sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit; ath9k_ps_restore(sc); } - - return ret; } /* diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 9d2051aeb782..aaa2699e5a8c 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1918,13 +1918,13 @@ static int carl9170_parse_eeprom(struct ar9170 *ar) return 0; } -static int carl9170_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) +static void carl9170_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ar9170 *ar = hw->priv; - return ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory); + ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory); } int carl9170_register(struct ar9170 *ar) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 7a6c79e1f819..ccc4c718f124 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -356,9 +356,9 @@ static u16 ath_regd_find_country_by_name(char *alpha2) return -1; } -int ath_reg_notifier_apply(struct wiphy *wiphy, - struct regulatory_request *request, - struct ath_regulatory *reg) +void ath_reg_notifier_apply(struct wiphy *wiphy, + struct regulatory_request *request, + struct ath_regulatory *reg) { struct ath_common *common = container_of(reg, struct ath_common, regulatory); @@ -373,7 +373,7 @@ int ath_reg_notifier_apply(struct wiphy *wiphy, * any pending requests in the queue. */ if (!request) - return 0; + return; switch (request->initiator) { case NL80211_REGDOM_SET_BY_CORE: @@ -409,8 +409,6 @@ int ath_reg_notifier_apply(struct wiphy *wiphy, break; } - - return 0; } EXPORT_SYMBOL(ath_reg_notifier_apply); @@ -500,8 +498,8 @@ ath_get_regpair(int regdmn) static int ath_regd_init_wiphy(struct ath_regulatory *reg, struct wiphy *wiphy, - int (*reg_notifier)(struct wiphy *wiphy, - struct regulatory_request *request)) + void (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request)) { const struct ieee80211_regdomain *regd; @@ -621,8 +619,8 @@ static int __ath_regd_init(struct ath_regulatory *reg) int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, - int (*reg_notifier)(struct wiphy *wiphy, - struct regulatory_request *request)) + void (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request)) { struct ath_common *common = container_of(reg, struct ath_common, regulatory); diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h index 03a8268ccf21..37f53bd8fcb1 100644 --- a/drivers/net/wireless/ath/regd.h +++ b/drivers/net/wireless/ath/regd.h @@ -252,12 +252,12 @@ enum CountryCode { bool ath_is_world_regd(struct ath_regulatory *reg); bool ath_is_49ghz_allowed(u16 redomain); int ath_regd_init(struct ath_regulatory *reg, struct wiphy *wiphy, - int (*reg_notifier)(struct wiphy *wiphy, - struct regulatory_request *request)); + void (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request)); u32 ath_regd_get_band_ctl(struct ath_regulatory *reg, enum ieee80211_band band); -int ath_reg_notifier_apply(struct wiphy *wiphy, - struct regulatory_request *request, - struct ath_regulatory *reg); +void ath_reg_notifier_apply(struct wiphy *wiphy, + struct regulatory_request *request, + struct ath_regulatory *reg); #endif diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 4eb3f0d52105..cdb62b8ccc79 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -702,8 +702,8 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, } } -static int brcms_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) +static void brcms_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct brcms_info *wl = hw->priv; @@ -744,8 +744,6 @@ static int brcms_reg_notifier(struct wiphy *wiphy, if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, brcms_c_japan_ccode(request->alpha2)); - - return 0; } void brcms_c_regd_init(struct brcms_c_info *wlc) diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index ec6d5d6b452e..230f8ebbe289 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -2132,6 +2132,21 @@ static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv) lbs_deb_leave(LBS_DEB_CFG80211); } +static void lbs_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " + "callback for domain %c%c\n", request->alpha2[0], + request->alpha2[1]); + + memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); + if (lbs_iface_active(priv)) + lbs_set_11d_domain_info(priv); + + lbs_deb_leave(LBS_DEB_CFG80211); +} /* * This function get's called after lbs_setup_firmware() determined the @@ -2184,24 +2199,6 @@ int lbs_cfg_register(struct lbs_private *priv) return ret; } -int lbs_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = 0; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " - "callback for domain %c%c\n", request->alpha2[0], - request->alpha2[1]); - - memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); - if (lbs_iface_active(priv)) - ret = lbs_set_11d_domain_info(priv); - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - void lbs_scan_deinit(struct lbs_private *priv) { lbs_deb_enter(LBS_DEB_CFG80211); diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h index 558168ce634d..10995f59fe34 100644 --- a/drivers/net/wireless/libertas/cfg.h +++ b/drivers/net/wireless/libertas/cfg.h @@ -10,9 +10,6 @@ struct wireless_dev *lbs_cfg_alloc(struct device *dev); int lbs_cfg_register(struct lbs_private *priv); void lbs_cfg_free(struct lbs_private *priv); -int lbs_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request); - void lbs_send_disconnect_notification(struct lbs_private *priv); void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index a875499f8945..a838ddecd91a 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -519,8 +519,8 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) * - Set by user * - Set bt Country IE */ -static int mwifiex_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) +static void mwifiex_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); @@ -540,8 +540,6 @@ static int mwifiex_reg_notifier(struct wiphy *wiphy, break; } mwifiex_send_domain_info_cmd_fw(wiphy); - - return 0; } /* diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c index 7e3ead774fb9..d7d0d4948b01 100644 --- a/drivers/net/wireless/rtlwifi/regd.c +++ b/drivers/net/wireless/rtlwifi/regd.c @@ -298,9 +298,9 @@ static void _rtl_reg_apply_world_flags(struct wiphy *wiphy, return; } -static int _rtl_reg_notifier_apply(struct wiphy *wiphy, - struct regulatory_request *request, - struct rtl_regulatory *reg) +static void _rtl_reg_notifier_apply(struct wiphy *wiphy, + struct regulatory_request *request, + struct rtl_regulatory *reg) { /* We always apply this */ _rtl_reg_apply_radar_flags(wiphy); @@ -314,8 +314,6 @@ static int _rtl_reg_notifier_apply(struct wiphy *wiphy, _rtl_reg_apply_world_flags(wiphy, request->initiator, reg); break; } - - return 0; } static const struct ieee80211_regdomain *_rtl_regdomain_select( @@ -348,9 +346,9 @@ static const struct ieee80211_regdomain *_rtl_regdomain_select( static int _rtl_regd_init_wiphy(struct rtl_regulatory *reg, struct wiphy *wiphy, - int (*reg_notifier) (struct wiphy *wiphy, - struct regulatory_request * - request)) + void (*reg_notifier) (struct wiphy *wiphy, + struct regulatory_request * + request)) { const struct ieee80211_regdomain *regd; @@ -379,7 +377,7 @@ static struct country_code_to_enum_rd *_rtl_regd_find_country(u16 countrycode) } int rtl_regd_init(struct ieee80211_hw *hw, - int (*reg_notifier) (struct wiphy *wiphy, + void (*reg_notifier) (struct wiphy *wiphy, struct regulatory_request *request)) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -421,12 +419,12 @@ int rtl_regd_init(struct ieee80211_hw *hw, return 0; } -int rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) +void rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct rtl_priv *rtlpriv = rtl_priv(hw); RT_TRACE(rtlpriv, COMP_REGD, DBG_LOUD, "\n"); - return _rtl_reg_notifier_apply(wiphy, request, &rtlpriv->regd); + _rtl_reg_notifier_apply(wiphy, request, &rtlpriv->regd); } diff --git a/drivers/net/wireless/rtlwifi/regd.h b/drivers/net/wireless/rtlwifi/regd.h index 70ef2f418a44..4e1f4f00e6e9 100644 --- a/drivers/net/wireless/rtlwifi/regd.h +++ b/drivers/net/wireless/rtlwifi/regd.h @@ -55,7 +55,7 @@ enum country_code_type_t { }; int rtl_regd_init(struct ieee80211_hw *hw, - int (*reg_notifier) (struct wiphy *wiphy, - struct regulatory_request *request)); -int rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); + void (*reg_notifier) (struct wiphy *wiphy, + struct regulatory_request *request)); +void rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index d7de06359ae1..ce6e62a37e14 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -89,8 +89,8 @@ static int wl12xx_set_authorized(struct wl1271 *wl, return 0; } -static int wl1271_reg_notify(struct wiphy *wiphy, - struct regulatory_request *request) +static void wl1271_reg_notify(struct wiphy *wiphy, + struct regulatory_request *request) { struct ieee80211_supported_band *band; struct ieee80211_channel *ch; @@ -107,8 +107,6 @@ static int wl1271_reg_notify(struct wiphy *wiphy, IEEE80211_CHAN_PASSIVE_SCAN; } - - return 0; } static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e5f085c89221..4275127da05a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2364,8 +2364,8 @@ struct wiphy { struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]; /* Lets us get back the wiphy on the callback */ - int (*reg_notifier)(struct wiphy *wiphy, - struct regulatory_request *request); + void (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request); /* fields below are read-only, assigned by cfg80211 */ From d3710e74cf329839dea8d13b1ad56e572b06b173 Mon Sep 17 00:00:00 2001 From: Lauro Ramos Venancio Date: Wed, 5 Dec 2012 21:12:25 -0300 Subject: [PATCH 348/478] NFC: Change nfc.h license nfc.h being GPL makes it quite controversial for non GPL applications to include it. Moreover, nfc.h only includes structures and API definitions that are hardly copyrightable. Signed-off-by: Lauro Ramos Venancio Signed-off-by: Samuel Ortiz --- include/uapi/linux/nfc.h | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 80e4ecd8c04c..7969f46f1bb3 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -5,20 +5,17 @@ * Lauro Ramos Venancio * Aloisio Almeida Jr * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __LINUX_NFC_H From 463e3ed3eacc8f47866e5d612bd8ee0bcee5e2f0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Jan 2013 10:50:15 +0100 Subject: [PATCH 349/478] ath9k: remove sc->rx.rxbuflock to fix a deadlock The commit "ath9k: fix rx flush handling" added a deadlock that happens because ath_rx_tasklet is called in a section that has already taken the rx buffer lock. It seems that the only purpose of the rxbuflock was a band-aid fix to the reset vs rx tasklet race, which has been properly fixed in the commit "ath9k: add a better fix for the rx tasklet vs rx flush race". Now that the fix is in, we can safely remove the lock to avoid such issues. Cc: stable@vger.kernel.org Reported-by: Sujith Manoharan Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 - drivers/net/wireless/ath/ath9k/recv.c | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 32b2d5cd9a5a..42794c546a40 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -317,7 +317,6 @@ struct ath_rx { u32 *rxlink; u32 num_pkts; unsigned int rxfilter; - spinlock_t rxbuflock; struct list_head rxbuf; struct ath_descdma rxdma; struct ath_buf *rx_bufptr; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 67f58d4bb10e..90752f246970 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -254,8 +254,6 @@ rx_init_fail: static void ath_edma_start_recv(struct ath_softc *sc) { - spin_lock_bh(&sc->rx.rxbuflock); - ath9k_hw_rxena(sc->sc_ah); ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP, @@ -267,8 +265,6 @@ static void ath_edma_start_recv(struct ath_softc *sc) ath_opmode_init(sc); ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); - - spin_unlock_bh(&sc->rx.rxbuflock); } static void ath_edma_stop_recv(struct ath_softc *sc) @@ -285,7 +281,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) int error = 0; spin_lock_init(&sc->sc_pcu_lock); - spin_lock_init(&sc->rx.rxbuflock); common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 + sc->sc_ah->caps.rx_status_len; @@ -446,7 +441,6 @@ int ath_startrecv(struct ath_softc *sc) return 0; } - spin_lock_bh(&sc->rx.rxbuflock); if (list_empty(&sc->rx.rxbuf)) goto start_recv; @@ -467,8 +461,6 @@ start_recv: ath_opmode_init(sc); ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); - spin_unlock_bh(&sc->rx.rxbuflock); - return 0; } @@ -484,7 +476,6 @@ bool ath_stoprecv(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; bool stopped, reset = false; - spin_lock_bh(&sc->rx.rxbuflock); ath9k_hw_abortpcurecv(ah); ath9k_hw_setrxfilter(ah, 0); stopped = ath9k_hw_stopdmarecv(ah, &reset); @@ -495,7 +486,6 @@ bool ath_stoprecv(struct ath_softc *sc) ath_edma_stop_recv(sc); else sc->rx.rxlink = NULL; - spin_unlock_bh(&sc->rx.rxbuflock); if (!(ah->ah_flags & AH_UNPLUGGED) && unlikely(!stopped)) { @@ -1059,7 +1049,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) dma_type = DMA_FROM_DEVICE; qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP; - spin_lock_bh(&sc->rx.rxbuflock); tsf = ath9k_hw_gettsf64(ah); tsf_lower = tsf & 0xffffffff; @@ -1254,8 +1243,6 @@ requeue: } } while (1); - spin_unlock_bh(&sc->rx.rxbuflock); - if (!(ah->imask & ATH9K_INT_RXEOL)) { ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN); ath9k_hw_set_interrupts(ah); From 4668cce527acb3bd048c5e6c99b157a14b214671 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Jan 2013 16:56:46 +0100 Subject: [PATCH 350/478] ath9k: disable the tasklet before taking the PCU lock Fixes a reported CPU soft lockup where the tasklet tries to acquire the lock and blocks while ath_prepare_reset (holding the lock) waits for it to complete. Cc: stable@vger.kernel.org Reported-by: Robert Shade Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9844b758f81d..3796e65c26f1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -196,16 +196,12 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx) ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); - tasklet_disable(&sc->intr_tq); - if (!ath_stoprecv(sc)) ret = false; if (!ath_drain_all_txq(sc, retry_tx)) ret = false; - tasklet_enable(&sc->intr_tq); - return ret; } @@ -262,6 +258,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, __ath_cancel_work(sc); + tasklet_disable(&sc->intr_tq); spin_lock_bh(&sc->sc_pcu_lock); if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { @@ -296,6 +293,8 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, out: spin_unlock_bh(&sc->sc_pcu_lock); + tasklet_enable(&sc->intr_tq); + return r; } From e2d4a24e03f9f7b255e86a2537dca0927f08af4a Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Sat, 12 Jan 2013 10:28:12 +0800 Subject: [PATCH 351/478] ath9k_hw: ar9002_hw_spectral_scan_config() can be static Signed-off-by: Fengguang Wu Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 2cb665e2ea22..f4003512d8d5 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -555,7 +555,7 @@ static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval); } -void ar9002_hw_spectral_scan_config(struct ath_hw *ah, +static void ar9002_hw_spectral_scan_config(struct ath_hw *ah, struct ath_spec_scan *param) { u8 count; From 6bf2e5461479c4511f59946a7378db576b04dbc5 Mon Sep 17 00:00:00 2001 From: Nathan Hintz Date: Fri, 11 Jan 2013 22:07:22 -0800 Subject: [PATCH 352/478] bcma: fix bcm4716/bcm4748 i2s irqflag The default irqflag assignment for the I2S core on some Broadcom 4716/4748 devices is invalid and needs to be corrected (from the Broadcom SDK). Signed-off-by: Nathan Hintz Acked-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_mips.c | 28 +++++++++++++++++++++++++++ include/linux/bcma/bcma_driver_mips.h | 1 + 2 files changed, 29 insertions(+) diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index a808404db4b1..9fe86ee16c66 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -256,6 +256,32 @@ void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) mcore->early_setup_done = true; } +static void bcma_fix_i2s_irqflag(struct bcma_bus *bus) +{ + struct bcma_device *cpu, *pcie, *i2s; + + /* Fixup the interrupts in 4716/4748 for i2s core (2010 Broadcom SDK) + * (IRQ flags > 7 are ignored when setting the interrupt masks) + */ + if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4716 && + bus->chipinfo.id != BCMA_CHIP_ID_BCM4748) + return; + + cpu = bcma_find_core(bus, BCMA_CORE_MIPS_74K); + pcie = bcma_find_core(bus, BCMA_CORE_PCIE); + i2s = bcma_find_core(bus, BCMA_CORE_I2S); + if (cpu && pcie && i2s && + bcma_aread32(cpu, BCMA_MIPS_OOBSELINA74) == 0x08060504 && + bcma_aread32(pcie, BCMA_MIPS_OOBSELINA74) == 0x08060504 && + bcma_aread32(i2s, BCMA_MIPS_OOBSELOUTA30) == 0x88) { + bcma_awrite32(cpu, BCMA_MIPS_OOBSELINA74, 0x07060504); + bcma_awrite32(pcie, BCMA_MIPS_OOBSELINA74, 0x07060504); + bcma_awrite32(i2s, BCMA_MIPS_OOBSELOUTA30, 0x87); + bcma_debug(bus, + "Moved i2s interrupt to oob line 7 instead of 8\n"); + } +} + void bcma_core_mips_init(struct bcma_drv_mips *mcore) { struct bcma_bus *bus; @@ -269,6 +295,8 @@ void bcma_core_mips_init(struct bcma_drv_mips *mcore) bcma_core_mips_early_init(mcore); + bcma_fix_i2s_irqflag(bus); + switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4716: case BCMA_CHIP_ID_BCM4748: diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h index 73c7f4b882cc..0d1ea297851a 100644 --- a/include/linux/bcma/bcma_driver_mips.h +++ b/include/linux/bcma/bcma_driver_mips.h @@ -28,6 +28,7 @@ #define BCMA_MIPS_MIPS74K_GPIOEN 0x0048 #define BCMA_MIPS_MIPS74K_CLKCTLST 0x01E0 +#define BCMA_MIPS_OOBSELINA74 0x004 #define BCMA_MIPS_OOBSELOUTA30 0x100 struct bcma_device; From f6734627edea983b307593f71824d7ec2dd2625a Mon Sep 17 00:00:00 2001 From: Nathan Hintz Date: Sat, 12 Jan 2013 02:46:13 -0800 Subject: [PATCH 353/478] bcma: delete duplicate readl The 'val' has already been read by the prior call to 'mips_busprobe32'; this 'readl' is unnecessary. Signed-off-by: Nathan Hintz Acked-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci_host.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index 37d1777dcd47..cf3dca987257 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -122,8 +122,6 @@ static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev, val = 0xffffffff; goto unmap; } - - val = readl(mmio); } val >>= (8 * (off & 3)); From a35ab937cb27ec8c00b6f13cade93dc10e22b670 Mon Sep 17 00:00:00 2001 From: Nathan Hintz Date: Sat, 12 Jan 2013 02:46:14 -0800 Subject: [PATCH 354/478] bcma: jump to 'out' label for invalid 'func' value Consistently jump to the 'out' label for error conditions (adds missing check for 'func' validity in bcma_extpci_write_config). Signed-off-by: Nathan Hintz Acked-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci_host.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index cf3dca987257..4a4152389515 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -94,7 +94,7 @@ static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev, if (dev == 0) { /* we support only two functions on device 0 */ if (func > 1) - return -EINVAL; + goto out; /* accesses to config registers with offsets >= 256 * requires indirect access. @@ -157,6 +157,10 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, if (unlikely(len != 1 && len != 2 && len != 4)) goto out; if (dev == 0) { + /* we support only two functions on device 0 */ + if (func > 1) + goto out; + /* accesses to config registers with offsets >= 256 * requires indirect access. */ From 447d7e25be462329268f279bef2f6faa986a5185 Mon Sep 17 00:00:00 2001 From: Nathan Hintz Date: Sat, 12 Jan 2013 02:46:15 -0800 Subject: [PATCH 355/478] bcma: don't map/unmap a subset of the PCI config space For PCI config space access offsets < 256 for device '0', bcma_extpci_write_config performs an 'ioremap_nocache' on a 4 byte section of the PCI config space (an area that has already previously been mapped), and then subsequently unmaps that 4 byte section. This can't be a good thing for future read access from that now unmapped location. Modify the config space writes to use the existing access functions (similar to how it is done for the reads). Signed-off-by: Nathan Hintz Acked-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci_host.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index 4a4152389515..c0cb0620ab62 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -149,7 +149,7 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, const void *buf, int len) { int err = -EINVAL; - u32 addr = 0, val = 0; + u32 addr, val; void __iomem *mmio = 0; u16 chipid = pc->core->bus->chipinfo.id; @@ -165,12 +165,10 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, * requires indirect access. */ if (off < PCI_CONFIG_SPACE_SIZE) { - addr = pc->core->addr + BCMA_CORE_PCI_PCICFG0; + addr = BCMA_CORE_PCI_PCICFG0; addr |= (func << 8); addr |= (off & 0xfc); - mmio = ioremap_nocache(addr, sizeof(val)); - if (!mmio) - goto out; + val = pcicore_read32(pc, addr); } } else { addr = bcma_get_cfgspace_addr(pc, dev, func, off); @@ -189,12 +187,10 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, switch (len) { case 1: - val = readl(mmio); val &= ~(0xFF << (8 * (off & 3))); val |= *((const u8 *)buf) << (8 * (off & 3)); break; case 2: - val = readl(mmio); val &= ~(0xFFFF << (8 * (off & 3))); val |= *((const u16 *)buf) << (8 * (off & 3)); break; @@ -202,13 +198,17 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, val = *((const u32 *)buf); break; } - if (dev == 0 && !addr) { + if (dev == 0) { /* accesses to config registers with offsets >= 256 * requires indirect access. */ - addr = (func << 12); - addr |= (off & 0x0FFF); - bcma_pcie_write_config(pc, addr, val); + if (off >= PCI_CONFIG_SPACE_SIZE) { + addr = (func << 12); + addr |= (off & 0x0FFF); + bcma_pcie_write_config(pc, addr, val); + } else { + pcicore_write32(pc, addr, val); + } } else { writel(val, mmio); From b09e9abd091a4ed1e638a810b25c6577b4621b12 Mon Sep 17 00:00:00 2001 From: Nathan Hintz Date: Sat, 12 Jan 2013 02:46:16 -0800 Subject: [PATCH 356/478] bcma: add support for 1 and 2 byte extended config space access The sanity checks allow 1 and 2 byte reads/writes of the extended PCI config space to proceed; however, the code only supports 4 byte reads/writes. This patch adds support for 1 and 2 byte reads/writes of the extended PCI config space. Signed-off-by: Nathan Hintz Acked-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci_host.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index c0cb0620ab62..f92124c29998 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -101,7 +101,7 @@ static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev, */ if (off >= PCI_CONFIG_SPACE_SIZE) { addr = (func << 12); - addr |= (off & 0x0FFF); + addr |= (off & 0x0FFC); val = bcma_pcie_read_config(pc, addr); } else { addr = BCMA_CORE_PCI_PCICFG0; @@ -164,7 +164,11 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, /* accesses to config registers with offsets >= 256 * requires indirect access. */ - if (off < PCI_CONFIG_SPACE_SIZE) { + if (off >= PCI_CONFIG_SPACE_SIZE) { + addr = (func << 12); + addr |= (off & 0x0FFC); + val = bcma_pcie_read_config(pc, addr); + } else { addr = BCMA_CORE_PCI_PCICFG0; addr |= (func << 8); addr |= (off & 0xfc); @@ -202,13 +206,10 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, /* accesses to config registers with offsets >= 256 * requires indirect access. */ - if (off >= PCI_CONFIG_SPACE_SIZE) { - addr = (func << 12); - addr |= (off & 0x0FFF); + if (off >= PCI_CONFIG_SPACE_SIZE) bcma_pcie_write_config(pc, addr, val); - } else { + else pcicore_write32(pc, addr, val); - } } else { writel(val, mmio); From 660b9caaad380758c756a7e60397e33dab44fe3f Mon Sep 17 00:00:00 2001 From: Nathan Hintz Date: Sat, 12 Jan 2013 02:46:17 -0800 Subject: [PATCH 357/478] bcma: use consistent case for 'hex' constants Signed-off-by: Nathan Hintz Acked-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci_host.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index f92124c29998..221f8bb76390 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -106,7 +106,7 @@ static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev, } else { addr = BCMA_CORE_PCI_PCICFG0; addr |= (func << 8); - addr |= (off & 0xfc); + addr |= (off & 0xFC); val = pcicore_read32(pc, addr); } } else { @@ -119,7 +119,7 @@ static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev, goto out; if (mips_busprobe32(val, mmio)) { - val = 0xffffffff; + val = 0xFFFFFFFF; goto unmap; } } @@ -171,7 +171,7 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, } else { addr = BCMA_CORE_PCI_PCICFG0; addr |= (func << 8); - addr |= (off & 0xfc); + addr |= (off & 0xFC); val = pcicore_read32(pc, addr); } } else { @@ -184,7 +184,7 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, goto out; if (mips_busprobe32(val, mmio)) { - val = 0xffffffff; + val = 0xFFFFFFFF; goto unmap; } } @@ -280,7 +280,7 @@ static u8 __devinit bcma_find_pci_capability(struct bcma_drv_pci *pc, /* check for Header type 0 */ bcma_extpci_read_config(pc, dev, func, PCI_HEADER_TYPE, &byte_val, sizeof(u8)); - if ((byte_val & 0x7f) != PCI_HEADER_TYPE_NORMAL) + if ((byte_val & 0x7F) != PCI_HEADER_TYPE_NORMAL) return cap_ptr; /* check if the capability pointer field exists */ From c3e5d7181afb66657393066bccce0956fab09ab3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 9 Jan 2013 10:20:36 +0200 Subject: [PATCH 358/478] iwlwifi: audit single frames from AGG queue in RS The rate scaling won't treat the information in a frame with IEEE80211_TX_CTL_AMPDU set if IEEE80211_TX_STAT_AMPDU is cleared. But all the frames coming from an AGG tx queue have IEEE80211_TX_CTL_AMPDU set, and IEEE80211_TX_STAT_AMPDU is set only if the frame was sent in an AMPDU. This means that all the data in frames in AGG tx queues that aren't sent as an AMPDU is thrown away. This is even more harmful when in bad link conditions, the frames are sent in an AMPDU and then finally sent as single frame. So a lot of failures weren't reported and the rate scaling got stuck in high rates leading to very poor connectivity. Fix that by clearing IEEE80211_TX_CTL_AMPDU when the frame isn't part of an AMPDU. This bug was introduced by 2eb81a40aa521035ff9c8c8309e482dff523f8c9 iwlwifi: don't clear CTL_AMPDU on frame status This fix basically reverts the aforementioned commit. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/tx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index a790599fe2c2..31534f7c0548 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1079,6 +1079,8 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv, { u16 status = le16_to_cpu(tx_resp->status.status); + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags |= iwl_tx_status_to_mac80211(status); iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), From 1626e0fa740dec8665a973cf2349405cdfeb46dc Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 11 Jan 2013 14:34:25 +0100 Subject: [PATCH 359/478] mac80211: fix FT roaming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During FT roaming, wpa_supplicant attempts to set the key before association. This used to be rejected, but as a side effect of my commit 66e67e418908442389d3a9e ("mac80211: redesign auth/assoc") the key was accepted causing hardware crypto to not be used for it as the station isn't added to the driver yet. It would be possible to accept the key and then add it to the driver when the station has been added. However, this may run into issues with drivers using the state- based station adding if they accept the key only after association like it used to be. For now, revert to the behaviour from before the auth and assoc change. Cc: stable@vger.kernel.org Reported-by: Cédric Debarge Tested-by: Cédric Debarge Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 47e0aca614b7..516fbc96feff 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -164,7 +164,17 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, sta = sta_info_get(sdata, mac_addr); else sta = sta_info_get_bss(sdata, mac_addr); - if (!sta) { + /* + * The ASSOC test makes sure the driver is ready to + * receive the key. When wpa_supplicant has roamed + * using FT, it attempts to set the key before + * association has completed, this rejects that attempt + * so it will set the key again after assocation. + * + * TODO: accept the key if we have a station entry and + * add it to the device after the station. + */ + if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) { ieee80211_key_free(sdata->local, key); err = -ENOENT; goto out_unlock; From aacde9ee45225f7e0b90960f479aef83c66bfdc0 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 20 Dec 2012 14:41:18 +0100 Subject: [PATCH 360/478] mac80211: synchronize scan off/on-channel and PS states Since: commit b23b025fe246f3acc2988eb6d400df34c27cb8ae Author: Ben Greear Date: Fri Feb 4 11:54:17 2011 -0800 mac80211: Optimize scans on current operating channel. we do not disable PS while going back to operational channel (on ieee80211_scan_state_suspend) and deffer that until scan finish. But since we are allowed to send frames, we can send a frame to AP without PM bit set, so disable PS on AP side. Then when we switch to off-channel (in ieee80211_scan_state_resume) we do not enable PS. Hence we are off-channel with PS disabled, frames are not buffered by AP. To fix remove offchannel_ps_disable argument and always enable PS when going off-channel and disable it when going on-channel, like it was before. Cc: stable@vger.kernel.org # 2.6.39+ Signed-off-by: Stanislaw Gruszka Tested-by: Seth Forshee Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 6 ++---- net/mac80211/offchannel.c | 19 +++++++------------ net/mac80211/scan.c | 15 +++++---------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8563b9a5cac3..2ed065c09562 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1358,10 +1358,8 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); void ieee80211_sched_scan_stopped_work(struct work_struct *work); /* off-channel helpers */ -void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, - bool offchannel_ps_enable); -void ieee80211_offchannel_return(struct ieee80211_local *local, - bool offchannel_ps_disable); +void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local); +void ieee80211_offchannel_return(struct ieee80211_local *local); void ieee80211_roc_setup(struct ieee80211_local *local); void ieee80211_start_next_roc(struct ieee80211_local *local); void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index a5379aea7d09..a3ad4c3c80a3 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -102,8 +102,7 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) ieee80211_sta_reset_conn_monitor(sdata); } -void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, - bool offchannel_ps_enable) +void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; @@ -134,8 +133,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { netif_tx_stop_all_queues(sdata->dev); - if (offchannel_ps_enable && - (sdata->vif.type == NL80211_IFTYPE_STATION) && + if (sdata->vif.type == NL80211_IFTYPE_STATION && sdata->u.mgd.associated) ieee80211_offchannel_ps_enable(sdata); } @@ -143,8 +141,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, mutex_unlock(&local->iflist_mtx); } -void ieee80211_offchannel_return(struct ieee80211_local *local, - bool offchannel_ps_disable) +void ieee80211_offchannel_return(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; @@ -163,11 +160,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, continue; /* Tell AP we're back */ - if (offchannel_ps_disable && - sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.associated) - ieee80211_offchannel_ps_disable(sdata); - } + if (sdata->vif.type == NL80211_IFTYPE_STATION && + sdata->u.mgd.associated) + ieee80211_offchannel_ps_disable(sdata); if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { /* @@ -385,7 +380,7 @@ void ieee80211_sw_roc_work(struct work_struct *work) local->tmp_channel = NULL; ieee80211_hw_config(local, 0); - ieee80211_offchannel_return(local, true); + ieee80211_offchannel_return(local); } ieee80211_recalc_idle(local); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d59fc6818b1c..bf82e69d0601 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -292,7 +292,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, if (!was_hw_scan) { ieee80211_configure_filter(local); drv_sw_scan_complete(local); - ieee80211_offchannel_return(local, true); + ieee80211_offchannel_return(local); } ieee80211_recalc_idle(local); @@ -341,7 +341,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; - ieee80211_offchannel_stop_vifs(local, true); + ieee80211_offchannel_stop_vifs(local); ieee80211_configure_filter(local); @@ -678,12 +678,8 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local, local->scan_channel = NULL; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - /* - * Re-enable vifs and beaconing. Leave PS - * in off-channel state..will put that back - * on-channel at the end of scanning. - */ - ieee80211_offchannel_return(local, false); + /* disable PS */ + ieee80211_offchannel_return(local); *next_delay = HZ / 5; /* afterwards, resume scan & go to next channel */ @@ -693,8 +689,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local, static void ieee80211_scan_state_resume(struct ieee80211_local *local, unsigned long *next_delay) { - /* PS already is in off-channel mode */ - ieee80211_offchannel_stop_vifs(local, false); + ieee80211_offchannel_stop_vifs(local); if (local->ops->flush) { drv_flush(local, false); From b4a7ff75ba3545b061d4fe63f0bb9136ccfe8b19 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 13 Jan 2013 23:10:26 +0100 Subject: [PATCH 361/478] mac80211: fix monitor mode injection Channel contexts are not always used with monitor interfaces. If no channel context is set, use the oper channel, otherwise tx fails. Signed-off-by: Felix Fietkau [check local->use_chanctx] Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e9eadc40c09c..467c1d1b66f2 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1673,10 +1673,13 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, chanctx_conf = rcu_dereference(tmp_sdata->vif.chanctx_conf); } - if (!chanctx_conf) - goto fail_rcu; - chan = chanctx_conf->def.chan; + if (chanctx_conf) + chan = chanctx_conf->def.chan; + else if (!local->use_chanctx) + chan = local->_oper_channel; + else + goto fail_rcu; /* * Frame injection is not allowed if beaconing is not allowed From 0ae997dc75efb60d47f0c71890f1e972f7d462c1 Mon Sep 17 00:00:00 2001 From: Yacine Belkadi Date: Sat, 12 Jan 2013 13:54:14 +0100 Subject: [PATCH 362/478] {cfg,mac}80211.h: fix some kernel-doc warnings When building the 80211 DocBook, scripts/kernel-doc reports the following type of warnings: Warning(include/net/cfg80211.h:334): No description found for return value of 'cfg80211_get_chandef_type' These warnings are only reported when scripts/kernel-doc runs in verbose mode. To fix these use "Return:" to describe function return values. Signed-off-by: Yacine Belkadi [adjust for freq_reg_info() change] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 161 ++++++++++++++++++++++++----------------- include/net/mac80211.h | 74 +++++++++++++++---- 2 files changed, 154 insertions(+), 81 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4275127da05a..3d8717a0d3b2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -326,7 +326,7 @@ struct cfg80211_chan_def { * cfg80211_get_chandef_type - return old channel type from chandef * @chandef: the channel definition * - * Returns the old channel type (NOHT, HT20, HT40+/-) from a given + * Return: The old channel type (NOHT, HT20, HT40+/-) from a given * chandef, which must have a bandwidth allowing this conversion. */ static inline enum nl80211_channel_type @@ -364,7 +364,7 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, * @chandef1: first channel definition * @chandef2: second channel definition * - * Returns %true if the channels defined by the channel definitions are + * Return: %true if the channels defined by the channel definitions are * identical, %false otherwise. */ static inline bool @@ -382,7 +382,7 @@ cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1, * @chandef1: first channel definition * @chandef2: second channel definition * - * Returns %NULL if the given channel definitions are incompatible, + * Return: %NULL if the given channel definitions are incompatible, * chandef1 or chandef2 otherwise. */ const struct cfg80211_chan_def * @@ -392,6 +392,7 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1, /** * cfg80211_chandef_valid - check if a channel definition is valid * @chandef: the channel definition to check + * Return: %true if the channel definition is valid. %false otherwise. */ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef); @@ -399,7 +400,8 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef); * cfg80211_chandef_usable - check if secondary channels can be used * @wiphy: the wiphy to validate against * @chandef: the channel definition to check - * @prohibited_flags: the regulatory chanenl flags that must not be set + * @prohibited_flags: the regulatory channel flags that must not be set + * Return: %true if secondary channels are usable. %false otherwise. */ bool cfg80211_chandef_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, @@ -1266,7 +1268,7 @@ struct cfg80211_bss { * * Note that the return value is an RCU-protected pointer, so * rcu_read_lock() must be held when calling this function. - * Returns %NULL if not found. + * Return: %NULL if not found. */ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); @@ -2409,6 +2411,7 @@ static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) * wiphy_priv - return priv from wiphy * * @wiphy: the wiphy whose priv pointer to return + * Return: The priv of @wiphy. */ static inline void *wiphy_priv(struct wiphy *wiphy) { @@ -2420,6 +2423,7 @@ static inline void *wiphy_priv(struct wiphy *wiphy) * priv_to_wiphy - return the wiphy containing the priv * * @priv: a pointer previously returned by wiphy_priv + * Return: The wiphy of @priv. */ static inline struct wiphy *priv_to_wiphy(void *priv) { @@ -2442,6 +2446,7 @@ static inline void set_wiphy_dev(struct wiphy *wiphy, struct device *dev) * wiphy_dev - get wiphy dev pointer * * @wiphy: The wiphy whose device struct to look up + * Return: The dev of @wiphy. */ static inline struct device *wiphy_dev(struct wiphy *wiphy) { @@ -2452,6 +2457,7 @@ static inline struct device *wiphy_dev(struct wiphy *wiphy) * wiphy_name - get wiphy name * * @wiphy: The wiphy whose name to return + * Return: The name of @wiphy. */ static inline const char *wiphy_name(const struct wiphy *wiphy) { @@ -2467,8 +2473,8 @@ static inline const char *wiphy_name(const struct wiphy *wiphy) * Create a new wiphy and associate the given operations with it. * @sizeof_priv bytes are allocated for private use. * - * The returned pointer must be assigned to each netdev's - * ieee80211_ptr for proper operation. + * Return: A pointer to the new wiphy. This pointer must be + * assigned to each netdev's ieee80211_ptr for proper operation. */ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv); @@ -2477,7 +2483,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv); * * @wiphy: The wiphy to register. * - * Returns a non-negative wiphy index or a negative error code. + * Return: A non-negative wiphy index or a negative error code. */ extern int wiphy_register(struct wiphy *wiphy); @@ -2626,6 +2632,7 @@ static inline u8 *wdev_address(struct wireless_dev *wdev) * wdev_priv - return wiphy priv from wireless_dev * * @wdev: The wireless device whose wiphy's priv pointer to return + * Return: The wiphy priv of @wdev. */ static inline void *wdev_priv(struct wireless_dev *wdev) { @@ -2643,12 +2650,14 @@ static inline void *wdev_priv(struct wireless_dev *wdev) * ieee80211_channel_to_frequency - convert channel number to frequency * @chan: channel number * @band: band, necessary due to channel number overlap + * Return: The corresponding frequency (in MHz), or 0 if the conversion failed. */ extern int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band); /** * ieee80211_frequency_to_channel - convert frequency to channel number * @freq: center frequency + * Return: The corresponding channel, or 0 if the conversion failed. */ extern int ieee80211_frequency_to_channel(int freq); @@ -2665,6 +2674,7 @@ extern struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, * ieee80211_get_channel - get channel struct from wiphy for specified frequency * @wiphy: the struct wiphy to get the channel for * @freq: the center frequency of the channel + * Return: The channel struct from @wiphy at @freq. */ static inline struct ieee80211_channel * ieee80211_get_channel(struct wiphy *wiphy, int freq) @@ -2679,10 +2689,10 @@ ieee80211_get_channel(struct wiphy *wiphy, int freq) * @basic_rates: bitmap of basic rates * @bitrate: the bitrate for which to find the basic rate * - * This function returns the basic rate corresponding to a given - * bitrate, that is the next lower bitrate contained in the basic - * rate map, which is, for this function, given as a bitmap of - * indices of rates in the band's bitrate table. + * Return: The basic rate corresponding to a given bitrate, that + * is the next lower bitrate contained in the basic rate map, + * which is, for this function, given as a bitmap of indices of + * rates in the band's bitrate table. */ struct ieee80211_rate * ieee80211_get_response_rate(struct ieee80211_supported_band *sband, @@ -2775,18 +2785,21 @@ extern const unsigned char bridge_tunnel_header[6]; /** * ieee80211_get_hdrlen_from_skb - get header length from data * - * Given an skb with a raw 802.11 header at the data pointer this function - * returns the 802.11 header length in bytes (not including encryption - * headers). If the data in the sk_buff is too short to contain a valid 802.11 - * header the function returns 0. - * * @skb: the frame + * + * Given an skb with a raw 802.11 header at the data pointer this function + * returns the 802.11 header length. + * + * Return: The 802.11 header length in bytes (not including encryption + * headers). Or 0 if the data in the sk_buff is too short to contain a valid + * 802.11 header. */ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); /** * ieee80211_hdrlen - get header length in bytes from frame control * @fc: frame control field in little-endian format + * Return: The header length in bytes. */ unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc); @@ -2794,7 +2807,7 @@ unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc); * ieee80211_get_mesh_hdrlen - get mesh extension header length * @meshhdr: the mesh extension header, only the flags field * (first byte) will be accessed - * Returns the length of the extension header, which is always at + * Return: The length of the extension header, which is always at * least 6 bytes and at most 18 if address 5 and 6 are present. */ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr); @@ -2812,6 +2825,7 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr); * @skb: the 802.11 data frame * @addr: the device MAC address * @iftype: the virtual interface type + * Return: 0 on success. Non-zero on error. */ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype); @@ -2823,6 +2837,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, * @iftype: the virtual interface type * @bssid: the network bssid (used only for iftype STATION and ADHOC) * @qos: build 802.11 QoS data frame + * Return: 0 on success, or a negative error code. */ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype, u8 *bssid, bool qos); @@ -2850,6 +2865,7 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, /** * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame * @skb: the data frame + * Return: The 802.1p/1d tag. */ unsigned int cfg80211_classify8021d(struct sk_buff *skb); @@ -2860,12 +2876,13 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb); * @ies: data consisting of IEs * @len: length of data * - * This function will return %NULL if the element ID could - * not be found or if the element is invalid (claims to be - * longer than the given data), or a pointer to the first byte - * of the requested element, that is the byte containing the - * element ID. There are no checks on the element length - * other than having to fit into the given data. + * Return: %NULL if the element ID could not be found or if + * the element is invalid (claims to be longer than the given + * data), or a pointer to the first byte of the requested + * element, that is the byte containing the element ID. + * + * Note: There are no checks on the element length other than + * having to fit into the given data. */ const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len); @@ -2877,12 +2894,13 @@ const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len); * @ies: data consisting of IEs * @len: length of data * - * This function will return %NULL if the vendor specific element ID - * could not be found or if the element is invalid (claims to be - * longer than the given data), or a pointer to the first byte - * of the requested element, that is the byte containing the - * element ID. There are no checks on the element length - * other than having to fit into the given data. + * Return: %NULL if the vendor specific element ID could not be found or if the + * element is invalid (claims to be longer than the given data), or a pointer to + * the first byte of the requested element, that is the byte containing the + * element ID. + * + * Note: There are no checks on the element length other than having to fit into + * the given data. */ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, const u8 *ies, int len); @@ -2915,6 +2933,8 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, * * Drivers should check the return value, its possible you can get * an -ENOMEM. + * + * Return: 0 on success. -ENOMEM. */ extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2); @@ -2944,13 +2964,13 @@ extern void wiphy_apply_custom_regulatory( * it wants to follow we respect that unless a country IE has been received * and processed already. * - * When an error occurs, for example if no rule can be found, the return value - * is encoded using ERR_PTR(). Use IS_ERR() to check and PTR_ERR() to obtain - * the numeric return value. The numeric return value will be -ERANGE if we - * determine the given center_freq does not even have a regulatory rule for a - * frequency range in the center_freq's band. See freq_in_rule_band() for our - * current definition of a band -- this is purely subjective and right now it's - * 802.11 specific. + * Return: A valid pointer, or, when an error occurs, for example if no rule + * can be found, the return value is encoded using ERR_PTR(). Use IS_ERR() to + * check and PTR_ERR() to obtain the numeric return value. The numeric return + * value will be -ERANGE if we determine the given center_freq does not even + * have a regulatory rule for a frequency range in the center_freq's band. + * See freq_in_rule_band() for our current definition of a band -- this is + * purely subjective and right now it's 802.11 specific. */ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, u32 center_freq); @@ -3000,7 +3020,8 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy); * This informs cfg80211 that BSS information was found and * the BSS should be updated/added. * - * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()! + * Return: A referenced struct, must be released with cfg80211_put_bss()! + * Or %NULL on error. */ struct cfg80211_bss * __must_check cfg80211_inform_bss_frame(struct wiphy *wiphy, @@ -3025,7 +3046,8 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, * This informs cfg80211 that BSS information was found and * the BSS should be updated/added. * - * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()! + * Return: A referenced struct, must be released with cfg80211_put_bss()! + * Or %NULL on error. */ struct cfg80211_bss * __must_check cfg80211_inform_bss(struct wiphy *wiphy, @@ -3302,16 +3324,18 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy); * the testmode command. Since it is intended for a reply, calling * it outside of the @testmode_cmd operation is invalid. * - * The returned skb (or %NULL if any errors happen) is pre-filled - * with the wiphy index and set up in a way that any data that is - * put into the skb (with skb_put(), nla_put() or similar) will end - * up being within the %NL80211_ATTR_TESTDATA attribute, so all that - * needs to be done with the skb is adding data for the corresponding - * userspace tool which can then read that data out of the testdata - * attribute. You must not modify the skb in any other way. + * The returned skb is pre-filled with the wiphy index and set up in + * a way that any data that is put into the skb (with skb_put(), + * nla_put() or similar) will end up being within the + * %NL80211_ATTR_TESTDATA attribute, so all that needs to be done + * with the skb is adding data for the corresponding userspace tool + * which can then read that data out of the testdata attribute. You + * must not modify the skb in any other way. * * When done, call cfg80211_testmode_reply() with the skb and return * its error code as the result of the @testmode_cmd operation. + * + * Return: An allocated and pre-filled skb. %NULL if any errors happen. */ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen); @@ -3321,11 +3345,12 @@ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, * @skb: The skb, must have been allocated with * cfg80211_testmode_alloc_reply_skb() * - * Returns an error code or 0 on success, since calling this - * function will usually be the last thing before returning - * from the @testmode_cmd you should return the error code. - * Note that this function consumes the skb regardless of the - * return value. + * Since calling this function will usually be the last thing + * before returning from the @testmode_cmd you should return + * the error code. Note that this function consumes the skb + * regardless of the return value. + * + * Return: An error code or 0 on success. */ int cfg80211_testmode_reply(struct sk_buff *skb); @@ -3339,14 +3364,16 @@ int cfg80211_testmode_reply(struct sk_buff *skb); * This function allocates and pre-fills an skb for an event on the * testmode multicast group. * - * The returned skb (or %NULL if any errors happen) is set up in the - * same way as with cfg80211_testmode_alloc_reply_skb() but prepared - * for an event. As there, you should simply add data to it that will - * then end up in the %NL80211_ATTR_TESTDATA attribute. Again, you must - * not modify the skb in any other way. + * The returned skb is set up in the same way as with + * cfg80211_testmode_alloc_reply_skb() but prepared for an event. As + * there, you should simply add data to it that will then end up in the + * %NL80211_ATTR_TESTDATA attribute. Again, you must not modify the skb + * in any other way. * * When done filling the skb, call cfg80211_testmode_event() with the * skb to send the event. + * + * Return: An allocated and pre-filled skb. %NULL if any errors happen. */ struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp); @@ -3527,13 +3554,13 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, * @len: length of the frame data * @gfp: context flags * - * Returns %true if a user space application has registered for this frame. + * This function is called whenever an Action frame is received for a station + * mode interface, but is not processed in kernel. + * + * Return: %true if a user space application has registered for this frame. * For action frames, that makes it responsible for rejecting unrecognized * action frames; %false otherwise, in which case for action frames the * driver is responsible for rejecting the frame. - * - * This function is called whenever an Action frame is received for a station - * mode interface, but is not processed in kernel. */ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp); @@ -3625,7 +3652,7 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, * This function is used in AP mode (only!) to inform userspace that * a spurious class 3 frame was received, to be able to deauth the * sender. - * Returns %true if the frame was passed to userspace (or this failed + * Return: %true if the frame was passed to userspace (or this failed * for a reason other than not having a subscription.) */ bool cfg80211_rx_spurious_frame(struct net_device *dev, @@ -3641,7 +3668,7 @@ bool cfg80211_rx_spurious_frame(struct net_device *dev, * an associated station sent a 4addr frame but that wasn't expected. * It is allowed and desirable to send this event only once for each * station to avoid event flooding. - * Returns %true if the frame was passed to userspace (or this failed + * Return: %true if the frame was passed to userspace (or this failed * for a reason other than not having a subscription.) */ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, @@ -3679,8 +3706,8 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, * @wiphy: the wiphy * @chandef: the channel definition * - * This function returns true if there is no secondary channel or the secondary - * channel(s) can be used for beaconing (i.e. is not a radar channel etc.) + * Return: %true if there is no secondary channel or the secondary channel(s) + * can be used for beaconing (i.e. is not a radar channel etc.) */ bool cfg80211_reg_can_beacon(struct wiphy *wiphy, struct cfg80211_chan_def *chandef); @@ -3750,9 +3777,9 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev); * The function finds a given P2P attribute in the (vendor) IEs and * copies its contents to the given buffer. * - * The return value is a negative error code (-%EILSEQ or -%ENOENT) if - * the data is malformed or the attribute can't be found (respectively), - * or the length of the found attribute (which can be zero). + * Return: A negative error code (-%EILSEQ or -%ENOENT) if the data is + * malformed or the attribute can't be found (respectively), or the + * length of the found attribute (which can be zero). */ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, enum ieee80211_p2p_attr_id attr, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 23daed3c78ed..79bc8709e83b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1522,6 +1522,8 @@ struct ieee80211_hw { * structure can then access it via hw->priv. Note that mac802111 drivers should * not use wiphy_priv() to try to get their private driver structure as this * is already used internally by mac80211. + * + * Return: The mac80211 driver hw struct of @wiphy. */ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy); @@ -2673,6 +2675,8 @@ struct ieee80211_ops { * * @priv_data_len: length of private data * @ops: callbacks for this device + * + * Return: A pointer to the new hardware device, or %NULL on error. */ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, const struct ieee80211_ops *ops); @@ -2685,6 +2689,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, * need to fill the contained wiphy's information. * * @hw: the device to register as returned by ieee80211_alloc_hw() + * + * Return: 0 on success. An error code otherwise. */ int ieee80211_register_hw(struct ieee80211_hw *hw); @@ -2731,6 +2737,8 @@ extern char *__ieee80211_create_tpt_led_trigger( * of the trigger so you can automatically link the LED device. * * @hw: the hardware to get the LED trigger name for + * + * Return: The name of the LED trigger. %NULL if not configured for LEDs. */ static inline char *ieee80211_get_tx_led_name(struct ieee80211_hw *hw) { @@ -2750,6 +2758,8 @@ static inline char *ieee80211_get_tx_led_name(struct ieee80211_hw *hw) * of the trigger so you can automatically link the LED device. * * @hw: the hardware to get the LED trigger name for + * + * Return: The name of the LED trigger. %NULL if not configured for LEDs. */ static inline char *ieee80211_get_rx_led_name(struct ieee80211_hw *hw) { @@ -2769,6 +2779,8 @@ static inline char *ieee80211_get_rx_led_name(struct ieee80211_hw *hw) * of the trigger so you can automatically link the LED device. * * @hw: the hardware to get the LED trigger name for + * + * Return: The name of the LED trigger. %NULL if not configured for LEDs. */ static inline char *ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) { @@ -2788,6 +2800,8 @@ static inline char *ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) * of the trigger so you can automatically link the LED device. * * @hw: the hardware to get the LED trigger name for + * + * Return: The name of the LED trigger. %NULL if not configured for LEDs. */ static inline char *ieee80211_get_radio_led_name(struct ieee80211_hw *hw) { @@ -2805,9 +2819,10 @@ static inline char *ieee80211_get_radio_led_name(struct ieee80211_hw *hw) * @blink_table: the blink table -- needs to be ordered by throughput * @blink_table_len: size of the blink table * - * This function returns %NULL (in case of error, or if no LED - * triggers are configured) or the name of the new trigger. - * This function must be called before ieee80211_register_hw(). + * Return: %NULL (in case of error, or if no LED triggers are + * configured) or the name of the new trigger. + * + * Note: This function must be called before ieee80211_register_hw(). */ static inline char * ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, unsigned int flags, @@ -2940,10 +2955,10 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw, * Calls to this function for a single hardware must be synchronized against * each other. * - * The function returns -EINVAL when the requested PS mode is already set. - * * @sta: currently connected sta * @start: start or stop PS + * + * Return: 0 on success. -EINVAL when the requested PS mode is already set. */ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start); @@ -2957,6 +2972,8 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start); * * @sta: currently connected sta * @start: start or stop PS + * + * Return: Like ieee80211_sta_ps_transition(). */ static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, bool start) @@ -3094,6 +3111,8 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets); * according to the current DTIM parameters/TIM bitmap. * * The driver is responsible for freeing the returned skb. + * + * Return: The beacon template. %NULL on error. */ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -3105,6 +3124,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, * @vif: &struct ieee80211_vif pointer from the add_interface callback. * * See ieee80211_beacon_get_tim(). + * + * Return: See ieee80211_beacon_get_tim(). */ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) @@ -3121,6 +3142,8 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, * hardware. The destination address should be set by the caller. * * Can only be called in AP mode. + * + * Return: The Probe Response template. %NULL on error. */ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif); @@ -3136,6 +3159,8 @@ struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, * * Note: Caller (or hardware) is responsible for setting the * &IEEE80211_FCTL_PM bit. + * + * Return: The PS Poll template. %NULL on error. */ struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif); @@ -3151,6 +3176,8 @@ struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, * * Note: Caller (or hardware) is responsible for setting the * &IEEE80211_FCTL_PM bit as well as Duration and Sequence Control fields. + * + * Return: The nullfunc template. %NULL on error. */ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif); @@ -3165,6 +3192,8 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, * * Creates a Probe Request template which can, for example, be uploaded to * hardware. + * + * Return: The Probe Request template. %NULL on error. */ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -3200,6 +3229,8 @@ void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * If the RTS is generated in firmware, but the host system must provide * the duration field, the low-level driver uses this function to receive * the duration field value in little-endian byteorder. + * + * Return: The duration. */ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, size_t frame_len, @@ -3235,6 +3266,8 @@ void ieee80211_ctstoself_get(struct ieee80211_hw *hw, * If the CTS-to-self is generated in firmware, but the host system must provide * the duration field, the low-level driver uses this function to receive * the duration field value in little-endian byteorder. + * + * Return: The duration. */ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -3251,6 +3284,8 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, * * Calculate the duration field of some generic frame, given its * length and transmission rate (in 100kbps). + * + * Return: The duration. */ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -3267,9 +3302,10 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, * hardware/firmware does not implement buffering of broadcast/multicast * frames when power saving is used, 802.11 code buffers them in the host * memory. The low-level driver uses this function to fetch next buffered - * frame. In most cases, this is used when generating beacon frame. This - * function returns a pointer to the next buffered skb or NULL if no more - * buffered frames are available. + * frame. In most cases, this is used when generating beacon frame. + * + * Return: A pointer to the next buffered skb or NULL if no more buffered + * frames are available. * * Note: buffered frames are returned only after DTIM beacon frame was * generated with ieee80211_beacon_get() and the low-level driver must thus @@ -3449,6 +3485,8 @@ void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue); * @queue: queue number (counted from zero). * * Drivers should use this function instead of netif_stop_queue. + * + * Return: %true if the queue is stopped. %false otherwise. */ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue); @@ -3646,7 +3684,9 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, const u8 *ra, * @vif: virtual interface to look for station on * @addr: station's address * - * This function must be called under RCU lock and the + * Return: The station, if found. %NULL otherwise. + * + * Note: This function must be called under RCU lock and the * resulting pointer is only valid under RCU lock as well. */ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, @@ -3659,7 +3699,9 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif, * @addr: remote station's address * @localaddr: local address (vif->sdata->vif.addr). Use NULL for 'any'. * - * This function must be called under RCU lock and the + * Return: The station, if found. %NULL otherwise. + * + * Note: This function must be called under RCU lock and the * resulting pointer is only valid under RCU lock as well. * * NOTE: You may pass NULL for localaddr, but then you will just get @@ -3789,7 +3831,9 @@ void ieee80211_iter_chan_contexts_atomic( * information. This function must only be called from within the * .bss_info_changed callback function and only in managed mode. The function * is only useful when the interface is associated, otherwise it will return - * NULL. + * %NULL. + * + * Return: The Probe Request template. %NULL on error. */ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif); @@ -4136,12 +4180,14 @@ void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); /** - * ieee80211_ave_rssi - report the average rssi for the specified interface + * ieee80211_ave_rssi - report the average RSSI for the specified interface * * @vif: the specified virtual interface * - * This function return the average rssi value for the requested interface. - * It assumes that the given vif is valid. + * Note: This function assumes that the given vif is valid. + * + * Return: The average RSSI value for the requested interface, or 0 if not + * applicable. */ int ieee80211_ave_rssi(struct ieee80211_vif *vif); From d888130a024d1d1e8b2c3d0c9841a218fc0aed9e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 10 Jan 2013 23:55:33 +0100 Subject: [PATCH 363/478] mac80211: fix resume from WoWLAN My commit 529ba6e9313dbe60dab7e72c6fdf647a012e9f5b ("mac80211: clean up association better in suspend") introduced a bug when resuming from WoWLAN when a device reset is desired. This case must not use the suspend_bss_conf as it hasn't been stored. Signed-off-by: Johannes Berg --- net/mac80211/util.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1b9420730d8c..0ad51e14f3c8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1358,8 +1358,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct ieee80211_chanctx *ctx; struct sta_info *sta; int res, i; - #ifdef CONFIG_PM + bool reconfig_due_to_wowlan = false; + if (local->suspended) local->resuming = true; @@ -1377,6 +1378,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) * res is 1, which means the driver requested * to go through a regular reset on wakeup. */ + reconfig_due_to_wowlan = true; } #endif /* everything else happens only if HW was up & running */ @@ -1527,7 +1529,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_TXPOWER; #ifdef CONFIG_PM - if (local->resuming) + if (local->resuming && !reconfig_due_to_wowlan) sdata->vif.bss_conf = sdata->suspend_bss_conf; #endif From 8f21b0adfe95907926da1bb0bcd3382b13d0143d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 11 Jan 2013 00:28:01 +0100 Subject: [PATCH 364/478] mac80211: call restart complete at wowlan resume time When the driver's resume function can't completely restore the configuration in the device, it returns 1 from the callback which will be treated like a HW restart request, but done directly. In this case, also call the driver's restart_complete() function so it can finish the reconfiguration there. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 ++++- net/mac80211/util.c | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 79bc8709e83b..3037f49e51c8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2488,7 +2488,10 @@ enum ieee80211_rate_control_changed { * * @restart_complete: Called after a call to ieee80211_restart_hw(), when the * reconfiguration has completed. This can help the driver implement the - * reconfiguration step. This callback may sleep. + * reconfiguration step. Also called when reconfiguring because the + * driver's resume function returned 1, as this is just like an "inline" + * hardware restart. This callback may sleep. + * */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0ad51e14f3c8..7519018ff71a 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1358,9 +1358,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct ieee80211_chanctx *ctx; struct sta_info *sta; int res, i; -#ifdef CONFIG_PM bool reconfig_due_to_wowlan = false; +#ifdef CONFIG_PM if (local->suspended) local->resuming = true; @@ -1656,10 +1656,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) * If this is for hw restart things are still running. * We may want to change that later, however. */ - if (!local->suspended) { + if (!local->suspended || reconfig_due_to_wowlan) drv_restart_complete(local); + + if (!local->suspended) return 0; - } #ifdef CONFIG_PM /* first set suspended false, then resuming */ From ee1f668136b2fb6640ee2d54c2a525ea41f98211 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Thu, 10 Jan 2013 23:31:54 +0800 Subject: [PATCH 365/478] mac80211: allow disable power save in mesh Allow mesh interface to disable the power save which is by default turn on in certain chipset. Testing with 2 units of ZCN-1523H-5-16 featuring AR9280 chipset which have power save enabled by default. Constant reset if the average signal of the peer mesh STA is below -80 dBm and power save is enabled. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7d290bce0927..817992b51fe7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2232,7 +2232,8 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - if (sdata->vif.type != NL80211_IFTYPE_STATION) + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) From ea54fba20985b7a0cb8e1c8e2760a0865ee47763 Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:48 +0100 Subject: [PATCH 366/478] nl80211: add range checks to mesh parameters Ranges are taken from IEEE 802.11-2012, common sense or current implementation requirements. Signed-off-by: Marco Porsch Acked-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 79 +++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9bd8340af999..ceb27fda9ec5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3981,13 +3981,15 @@ static int nl80211_parse_mesh_config(struct genl_info *info, struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; u32 mask = 0; -#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ -do {\ - if (table[attr_num]) {\ - cfg->param = nla_fn(table[attr_num]); \ - mask |= (1 << (attr_num - 1)); \ - } \ -} while (0);\ +#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \ +do { \ + if (tb[attr]) { \ + if (fn(tb[attr]) < min || fn(tb[attr]) > max) \ + return -EINVAL; \ + cfg->param = fn(tb[attr]); \ + mask |= (1 << (attr - 1)); \ + } \ +} while (0) if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) @@ -4002,81 +4004,88 @@ do {\ BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); /* Fill in the params struct */ - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255, mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255, mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255, mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255, mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16, mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255, mask, NL80211_MESHCONF_TTL, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255, mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1, mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, + 1, 255, mask, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255, mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535, mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535, mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, + 1, 65535, mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, - mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + 1, 65535, mask, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, - mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + 1, 65535, mask, + NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPnetDiameterTraversalTime, mask, + dot11MeshHWMPnetDiameterTraversalTime, + 1, 65535, mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask, - NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask, - NL80211_MESHCONF_HWMP_RANN_INTERVAL, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4, + mask, NL80211_MESHCONF_HWMP_ROOTMODE, + nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535, + mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshGateAnnouncementProtocol, mask, - NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + dot11MeshGateAnnouncementProtocol, 0, 1, + mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1, mask, NL80211_MESHCONF_FORWARDING, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, 1, 255, mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16, mask, NL80211_MESHCONF_HT_OPMODE, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout, - mask, + 1, 65535, mask, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535, mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPconfirmationInterval, mask, + dot11MeshHWMPconfirmationInterval, + 1, 65535, mask, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, nla_get_u16); if (mask_out) From 1617bab8db81427392d424f2c7ffb5a3282c417c Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:49 +0100 Subject: [PATCH 367/478] mac80211: update mesh peer link counter during userspace peering The established peer link count is indicated in mesh beacons and used for other internal tasks. Previously it was not updated when authenticated peering is performed in userspace. Signed-off-by: Marco Porsch Acked-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 22 +++++++++++++++++++--- net/mac80211/mesh.h | 14 ++++++++++++++ net/mac80211/mesh_plink.c | 14 -------------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 817992b51fe7..8a91dd22d571 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1243,18 +1243,33 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH - if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) { + u32 changed = 0; + switch (params->plink_state) { - case NL80211_PLINK_LISTEN: case NL80211_PLINK_ESTAB: + if (sta->plink_state != NL80211_PLINK_ESTAB) + changed = mesh_plink_inc_estab_count( + sdata); + sta->plink_state = params->plink_state; + break; + case NL80211_PLINK_LISTEN: case NL80211_PLINK_BLOCKED: + case NL80211_PLINK_OPN_SNT: + case NL80211_PLINK_OPN_RCVD: + case NL80211_PLINK_CNF_RCVD: + case NL80211_PLINK_HOLDING: + if (sta->plink_state == NL80211_PLINK_ESTAB) + changed = mesh_plink_dec_estab_count( + sdata); sta->plink_state = params->plink_state; break; default: /* nothing */ break; } - else + ieee80211_bss_info_change_notify(sdata, changed); + } else { switch (params->plink_action) { case PLINK_ACTION_OPEN: mesh_plink_open(sta); @@ -1263,6 +1278,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, mesh_plink_block(sta); break; } + } #endif } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 84c28c6101cd..c07f6fc63dc6 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -307,6 +307,20 @@ extern int mesh_paths_generation; #ifdef CONFIG_MAC80211_MESH extern int mesh_allocated; +static inline +u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) +{ + atomic_inc(&sdata->u.mesh.estab_plinks); + return mesh_accept_plinks_update(sdata); +} + +static inline +u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) +{ + atomic_dec(&sdata->u.mesh.estab_plinks); + return mesh_accept_plinks_update(sdata); +} + static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata) { return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks - diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 4b274e9c91a5..9e0416696a83 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -41,20 +41,6 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode action, u8 *da, __le16 llid, __le16 plid, __le16 reason); -static inline -u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) -{ - atomic_inc(&sdata->u.mesh.estab_plinks); - return mesh_accept_plinks_update(sdata); -} - -static inline -u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) -{ - atomic_dec(&sdata->u.mesh.estab_plinks); - return mesh_accept_plinks_update(sdata); -} - /** * mesh_plink_fsm_restart - restart a mesh peer link finite state machine * From eac70c135def117849faa7cc8b7ccb941498085f Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:50 +0100 Subject: [PATCH 368/478] mac80211: move add_tim to subfunction This functions will be used for mesh beacons, too. Signed-off-by: Marco Porsch [some formatting fixes] Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e9eadc40c09c..0d7381679a5c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2261,9 +2261,8 @@ void ieee80211_tx_pending(unsigned long data) /* functions for drivers to get certain frames */ -static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, - struct ps_data *ps, - struct sk_buff *skb) +static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, + struct ps_data *ps, struct sk_buff *skb) { u8 *pos, *tim; int aid0 = 0; @@ -2325,6 +2324,31 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, } } +static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, + struct ps_data *ps, struct sk_buff *skb) +{ + struct ieee80211_local *local = sdata->local; + + /* + * Not very nice, but we want to allow the driver to call + * ieee80211_beacon_get() as a response to the set_tim() + * callback. That, however, is already invoked under the + * sta_lock to guarantee consistent and race-free update + * of the tim bitmap in mac80211 and the driver. + */ + if (local->tim_in_locked_section) { + __ieee80211_beacon_add_tim(sdata, ps, skb); + } else { + unsigned long flags; + + spin_lock_irqsave(&local->tim_lock, flags); + __ieee80211_beacon_add_tim(sdata, ps, skb); + spin_unlock_irqrestore(&local->tim_lock, flags); + } + + return 0; +} + struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 *tim_offset, u16 *tim_length) @@ -2369,22 +2393,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, memcpy(skb_put(skb, beacon->head_len), beacon->head, beacon->head_len); - /* - * Not very nice, but we want to allow the driver to call - * ieee80211_beacon_get() as a response to the set_tim() - * callback. That, however, is already invoked under the - * sta_lock to guarantee consistent and race-free update - * of the tim bitmap in mac80211 and the driver. - */ - if (local->tim_in_locked_section) { - ieee80211_beacon_add_tim(sdata, &ap->ps, skb); - } else { - unsigned long flags; - - spin_lock_irqsave(&local->tim_lock, flags); - ieee80211_beacon_add_tim(sdata, &ap->ps, skb); - spin_unlock_irqrestore(&local->tim_lock, flags); - } + ieee80211_beacon_add_tim(sdata, &ap->ps, skb); if (tim_offset) *tim_offset = beacon->head_len; From 9bdbf04db099c11bbbaea9dcea7465c508531fb8 Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:51 +0100 Subject: [PATCH 369/478] {cfg,nl,mac}80211: set beacon interval and DTIM period on mesh join Move the default mesh beacon interval and DTIM period to cfg80211 and make them accessible to nl80211. This enables setting both values when joining an MBSS. Previously the DTIM parameter was not set by mac80211 so the driver's default value was used. Signed-off-by: Marco Porsch Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ net/mac80211/cfg.c | 3 +++ net/mac80211/mesh.c | 1 - net/mac80211/mesh.h | 2 -- net/wireless/mesh.c | 5 +++++ net/wireless/nl80211.c | 15 +++++++++++++++ 6 files changed, 27 insertions(+), 3 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3d8717a0d3b2..516aded3697f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1036,6 +1036,8 @@ struct mesh_config { * @ie_len: length of vendor information elements * @is_authenticated: this mesh requires authentication * @is_secure: this mesh uses security + * @dtim_period: DTIM period to use + * @beacon_interval: beacon interval to use * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a] * * These parameters are fixed when the mesh is created. @@ -1051,6 +1053,8 @@ struct mesh_setup { u8 ie_len; bool is_authenticated; bool is_secure; + u8 dtim_period; + u16 beacon_interval; int mcast_rate[IEEE80211_NUM_BANDS]; }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8a91dd22d571..36aa65f495ff 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1666,6 +1666,9 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate, sizeof(setup->mcast_rate)); + sdata->vif.bss_conf.beacon_int = setup->beacon_interval; + sdata->vif.bss_conf.dtim_period = setup->dtim_period; + return 0; } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 245885841c8d..694e27376afa 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -625,7 +625,6 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ieee80211_queue_work(&local->hw, &sdata->work); sdata->vif.bss_conf.ht_operation_mode = ifmsh->mshcfg.ht_opmode; - sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(local, band); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index c07f6fc63dc6..aff301544c7f 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -191,8 +191,6 @@ struct mesh_rmc { #define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) -#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */ - #define MESH_PATH_EXPIRE (600 * HZ) /* Default maximum number of plinks per interface */ diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index f9d6ce5cfabb..0fe8ceb5444e 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -44,6 +44,9 @@ #define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50 +#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */ +#define MESH_DEFAULT_DTIM_PERIOD 2 + const struct mesh_config default_mesh_config = { .dot11MeshRetryTimeout = MESH_RET_T, .dot11MeshConfirmTimeout = MESH_CONF_T, @@ -79,6 +82,8 @@ const struct mesh_setup default_mesh_setup = { .ie = NULL, .ie_len = 0, .is_secure = false, + .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL, + .dtim_period = MESH_DEFAULT_DTIM_PERIOD, }; int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ceb27fda9ec5..d5842eb35aec 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6669,6 +6669,21 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) return -EINVAL; + if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { + setup.beacon_interval = + nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); + if (setup.beacon_interval < 10 || + setup.beacon_interval > 10000) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { + setup.dtim_period = + nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); + if (setup.dtim_period < 1 || setup.dtim_period > 100) + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_MESH_SETUP]) { /* parse additional setup parameters if given */ err = nl80211_parse_mesh_setup(info, &setup); From 3b1c5a5307fb5277f395efdcf330c064d79df07d Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:52 +0100 Subject: [PATCH 370/478] {cfg,nl}80211: mesh power mode primitives and userspace access Add the nl80211_mesh_power_mode enumeration which holds possible values for the mesh power mode. These modes are unknown, active, light sleep and deep sleep. Add power_mode entry to the mesh config structure to hold the user-configured default mesh power mode. This value will be used for new peer links. Add the dot11MeshAwakeWindowDuration value to the mesh config. The awake window is a duration in TU describing how long the STA will stay awake after transmitting its beacon in PS mode. Add access routines to: - get/set local link-specific power mode (STA) - get remote STA's link-specific power mode (STA) - get remote STA's non-peer power mode (STA) - get/set default mesh power mode (mesh config) - get/set mesh awake window duration (mesh config) All config changes may be done at mesh runtime and take effect immediately. Signed-off-by: Marco Porsch Signed-off-by: Ivan Bezyazychnyy Signed-off-by: Mike Krinkin [fix commit message line length, error handling in set station] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 21 ++++++++++++++++ include/uapi/linux/nl80211.h | 47 ++++++++++++++++++++++++++++++++++++ net/wireless/mesh.c | 3 +++ net/wireless/nl80211.c | 43 ++++++++++++++++++++++++++++++++- 4 files changed, 113 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 516aded3697f..d9f08f65f7a5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -610,6 +610,8 @@ enum station_parameters_apply_mask { * @sta_modify_mask: bitmap indicating which parameters changed * (for those that don't have a natural "no change" value), * see &enum station_parameters_apply_mask + * @local_pm: local link-specific mesh power save mode (no change when set + * to unknown) */ struct station_parameters { u8 *supported_rates; @@ -625,6 +627,7 @@ struct station_parameters { struct ieee80211_vht_cap *vht_capa; u8 uapsd_queues; u8 max_sp; + enum nl80211_mesh_power_mode local_pm; }; /** @@ -655,6 +658,9 @@ struct station_parameters { * @STATION_INFO_STA_FLAGS: @sta_flags filled * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled * @STATION_INFO_T_OFFSET: @t_offset filled + * @STATION_INFO_LOCAL_PM: @local_pm filled + * @STATION_INFO_PEER_PM: @peer_pm filled + * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -678,6 +684,9 @@ enum station_info_flags { STATION_INFO_STA_FLAGS = 1<<18, STATION_INFO_BEACON_LOSS_COUNT = 1<<19, STATION_INFO_T_OFFSET = 1<<20, + STATION_INFO_LOCAL_PM = 1<<21, + STATION_INFO_PEER_PM = 1<<22, + STATION_INFO_NONPEER_PM = 1<<23, }; /** @@ -791,6 +800,9 @@ struct sta_bss_parameters { * @sta_flags: station flags mask & values * @beacon_loss_count: Number of times beacon loss event has triggered. * @t_offset: Time offset of the station relative to this host. + * @local_pm: local mesh STA power save mode + * @peer_pm: peer mesh STA power save mode + * @nonpeer_pm: non-peer mesh STA power save mode */ struct station_info { u32 filled; @@ -820,6 +832,9 @@ struct station_info { u32 beacon_loss_count; s64 t_offset; + enum nl80211_mesh_power_mode local_pm; + enum nl80211_mesh_power_mode peer_pm; + enum nl80211_mesh_power_mode nonpeer_pm; /* * Note: Add a new enum station_info_flags value for each new field and @@ -995,6 +1010,10 @@ struct bss_parameters { * @dot11MeshHWMPconfirmationInterval: The minimum interval of time (in TUs) * during which a mesh STA can send only one Action frame containing * a PREQ element for root path confirmation. + * @power_mode: The default mesh power save mode which will be the initial + * setting for new peer links. + * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake + * after transmitting its beacon. */ struct mesh_config { u16 dot11MeshRetryTimeout; @@ -1022,6 +1041,8 @@ struct mesh_config { u32 dot11MeshHWMPactivePathToRootTimeout; u16 dot11MeshHWMProotInterval; u16 dot11MeshHWMPconfirmationInterval; + enum nl80211_mesh_power_mode power_mode; + u16 dot11MeshAwakeWindowDuration; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 547017100a30..6c4f703ae890 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1310,6 +1310,9 @@ enum nl80211_commands { * if not given in START_AP 0 is assumed, if not given in SET_BSS * no change is made. * + * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode + * defined in &enum nl80211_mesh_power_mode. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1580,6 +1583,8 @@ enum nl80211_attrs { NL80211_ATTR_P2P_CTWINDOW, NL80211_ATTR_P2P_OPPPS, + NL80211_ATTR_LOCAL_MESH_POWER_MODE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1838,6 +1843,10 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) + * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode + * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode + * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards + * non-peer STA * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1862,6 +1871,9 @@ enum nl80211_sta_info { NL80211_STA_INFO_STA_FLAGS, NL80211_STA_INFO_BEACON_LOSS, NL80211_STA_INFO_T_OFFSET, + NL80211_STA_INFO_LOCAL_PM, + NL80211_STA_INFO_PEER_PM, + NL80211_STA_INFO_NONPEER_PM, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -2252,6 +2264,34 @@ enum nl80211_mntr_flags { NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 }; +/** + * enum nl80211_mesh_power_mode - mesh power save modes + * + * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is + * not known or has not been set yet. + * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is + * in Awake state all the time. + * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will + * alternate between Active and Doze states, but will wake up for + * neighbor's beacons. + * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will + * alternate between Active and Doze states, but may not wake up + * for neighbor's beacons. + * + * @__NL80211_MESH_POWER_AFTER_LAST - internal use + * @NL80211_MESH_POWER_MAX - highest possible power save level + */ + +enum nl80211_mesh_power_mode { + NL80211_MESH_POWER_UNKNOWN, + NL80211_MESH_POWER_ACTIVE, + NL80211_MESH_POWER_LIGHT_SLEEP, + NL80211_MESH_POWER_DEEP_SLEEP, + + __NL80211_MESH_POWER_AFTER_LAST, + NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1 +}; + /** * enum nl80211_meshconf_params - mesh configuration parameters * @@ -2346,6 +2386,11 @@ enum nl80211_mntr_flags { * (in TUs) during which a mesh STA can send only one Action frame * containing a PREQ element for root path confirmation. * + * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links. + * type &enum nl80211_mesh_power_mode (u32) + * + * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs) + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2375,6 +2420,8 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, + NL80211_MESHCONF_POWER_MODE, + NL80211_MESHCONF_AWAKE_WINDOW, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 0fe8ceb5444e..55957a284f6c 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -46,6 +46,7 @@ #define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */ #define MESH_DEFAULT_DTIM_PERIOD 2 +#define MESH_DEFAULT_AWAKE_WINDOW 10 /* in 1024 us units (=TUs) */ const struct mesh_config default_mesh_config = { .dot11MeshRetryTimeout = MESH_RET_T, @@ -72,6 +73,8 @@ const struct mesh_config default_mesh_config = { .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT, .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL, .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL, + .power_mode = NL80211_MESH_POWER_ACTIVE, + .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW, }; const struct mesh_setup default_mesh_setup = { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d5842eb35aec..1a7a710fe9bf 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3001,6 +3001,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, sinfo->beacon_loss_count)) goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_LOCAL_PM) && + nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM, + sinfo->local_pm)) + goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_PEER_PM) && + nla_put_u32(msg, NL80211_STA_INFO_PEER_PM, + sinfo->peer_pm)) + goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_NONPEER_PM) && + nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM, + sinfo->nonpeer_pm)) + goto nla_put_failure; if (sinfo->filled & STATION_INFO_BSS_PARAM) { bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); if (!bss_param) @@ -3206,6 +3218,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) params.plink_state = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); + if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) { + enum nl80211_mesh_power_mode pm = nla_get_u32( + info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]); + + if (pm <= NL80211_MESH_POWER_UNKNOWN || + pm > NL80211_MESH_POWER_MAX) + return -EINVAL; + + params.local_pm = pm; + } + switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: @@ -3213,6 +3236,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* disallow mesh-specific things */ if (params.plink_action) return -EINVAL; + if (params.local_pm) + return -EINVAL; /* TDLS can't be set, ... */ if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) @@ -3265,6 +3290,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* disallow things sta doesn't support */ if (params.plink_action) return -EINVAL; + if (params.local_pm) + return -EINVAL; /* reject any changes other than AUTHORIZED */ if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) return -EINVAL; @@ -3922,7 +3949,11 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, cur_params.dot11MeshHWMProotInterval) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, - cur_params.dot11MeshHWMPconfirmationInterval)) + cur_params.dot11MeshHWMPconfirmationInterval) || + nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE, + cur_params.power_mode) || + nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW, + cur_params.dot11MeshAwakeWindowDuration)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); @@ -3961,6 +3992,8 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 }, + [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 }, + [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 }, }; static const struct nla_policy @@ -4088,6 +4121,14 @@ do { \ 1, 65535, mask, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode, + NL80211_MESH_POWER_ACTIVE, + NL80211_MESH_POWER_MAX, + mask, NL80211_MESHCONF_POWER_MODE, + nla_get_u32); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, + 0, 65535, mask, + NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); if (mask_out) *mask_out = mask; From 1c18f1452a772dfe884ed25677bddb3ecaf9c43a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 8 Jan 2013 10:17:27 +0100 Subject: [PATCH 371/478] nl80211: allow user-space to set address for P2P_DEVICE As per email discussion Jouni Malinen pointed out that: "P2P message exchanges can be executed on the current operating channel of any operation (both P2P and non-P2P station). These can be on 5 GHz and even on 60 GHz (so yes, you _can_ do GO Negotiation on 60 GHz). As an example, it would be possible to receive a GO Negotiation Request frame on a 5 GHz only radio and then to complete GO Negotiation on that band. This can happen both when connected to a P2P group (through client discoverability mechanism) and when connected to a legacy AP (assuming the station receive Probe Request frame from full scan in the beginning of P2P device discovery)." This means that P2P messages can be sent over different radio devices. However, these should use the same P2P device address so it should be able to provision this from user-space. This patch adds a parameter for this to struct vif_params which should only be used during creation of the P2P device interface. Cc: Jouni Malinen Cc: Greg Goldman Cc: Jithu Jance Signed-off-by: Arend van Spriel [add error checking] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ net/wireless/nl80211.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d9f08f65f7a5..d81e730962cc 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -281,9 +281,13 @@ struct ieee80211_supported_band { /** * struct vif_params - describes virtual interface parameters * @use_4addr: use 4-address frames + * @macaddr: address to use for this virtual interface. This will only + * be used for non-netdevice interfaces. If this parameter is set + * to zero address the driver may determine the address as needed. */ struct vif_params { int use_4addr; + u8 macaddr[ETH_ALEN]; }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1a7a710fe9bf..d543cf152100 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2079,6 +2079,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) !(rdev->wiphy.interface_modes & (1 << type))) return -EOPNOTSUPP; + if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) { + nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC], + ETH_ALEN); + if (!is_valid_ether_addr(params.macaddr)) + return -EADDRNOTAVAIL; + } + if (info->attrs[NL80211_ATTR_4ADDR]) { params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); From 9cbbffe2ded494429b0d005a51a88242bd9b3095 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 9 Jan 2013 12:34:55 -0500 Subject: [PATCH 372/478] mac80211: set NEED_TXPROCESSING for PERR frames A user reported warnings in ath5k due to transmitting frames with no rates set up. The frames were Mesh PERR frames, and some debugging showed an empty control block with just the vif pointer: > [ 562.522682] XXX txinfo: 00000000: 00 00 00 00 00 00 00 00 00 00 00 > 00 00 00 00 00 ................ > [ 562.522688] XXX txinfo: 00000010: 00 00 00 00 00 00 00 00 54 b8 f2 > db 00 00 00 00 ........T....... > [ 562.522693] XXX txinfo: 00000020: 00 00 00 00 00 00 00 00 00 00 00 > 00 00 00 00 00 ................ Set the IEEE80211_TX_INTFL_NEED_TXPROCESSING flag to ensure that rate control gets run before the frame is sent. Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 47aeee2d8db1..40b390581b01 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -215,6 +215,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, skb->priority = 7; info->control.vif = &sdata->vif; + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; ieee80211_set_qos_hdr(sdata, skb); } From 8680451f38a64bd270233b3c0eeb7c45f2b5efe3 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Wed, 9 Jan 2013 12:34:56 -0500 Subject: [PATCH 373/478] mac80211: add encrypt headroom to PERR frames Mesh PERR action frames are robust and thus may be encrypted, so add proper head/tailroom to allow this. Fixes this warning when operating a Mesh STA on ath5k: WARNING: at net/mac80211/wpa.c:427 ccmp_encrypt_skb.isra.5+0x7b/0x1a0 [mac80211]() Call Trace: [] warn_slowpath_common+0x63/0x78 [] warn_slowpath_null+0xf/0x13 [] ccmp_encrypt_skb.isra.5+0x7b/0x1a0 [mac80211] [] ieee80211_crypto_ccmp_encrypt+0x1f/0x37 [mac80211] [] invoke_tx_handlers+0xcad/0x10bd [mac80211] [] ieee80211_tx+0x87/0xb3 [mac80211] [] ieee80211_tx_pending+0xcc/0x170 [mac80211] [] tasklet_action+0x3e/0x65 Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 40b390581b01..2659e428b80c 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -247,11 +247,13 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, return -EAGAIN; skb = dev_alloc_skb(local->tx_headroom + + IEEE80211_ENCRYPT_HEADROOM + + IEEE80211_ENCRYPT_TAILROOM + hdr_len + 2 + 15 /* PERR IE */); if (!skb) return -1; - skb_reserve(skb, local->tx_headroom); + skb_reserve(skb, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM); mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | From cee00a959c0a86571e6f99cf42f0261d7e54d2ae Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 15 Jan 2013 17:15:57 +0200 Subject: [PATCH 374/478] cfg80211: Allow use_mfp to be specified with the connect command The NL80211_ATTR_USE_MFP attribute was originally added for NL80211_CMD_ASSOCIATE, but it is actually as useful (if not even more useful) with NL80211_CMD_CONNECT, so process that attribute with the connect command, too. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 6 +++--- net/wireless/nl80211.c | 9 +++++++++ net/wireless/sme.c | 3 ++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d81e730962cc..f1686d460e6b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1465,6 +1465,7 @@ struct cfg80211_ibss_params { * @ie: IEs for association request * @ie_len: Length of assoc_ie in octets * @privacy: indicates whether privacy-enabled APs should be used + * @mfp: indicate whether management frame protection is used * @crypto: crypto settings * @key_len: length of WEP key for shared key authentication * @key_idx: index of WEP key for shared key authentication @@ -1485,6 +1486,7 @@ struct cfg80211_connect_params { u8 *ie; size_t ie_len; bool privacy; + enum nl80211_mfp mfp; struct cfg80211_crypto_settings crypto; const u8 *key; u8 key_len, key_idx; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6c4f703ae890..d01c16220dc5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -374,8 +374,8 @@ * requests to connect to a specified network but without separating * auth and assoc steps. For this, you need to specify the SSID in a * %NL80211_ATTR_SSID attribute, and can optionally specify the association - * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, - * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, + * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. * Background scan period can optionally be @@ -958,7 +958,7 @@ enum nl80211_commands { * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); * this attribute can be used - * with %NL80211_CMD_ASSOCIATE request + * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d543cf152100..df82a5c9faee 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5932,6 +5932,15 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } + if (info->attrs[NL80211_ATTR_USE_MFP]) { + connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); + if (connect.mfp != NL80211_MFP_REQUIRED && + connect.mfp != NL80211_MFP_NO) + return -EINVAL; + } else { + connect.mfp = NL80211_MFP_NO; + } + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { connect.channel = ieee80211_get_channel(wiphy, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d2d26518cdd7..a825dfe12cf7 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -192,7 +192,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) prev_bssid, params->ssid, params->ssid_len, params->ie, params->ie_len, - false, ¶ms->crypto, + params->mfp != NL80211_MFP_NO, + ¶ms->crypto, params->flags, ¶ms->ht_capa, ¶ms->ht_capa_mask); if (err) From 11c4a075db2f8774d37544342c8cb9752b4db9e1 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Tue, 8 Jan 2013 14:04:07 +0100 Subject: [PATCH 375/478] cfg80211: check radar interface combinations To ease further DFS development regarding interface combinations, use the interface combinations structure to test for radar capabilities. Drivers can specify which channel widths they support, and in which modes. Right now only a single AP interface is allowed, but as the DFS code evolves other combinations can be enabled. Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 3 +++ net/wireless/core.c | 12 +++++++++-- net/wireless/core.h | 7 +++--- net/wireless/nl80211.c | 3 +++ net/wireless/util.c | 41 +++++++++++++++++++++++++++++++++--- 6 files changed, 60 insertions(+), 8 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f1686d460e6b..970da4420676 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2125,6 +2125,7 @@ struct ieee80211_iface_limit { * @beacon_int_infra_match: In this combination, the beacon intervals * between infrastructure and AP types must match. This is required * only in special cases. + * @radar_detect_widths: bitmap of channel widths supported for radar detection * * These examples can be expressed as follows: * @@ -2177,6 +2178,7 @@ struct ieee80211_iface_combination { u16 max_interfaces; u8 n_limits; bool beacon_int_infra_match; + u8 radar_detect_widths; }; struct mac_address { diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d01c16220dc5..e6eeb4ba5dc5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2984,6 +2984,8 @@ enum nl80211_iface_limit_attrs { * the infrastructure network's beacon interval. * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many * different channels may be used within this group. + * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap + * of supported channel widths for radar detection. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -3016,6 +3018,7 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_MAXNUM, NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, /* keep last */ NUM_NL80211_IFACE_COMB, diff --git a/net/wireless/core.c b/net/wireless/core.c index 747dd9365a44..0e702cdc6043 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -382,8 +382,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) c = &wiphy->iface_combinations[i]; - /* Combinations with just one interface aren't real */ - if (WARN_ON(c->max_interfaces < 2)) + /* + * Combinations with just one interface aren't real, + * however we make an exception for DFS. + */ + if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths)) return -EINVAL; /* Need at least one channel */ @@ -398,6 +401,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) return -EINVAL; + /* DFS only works on one channel. */ + if (WARN_ON(c->radar_detect_widths && + (c->num_different_channels > 1))) + return -EINVAL; + if (WARN_ON(!c->n_limits)) return -EINVAL; diff --git a/net/wireless/core.h b/net/wireless/core.h index f342267e3620..8396f7671c8d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -425,7 +425,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype, struct ieee80211_channel *chan, - enum cfg80211_chan_mode chanmode); + enum cfg80211_chan_mode chanmode, + u8 radar_detect); static inline int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, @@ -433,7 +434,7 @@ cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype) { return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, - CHAN_MODE_UNDEFINED); + CHAN_MODE_UNDEFINED, 0); } static inline int @@ -450,7 +451,7 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chanmode) { return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, - chan, chanmode); + chan, chanmode, 0); } void diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index df82a5c9faee..33de80364c5c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -856,6 +856,9 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy, nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM, c->max_interfaces)) goto nla_put_failure; + if (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + c->radar_detect_widths)) + goto nla_put_failure; nla_nest_end(msg, nl_combi); } diff --git a/net/wireless/util.c b/net/wireless/util.c index 16d76a807c2f..1c2795d52db0 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1184,7 +1184,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype, struct ieee80211_channel *chan, - enum cfg80211_chan_mode chanmode) + enum cfg80211_chan_mode chanmode, + u8 radar_detect) { struct wireless_dev *wdev_iter; u32 used_iftypes = BIT(iftype); @@ -1195,14 +1196,45 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chmode; int num_different_channels = 0; int total = 1; + bool radar_required; int i, j; ASSERT_RTNL(); lockdep_assert_held(&rdev->devlist_mtx); + if (WARN_ON(hweight32(radar_detect) > 1)) + return -EINVAL; + + switch (iftype) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_WDS: + radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR); + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_MONITOR: + radar_required = false; + break; + case NL80211_IFTYPE_P2P_DEVICE: + case NUM_NL80211_IFTYPES: + case NL80211_IFTYPE_UNSPECIFIED: + default: + return -EINVAL; + } + + if (radar_required && !radar_detect) + return -EINVAL; + /* Always allow software iftypes */ - if (rdev->wiphy.software_iftypes & BIT(iftype)) + if (rdev->wiphy.software_iftypes & BIT(iftype)) { + if (radar_detect) + return -EINVAL; return 0; + } memset(num, 0, sizeof(num)); memset(used_channels, 0, sizeof(used_channels)); @@ -1275,7 +1307,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, used_iftypes |= BIT(wdev_iter->iftype); } - if (total == 1) + if (total == 1 && !radar_detect) return 0; for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { @@ -1308,6 +1340,9 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, } } + if (radar_detect && !(c->radar_detect_widths & radar_detect)) + goto cont; + /* * Finally check that all iftypes that we're currently * using are actually part of this combination. If they From 5a32aff37a02ebc959837f08d09ac8ba65d4f1b0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Dec 2012 12:36:33 +0100 Subject: [PATCH 376/478] mac80211: split out chandef tracing macros Some new APIs will require tracing a chandef without it being part of a channel context, so separate out the tracing macros for that. Signed-off-by: Johannes Berg --- net/mac80211/trace.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index a8270b441a6f..41861b91daa3 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -28,21 +28,27 @@ #define VIF_PR_FMT " vif:%s(%d%s)" #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" -#define CHANCTX_ENTRY __field(u32, control_freq) \ +#define CHANDEF_ENTRY __field(u32, control_freq) \ __field(u32, chan_width) \ __field(u32, center_freq1) \ - __field(u32, center_freq2) \ + __field(u32, center_freq2) +#define CHANDEF_ASSIGN(c) \ + __entry->control_freq = (c)->chan->center_freq; \ + __entry->chan_width = (c)->width; \ + __entry->center_freq1 = (c)->center_freq1; \ + __entry->center_freq1 = (c)->center_freq2; +#define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz" +#define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \ + __entry->center_freq1, __entry->center_freq2 + +#define CHANCTX_ENTRY CHANDEF_ENTRY \ __field(u8, rx_chains_static) \ __field(u8, rx_chains_dynamic) -#define CHANCTX_ASSIGN __entry->control_freq = ctx->conf.def.chan->center_freq;\ - __entry->chan_width = ctx->conf.def.width; \ - __entry->center_freq1 = ctx->conf.def.center_freq1; \ - __entry->center_freq2 = ctx->conf.def.center_freq2; \ +#define CHANCTX_ASSIGN CHANDEF_ASSIGN(&ctx->conf.def) \ __entry->rx_chains_static = ctx->conf.rx_chains_static; \ __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic -#define CHANCTX_PR_FMT " control:%d MHz width:%d center: %d/%d MHz chains:%d/%d" -#define CHANCTX_PR_ARG __entry->control_freq, __entry->chan_width, \ - __entry->center_freq1, __entry->center_freq2, \ +#define CHANCTX_PR_FMT CHANDEF_PR_FMT " chains:%d/%d" +#define CHANCTX_PR_ARG CHANDEF_PR_ARG, \ __entry->rx_chains_static, __entry->rx_chains_dynamic From 6ead1bbc381a674c20f227dbe6f3a8c6f67ce7a2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 14 Jan 2013 22:33:50 +0200 Subject: [PATCH 377/478] Bluetooth: Add a new workqueue for hci_request operations The hci_request function is blocking and cannot be called through the usual per-HCI device workqueue (hdev->workqueue). While hci_request is in progress any other work from the queue, including sending HCI commands to the controller would be blocked and eventually cause the hci_request call to time out. This patch adds a second workqueue to be used by operations needing hci_request and thereby avoiding issues with blocking other workqueue users. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 014a2eaa5389..769a740c104c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -216,6 +216,7 @@ struct hci_dev { unsigned long le_last_tx; struct workqueue_struct *workqueue; + struct workqueue_struct *req_workqueue; struct work_struct power_on; struct delayed_work power_off; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 596660d37c5e..f73907aad79f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1799,6 +1799,15 @@ int hci_register_dev(struct hci_dev *hdev) goto err; } + hdev->req_workqueue = alloc_workqueue(hdev->name, + WQ_HIGHPRI | WQ_UNBOUND | + WQ_MEM_RECLAIM, 1); + if (!hdev->req_workqueue) { + destroy_workqueue(hdev->workqueue); + error = -ENOMEM; + goto err; + } + error = hci_add_sysfs(hdev); if (error < 0) goto err_wqueue; @@ -1827,6 +1836,7 @@ int hci_register_dev(struct hci_dev *hdev) err_wqueue: destroy_workqueue(hdev->workqueue); + destroy_workqueue(hdev->req_workqueue); err: ida_simple_remove(&hci_index_ida, hdev->id); write_lock(&hci_dev_list_lock); @@ -1880,6 +1890,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_del_sysfs(hdev); destroy_workqueue(hdev->workqueue); + destroy_workqueue(hdev->req_workqueue); hci_dev_lock(hdev); hci_blacklist_clear(hdev); From 1920257316615676387794cc5fb838183b3bae7f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 14 Jan 2013 22:33:51 +0200 Subject: [PATCH 378/478] Bluetooth: Use req_workqueue for hci_request operations This patch converts work assignment relying on hci_request() from the system-global work queue to the per-HCI device specific work queue (hdev->req_workqueue) intended for hci_request() related tasks. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 5 +++-- net/bluetooth/mgmt.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f73907aad79f..545553b82295 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1146,7 +1146,8 @@ static void hci_power_on(struct work_struct *work) return; if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - schedule_delayed_work(&hdev->power_off, HCI_AUTO_OFF_TIMEOUT); + queue_delayed_work(hdev->req_workqueue, &hdev->power_off, + HCI_AUTO_OFF_TIMEOUT); if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) mgmt_index_added(hdev); @@ -1830,7 +1831,7 @@ int hci_register_dev(struct hci_dev *hdev) hci_notify(hdev, HCI_DEV_REG); hci_dev_hold(hdev); - schedule_work(&hdev->power_on); + queue_work(hdev->req_workqueue, &hdev->power_on); return id; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 37add53ce613..54114ff4090f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -812,9 +812,9 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, } if (cp->val) - schedule_work(&hdev->power_on); + queue_work(hdev->req_workqueue, &hdev->power_on); else - schedule_work(&hdev->power_off.work); + queue_work(hdev->req_workqueue, &hdev->power_off.work); err = 0; From 46818ed514102c8d251d4aff5c99ad3ff6805432 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 14 Jan 2013 22:33:52 +0200 Subject: [PATCH 379/478] Bluetooth: Fix using system-global workqueue when not necessary There's a per-HCI device workqueue (hdev->workqueue) that should be used for general per-HCI device work (except hdev->req_workqueue that's for hci_request() related work). This patch fixes places using the system-global work queue and makes them use the hdev->workqueue instead. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/mgmt.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 545553b82295..e061b354d2c4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1622,8 +1622,8 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, if (err < 0) return err; - schedule_delayed_work(&hdev->le_scan_disable, - msecs_to_jiffies(timeout)); + queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, + msecs_to_jiffies(timeout)); return 0; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 54114ff4090f..fc171f222ba4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1361,7 +1361,8 @@ static bool enable_service_cache(struct hci_dev *hdev) return false; if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { - schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT); + queue_delayed_work(hdev->workqueue, &hdev->service_cache, + CACHE_TIMEOUT); return true; } From de5fad815703b5b24bc4726cd71422929537d259 Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Wed, 30 May 2012 11:36:39 +0300 Subject: [PATCH 380/478] mac80211: add op to configure default key id There are hardwares which support offload of data packets for example when auto ARP is enabled the hw will send the ARP response. In such cases if WEP encryption is configured the hw must know the default WEP key in order to encrypt the packets correctly. When hw_accel is enabled and encryption type is set to WEP, the driver should get the default key index from mac80211. Signed-off-by: Yoni Divinsky [cleanups, fixes, documentation] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 10 ++++++++++ net/mac80211/driver-ops.h | 16 ++++++++++++++++ net/mac80211/key.c | 5 ++++- net/mac80211/trace.h | 23 +++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3037f49e51c8..e0825a9dbfea 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1630,6 +1630,10 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * rekeying), it will not include a valid phase 1 key. The valid phase 1 key is * provided by update_tkip_key only. The trigger that makes mac80211 call this * handler is software decryption with wrap around of iv16. + * + * The set_default_unicast_key() call updates the default WEP key index + * configured to the hardware for WEP encryption type. This is required + * for devices that support offload of data packets (e.g. ARP responses). */ /** @@ -2208,6 +2212,10 @@ enum ieee80211_rate_control_changed { * After rekeying was done it should (for example during resume) notify * userspace of the new replay counter using ieee80211_gtk_rekey_notify(). * + * @set_default_unicast_key: Set the default (unicast) key index, useful for + * WEP when the device sends data packets autonomously, e.g. for ARP + * offloading. The index can be 0-3, or -1 for unsetting it. + * * @hw_scan: Ask the hardware to service the scan request, no need to start * the scan state machine in stack. The scan must honour the channel * configuration done by the regulatory agent in the wiphy's @@ -2539,6 +2547,8 @@ struct ieee80211_ops { void (*set_rekey_data)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *data); + void (*set_default_unicast_key)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int idx); int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req); void (*cancel_hw_scan)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0c07f94c5378..e6033b06caba 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1020,4 +1020,20 @@ static inline void drv_restart_complete(struct ieee80211_local *local) trace_drv_return_void(local); } +static inline void +drv_set_default_unicast_key(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + int key_idx) +{ + check_sdata_in_driver(sdata); + + WARN_ON_ONCE(key_idx < -1 || key_idx > 3); + + trace_drv_set_default_unicast_key(local, sdata, key_idx); + if (local->ops->set_default_unicast_key) + local->ops->set_default_unicast_key(&local->hw, &sdata->vif, + key_idx); + trace_drv_return_void(local); +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 619c5d697999..ef252eb58c36 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -204,8 +204,11 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, if (idx >= 0 && idx < NUM_DEFAULT_KEYS) key = key_mtx_dereference(sdata->local, sdata->keys[idx]); - if (uni) + if (uni) { rcu_assign_pointer(sdata->default_unicast_key, key); + drv_set_default_unicast_key(sdata->local, sdata, idx); + } + if (multi) rcu_assign_pointer(sdata->default_multicast_key, key); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 41861b91daa3..9341b9359b66 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1821,6 +1821,29 @@ TRACE_EVENT(stop_queue, ) ); +TRACE_EVENT(drv_set_default_unicast_key, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + int key_idx), + + TP_ARGS(local, sdata, key_idx), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(int, key_idx) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->key_idx = key_idx; + ), + + TP_printk(LOCAL_PR_FMT VIF_PR_FMT " key_idx:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx) +); + #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211_msg From 0f19b41e223d787251c59137e61fc5145c13d1c4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 14 Jan 2013 16:39:07 +0100 Subject: [PATCH 381/478] mac80211: remove ARP filter enable/disable logic Depending on the driver, having ARP filtering for some addresses may be possible. Remove the logic that tracks whether ARP filter is enabled or not and give the driver the total number of addresses instead of the length of the list so it can make its own decision. Reviewed-by: Luciano Coelho Signed-off-by: Johannes Berg --- .../wireless/brcm80211/brcmsmac/mac80211_if.c | 5 ++--- drivers/net/wireless/ti/wlcore/main.c | 3 +-- include/net/mac80211.h | 11 ++++------- net/mac80211/ieee80211_i.h | 2 -- net/mac80211/iface.c | 3 --- net/mac80211/main.c | 16 ++++------------ net/mac80211/mlme.c | 8 ++------ net/mac80211/trace.h | 13 +++++++++---- 8 files changed, 22 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index f0fc8cd4d5df..e4b42f7e659c 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -539,9 +539,8 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ARP_FILTER) { /* Hardware ARP filter address list or state changed */ - brcms_err(core, "%s: arp filtering: enabled %s, count %d" - " (implement)\n", __func__, info->arp_filter_enabled ? - "true" : "false", info->arp_addr_cnt); + brcms_err(core, "%s: arp filtering: %d addresses" + " (implement)\n", __func__, info->arp_addr_cnt); } if (changed & BSS_CHANGED_QOS) { diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ce6e62a37e14..919ad70cc520 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4113,8 +4113,7 @@ sta_not_found: wlvif->sta.qos = bss_conf->qos; WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS); - if (bss_conf->arp_addr_cnt == 1 && - bss_conf->arp_filter_enabled) { + if (bss_conf->arp_addr_cnt == 1 && bss_conf->assoc) { wlvif->ip_addr = addr; /* * The template should have been configured only upon diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e0825a9dbfea..679ad4bb222b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -297,11 +297,9 @@ enum ieee80211_rssi_event { * may filter ARP queries targeted for other addresses than listed here. * The driver must allow ARP queries targeted for all address listed here * to pass through. An empty list implies no ARP queries need to pass. - * @arp_addr_cnt: Number of addresses currently on the list. - * @arp_filter_enabled: Enable ARP filtering - if enabled, the hardware may - * filter ARP queries based on the @arp_addr_list, if disabled, the - * hardware must not perform any ARP filtering. Note, that the filter will - * be enabled also in promiscuous mode. + * @arp_addr_cnt: Number of addresses currently on the list. Note that this + * may be larger than %IEEE80211_BSS_ARP_ADDR_LIST_LEN (the arp_addr_list + * array size), it's up to the driver what to do in that case. * @qos: This is a QoS-enabled BSS. * @idle: This interface is idle. There's also a global idle flag in the * hardware config which may be more appropriate depending on what @@ -338,8 +336,7 @@ struct ieee80211_bss_conf { u32 cqm_rssi_hyst; struct cfg80211_chan_def chandef; __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; - u8 arp_addr_cnt; - bool arp_filter_enabled; + int arp_addr_cnt; bool qos; bool idle; bool ps; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0fa44a965ad9..b1fed5491ed6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -747,8 +747,6 @@ struct ieee80211_sub_if_data { struct work_struct work; struct sk_buff_head skb_queue; - bool arp_filter_state; - u8 needed_rx_chains; enum ieee80211_smps_mode smps_mode; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 06fac2991d40..0a36dc6346bb 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1574,9 +1574,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, /* initialise type-independent data */ sdata->wdev.wiphy = local->hw.wiphy; sdata->local = local; -#ifdef CONFIG_INET - sdata->arp_filter_state = true; -#endif for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) skb_queue_head_init(&sdata->fragments[i].skb_list); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 39cfe8f10ad2..baf9720c1876 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -349,27 +349,19 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, /* Copy the addresses to the bss_conf list */ ifa = idev->ifa_list; - while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) { - bss_conf->arp_addr_list[c] = ifa->ifa_address; + while (ifa) { + if (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN) + bss_conf->arp_addr_list[c] = ifa->ifa_address; ifa = ifa->ifa_next; c++; } - /* If not all addresses fit the list, disable filtering */ - if (ifa) { - sdata->arp_filter_state = false; - c = 0; - } else { - sdata->arp_filter_state = true; - } bss_conf->arp_addr_cnt = c; /* Configure driver only if associated (which also implies it is up) */ - if (ifmgd->associated) { - bss_conf->arp_filter_enabled = sdata->arp_filter_state; + if (ifmgd->associated) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_ARP_FILTER); - } mutex_unlock(&ifmgd->mtx); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e930175771ff..2d9ef20cd38d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1465,10 +1465,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= BSS_CHANGED_CQM; /* Enable ARP filtering */ - if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) { - bss_conf->arp_filter_enabled = sdata->arp_filter_state; + if (bss_conf->arp_addr_cnt) bss_info_changed |= BSS_CHANGED_ARP_FILTER; - } ieee80211_bss_info_change_notify(sdata, bss_info_changed); @@ -1582,10 +1580,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&local->dynamic_ps_enable_work); /* Disable ARP filtering */ - if (sdata->vif.bss_conf.arp_filter_enabled) { - sdata->vif.bss_conf.arp_filter_enabled = false; + if (sdata->vif.bss_conf.arp_addr_cnt) changed |= BSS_CHANGED_ARP_FILTER; - } sdata->vif.bss_conf.qos = false; changed |= BSS_CHANGED_QOS; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 9341b9359b66..e9f95913c6f0 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -347,8 +347,11 @@ TRACE_EVENT(drv_bss_info_changed, __field(s32, cqm_rssi_hyst); __field(u32, channel_width); __field(u32, channel_cfreq1); - __dynamic_array(u32, arp_addr_list, info->arp_addr_cnt); - __field(bool, arp_filter_enabled); + __dynamic_array(u32, arp_addr_list, + info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ? + IEEE80211_BSS_ARP_ADDR_LIST_LEN : + info->arp_addr_cnt); + __field(int, arp_addr_cnt); __field(bool, qos); __field(bool, idle); __field(bool, ps); @@ -384,9 +387,11 @@ TRACE_EVENT(drv_bss_info_changed, __entry->cqm_rssi_hyst = info->cqm_rssi_hyst; __entry->channel_width = info->chandef.width; __entry->channel_cfreq1 = info->chandef.center_freq1; + __entry->arp_addr_cnt = info->arp_addr_cnt; memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list, - sizeof(u32) * info->arp_addr_cnt); - __entry->arp_filter_enabled = info->arp_filter_enabled; + sizeof(u32) * (info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ? + IEEE80211_BSS_ARP_ADDR_LIST_LEN : + info->arp_addr_cnt)); __entry->qos = info->qos; __entry->idle = info->idle; __entry->ps = info->ps; From 0a214d3f7e5041685ebff054628a4354bedd78c3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 Jan 2013 10:39:44 +0100 Subject: [PATCH 382/478] mac80211: improve aggregation debug messages A lot of the aggregation messages don't indicate the station so they're hard to understand if there are multiple sessions in progress. Make that easier by adding the MAC address to most messages. Also add the TID if it wasn't already there. Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 14 +++++++++----- net/mac80211/agg-tx.c | 44 ++++++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 808338a1bce5..31bf2586fb84 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -83,8 +83,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, &sta->sta, tid, NULL, 0)) sdata_info(sta->sdata, - "HW problem - can not stop rx aggregation for tid %d\n", - tid); + "HW problem - can not stop rx aggregation for %pM tid %d\n", + sta->sta.addr, tid); /* check if this is a self generated aggregation halt */ if (initiator == WLAN_BACK_RECIPIENT && tx) @@ -159,7 +159,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data) } rcu_read_unlock(); - ht_dbg(sta->sdata, "rx session timer expired on tid %d\n", (u16)*ptid); + ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n", + sta->sta.addr, (u16)*ptid); set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired); ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); @@ -247,7 +248,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_REQUEST_DECLINED; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { - ht_dbg(sta->sdata, "Suspend in progress - Denying ADDBA request\n"); + ht_dbg(sta->sdata, + "Suspend in progress - Denying ADDBA request (%pM tid %d)\n", + sta->sta.addr, tid); goto end_no_lock; } @@ -317,7 +320,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, &sta->sta, tid, &start_seq_num, 0); - ht_dbg(sta->sdata, "Rx A-MPDU request on tid %d result %d\n", tid, ret); + ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", + sta->sta.addr, tid, ret); if (ret) { kfree(tid_agg_rx->reorder_buf); kfree(tid_agg_rx->reorder_time); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 2f0ccbc5f13e..e90911819ed4 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -387,12 +387,13 @@ static void sta_addba_resp_timer_expired(unsigned long data) test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { rcu_read_unlock(); ht_dbg(sta->sdata, - "timer expired on tid %d but we are not (or no longer) expecting addBA response there\n", - tid); + "timer expired on %pM tid %d but we are not (or no longer) expecting addBA response there\n", + sta->sta.addr, tid); return; } - ht_dbg(sta->sdata, "addBA response timer expired on tid %d\n", tid); + ht_dbg(sta->sdata, "addBA response timer expired on %pM tid %d\n", + sta->sta.addr, tid); ieee80211_stop_tx_ba_session(&sta->sta, tid); rcu_read_unlock(); @@ -429,7 +430,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) &sta->sta, tid, &start_seq_num, 0); if (ret) { ht_dbg(sdata, - "BA request denied - HW unavailable for tid %d\n", tid); + "BA request denied - HW unavailable for %pM tid %d\n", + sta->sta.addr, tid); spin_lock_bh(&sta->lock); ieee80211_agg_splice_packets(sdata, tid_tx, tid); ieee80211_assign_tid_tx(sta, tid, NULL); @@ -442,7 +444,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) /* activate the timer for the recipient's addBA response */ mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); - ht_dbg(sdata, "activated addBA response timer on tid %d\n", tid); + ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n", + sta->sta.addr, tid); spin_lock_bh(&sta->lock); sta->ampdu_mlme.last_addba_req_time[tid] = jiffies; @@ -489,7 +492,8 @@ static void sta_tx_agg_session_timer_expired(unsigned long data) rcu_read_unlock(); - ht_dbg(sta->sdata, "tx session timer expired on tid %d\n", (u16)*ptid); + ht_dbg(sta->sdata, "tx session timer expired on %pM tid %d\n", + sta->sta.addr, (u16)*ptid); ieee80211_stop_tx_ba_session(&sta->sta, *ptid); } @@ -525,7 +529,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { ht_dbg(sdata, - "BA sessions blocked - Denying BA session request\n"); + "BA sessions blocked - Denying BA session request %pM tid %d\n", + sta->sta.addr, tid); return -EINVAL; } @@ -566,8 +571,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] + HT_AGG_RETRIES_PERIOD)) { ht_dbg(sdata, - "BA request denied - waiting a grace period after %d failed requests on tid %u\n", - sta->ampdu_mlme.addba_req_num[tid], tid); + "BA request denied - waiting a grace period after %d failed requests on %pM tid %u\n", + sta->ampdu_mlme.addba_req_num[tid], sta->sta.addr, tid); ret = -EBUSY; goto err_unlock_sta; } @@ -576,8 +581,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, /* check if the TID is not in aggregation flow already */ if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) { ht_dbg(sdata, - "BA request denied - session is not idle on tid %u\n", - tid); + "BA request denied - session is not idle on %pM tid %u\n", + sta->sta.addr, tid); ret = -EAGAIN; goto err_unlock_sta; } @@ -632,7 +637,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, tid_tx = rcu_dereference_protected_tid_tx(sta, tid); - ht_dbg(sta->sdata, "Aggregation is on for tid %d\n", tid); + ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n", + sta->sta.addr, tid); drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_OPERATIONAL, @@ -802,7 +808,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { - ht_dbg(sdata, "unexpected callback to A-MPDU stop\n"); + ht_dbg(sdata, + "unexpected callback to A-MPDU stop for %pM tid %d\n", + sta->sta.addr, tid); goto unlock_sta; } @@ -861,13 +869,15 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, goto out; if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) { - ht_dbg(sta->sdata, "wrong addBA response token, tid %d\n", tid); + ht_dbg(sta->sdata, "wrong addBA response token, %pM tid %d\n", + sta->sta.addr, tid); goto out; } del_timer_sync(&tid_tx->addba_resp_timer); - ht_dbg(sta->sdata, "switched off addBA timer for tid %d\n", tid); + ht_dbg(sta->sdata, "switched off addBA timer for %pM tid %d\n", + sta->sta.addr, tid); /* * addba_resp_timer may have fired before we got here, and @@ -877,8 +887,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) || test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { ht_dbg(sta->sdata, - "got addBA resp for tid %d but we already gave up\n", - tid); + "got addBA resp for %pM tid %d but we already gave up\n", + sta->sta.addr, tid); goto out; } From a65240c1013222dbf41166c8b2c5ed2720c807c3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 14 Jan 2013 15:14:34 +0100 Subject: [PATCH 383/478] mac80211: allow drivers to access IPv6 information To be able to implement NS response offloading (in regular operation or while in WoWLAN) drivers need to know the IPv6 addresses assigned to interfaces. Implement an IPv6 notifier in mac80211 to call the driver when addresses change. Unlike for IPv4, implement it as a callback rather than as a list in the BSS configuration, that is more flexible. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 9 +++++++ net/mac80211/driver-ops.h | 12 ++++++++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/main.c | 48 ++++++++++++++++++++++++++++++++++++++ net/mac80211/trace.h | 8 +++++++ 5 files changed, 78 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 679ad4bb222b..ece5733d113d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2497,6 +2497,9 @@ enum ieee80211_rate_control_changed { * driver's resume function returned 1, as this is just like an "inline" * hardware restart. This callback may sleep. * + * @ipv6_addr_change: IPv6 address assignment on the given interface changed. + * Currently, this is only called for managed or P2P client interfaces. + * This callback is optional; it must not sleep. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2672,6 +2675,12 @@ struct ieee80211_ops { struct ieee80211_chanctx_conf *ctx); void (*restart_complete)(struct ieee80211_hw *hw); + +#if IS_ENABLED(CONFIG_IPV6) + void (*ipv6_addr_change)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev); +#endif }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index e6033b06caba..d51afbd614d3 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1036,4 +1036,16 @@ drv_set_default_unicast_key(struct ieee80211_local *local, trace_drv_return_void(local); } +#if IS_ENABLED(CONFIG_IPV6) +static inline void drv_ipv6_addr_change(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct inet6_dev *idev) +{ + trace_drv_ipv6_addr_change(local, sdata); + if (local->ops->ipv6_addr_change) + local->ops->ipv6_addr_change(&local->hw, &sdata->vif, idev); + trace_drv_return_void(local); +} +#endif + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b1fed5491ed6..aec1b332aeb7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1127,6 +1127,7 @@ struct ieee80211_local { struct timer_list dynamic_ps_timer; struct notifier_block network_latency_notifier; struct notifier_block ifa_notifier; + struct notifier_block ifa6_notifier; /* * The dynamic ps timeout configured from user space via WEXT - diff --git a/net/mac80211/main.c b/net/mac80211/main.c index baf9720c1876..2bdd454e8bcf 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "ieee80211_i.h" #include "driver-ops.h" @@ -369,6 +370,37 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, } #endif +#if IS_ENABLED(CONFIG_IPV6) +static int ieee80211_ifa6_changed(struct notifier_block *nb, + unsigned long data, void *arg) +{ + struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg; + struct inet6_dev *idev = ifa->idev; + struct net_device *ndev = ifa->idev->dev; + struct ieee80211_local *local = + container_of(nb, struct ieee80211_local, ifa6_notifier); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + struct ieee80211_sub_if_data *sdata; + + /* Make sure it's our interface that got changed */ + if (!wdev || wdev->wiphy != local->hw.wiphy) + return NOTIFY_DONE; + + sdata = IEEE80211_DEV_TO_SUB_IF(ndev); + + /* + * For now only support station mode. This is mostly because + * doing AP would have to handle AP_VLAN in some way ... + */ + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return NOTIFY_DONE; + + drv_ipv6_addr_change(local, sdata, idev); + + return NOTIFY_DONE; +} +#endif + static int ieee80211_napi_poll(struct napi_struct *napi, int budget) { struct ieee80211_local *local = @@ -977,12 +1009,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) goto fail_ifa; #endif +#if IS_ENABLED(CONFIG_IPV6) + local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; + result = register_inet6addr_notifier(&local->ifa6_notifier); + if (result) + goto fail_ifa6; +#endif + netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll, local->hw.napi_weight); return 0; +#if IS_ENABLED(CONFIG_IPV6) + fail_ifa6: #ifdef CONFIG_INET + unregister_inetaddr_notifier(&local->ifa_notifier); +#endif +#endif +#if defined(CONFIG_INET) || defined(CONFIG_IPV6) fail_ifa: pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, &local->network_latency_notifier); @@ -1018,6 +1063,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) #ifdef CONFIG_INET unregister_inetaddr_notifier(&local->ifa_notifier); #endif +#if IS_ENABLED(CONFIG_IPV6) + unregister_inet6addr_notifier(&local->ifa6_notifier); +#endif rtnl_lock(); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e9f95913c6f0..2a2c2e20307d 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1437,6 +1437,14 @@ DEFINE_EVENT(local_only_evt, drv_restart_complete, TP_ARGS(local) ); +#if IS_ENABLED(CONFIG_IPV6) +DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); +#endif + /* * Tracing for API calls that drivers call. */ From fa4cffcba9e13798ed7c6b8526b91b1631ecb53e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 16 Jan 2013 11:45:15 +0100 Subject: [PATCH 384/478] iwlegacy: fix IBSS cleanup We do not correctly change interface type when switching from IBSS mode to STA mode, that results in microcode errors. Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=886946 Reported-by: Jaroslav Skarvada Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/common.c | 35 +++++++++++--------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index 7e16d10a7f14..90b8970eadf0 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -3958,17 +3958,21 @@ il_connection_init_rx_config(struct il_priv *il) memset(&il->staging, 0, sizeof(il->staging)); - if (!il->vif) { + switch (il->iw_mode) { + case NL80211_IFTYPE_UNSPECIFIED: il->staging.dev_type = RXON_DEV_TYPE_ESS; - } else if (il->vif->type == NL80211_IFTYPE_STATION) { + break; + case NL80211_IFTYPE_STATION: il->staging.dev_type = RXON_DEV_TYPE_ESS; il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; - } else if (il->vif->type == NL80211_IFTYPE_ADHOC) { + break; + case NL80211_IFTYPE_ADHOC: il->staging.dev_type = RXON_DEV_TYPE_IBSS; il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; il->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; - } else { + break; + default: IL_ERR("Unsupported interface type %d\n", il->vif->type); return; } @@ -4550,8 +4554,7 @@ out: EXPORT_SYMBOL(il_mac_add_interface); static void -il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif, - bool mode_change) +il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif) { lockdep_assert_held(&il->mutex); @@ -4560,9 +4563,7 @@ il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif, il_force_scan_end(il); } - if (!mode_change) - il_set_mode(il); - + il_set_mode(il); } void @@ -4575,8 +4576,8 @@ il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) WARN_ON(il->vif != vif); il->vif = NULL; - - il_teardown_interface(il, vif, false); + il->iw_mode = NL80211_IFTYPE_UNSPECIFIED; + il_teardown_interface(il, vif); memset(il->bssid, 0, ETH_ALEN); D_MAC80211("leave\n"); @@ -4685,18 +4686,10 @@ il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } /* success */ - il_teardown_interface(il, vif, true); vif->type = newtype; vif->p2p = false; - err = il_set_mode(il); - WARN_ON(err); - /* - * We've switched internally, but submitting to the - * device may have failed for some reason. Mask this - * error, because otherwise mac80211 will not switch - * (and set the interface type back) and we'll be - * out of sync with it. - */ + il->iw_mode = newtype; + il_teardown_interface(il, vif); err = 0; out: From 430d25251ef3c36404e6b4a7eaaae35e225bc8e7 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 19 Jan 2013 12:36:49 -0600 Subject: [PATCH 385/478] rtlwifi: Fix build warning introduced by commit a290593 The kbuild test robot reports the following warning with x86_64-randconfig-x955: warning: (RTL8192CE && RTL8192SE && RTL8192DE && RTL8723AE && RTL8192CU) selects RTLWIFI which has unmet direct dependencies (NETDEVICES && WLAN && (RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE)) This warning was introduced in commit a290593, "rtlwifi: Modify files for addition of rtl8723ae", and is d ue to a missing dependence of RTLWIFI on RTL8723AE. Signed-off-by: Larry Finger Cc: kbuild test robot Reported-by: kbuild test robot Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/Kconfig b/drivers/net/wireless/rtlwifi/Kconfig index 21b1bbb93a7e..b80bc4612581 100644 --- a/drivers/net/wireless/rtlwifi/Kconfig +++ b/drivers/net/wireless/rtlwifi/Kconfig @@ -57,12 +57,12 @@ config RTL8192CU config RTLWIFI tristate - depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE + depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE || RTL8723AE default m config RTLWIFI_DEBUG bool "Additional debugging output" - depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE + depends on RTL8192CE || RTL8192CU || RTL8192SE || RTL8192DE || RTL8723AE default y config RTL8192C_COMMON From 4a8f199508d79ff8a7d1e22f47b912baaf225336 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 21:55:20 +0100 Subject: [PATCH 386/478] ath9k_hw: fix calibration issues on chainmask that don't include chain 0 Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 2 ++ drivers/net/wireless/ath/ath9k/ar9003_phy.c | 2 +- drivers/net/wireless/ath/ath9k/hw.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 8b0d8dcd7625..56317b0fb6b6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -976,6 +976,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, AR_PHY_CL_TAB_1, AR_PHY_CL_TAB_2 }; + ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask); + if (rtt) { if (!ar9003_hw_rtt_restore(ah, chan)) run_rtt_cal = true; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index ce19c09fa8e8..8290eddaf0e3 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -586,7 +586,7 @@ static void ar9003_hw_init_bb(struct ath_hw *ah, ath9k_hw_synth_delay(ah, chan, synthDelay); } -static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) +void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) { switch (rx) { case 0x5: diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 7f1a8e91c908..9d26fc56ca56 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1066,6 +1066,7 @@ void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); int ar9003_paprd_init_table(struct ath_hw *ah); bool ar9003_paprd_is_done(struct ath_hw *ah); bool ar9003_is_paprd_enabled(struct ath_hw *ah); +void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx); /* Hardware family op attach helpers */ void ar5008_hw_attach_phy_ops(struct ath_hw *ah); From 24171dd92096fc370b195f3f6bdc0798855dc3f9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 21:55:21 +0100 Subject: [PATCH 387/478] ath9k_hw: fix chain swap setting when setting rx chainmask to 5 Chain swapping should only be enabled when the EEPROM chainmask is set to 5, regardless of what the runtime chainmask is. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 25 +++++---------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 8290eddaf0e3..3afc24bde6d6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -588,30 +588,17 @@ static void ar9003_hw_init_bb(struct ath_hw *ah, void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) { - switch (rx) { - case 0x5: + if (ah->caps.tx_chainmask == 5 || ah->caps.rx_chainmask == 5) REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); - case 0x3: - case 0x1: - case 0x2: - case 0x7: - REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx); - REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx); - break; - default: - break; - } + + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx); if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7)) - REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); - else - REG_WRITE(ah, AR_SELFGEN_MASK, tx); + tx = 3; - if (tx == 0x5) { - REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, - AR_PHY_SWAP_ALT_CHAIN); - } + REG_WRITE(ah, AR_SELFGEN_MASK, tx); } /* From fea92cbf0850d788683827990670d3968f893327 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 21:55:22 +0100 Subject: [PATCH 388/478] ath9k: allow setting arbitrary antenna masks on AR9003+ Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3796e65c26f1..dd91f8fdc01c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1826,6 +1826,9 @@ static u32 fill_chainmask(u32 cap, u32 new) static bool validate_antenna_mask(struct ath_hw *ah, u32 val) { + if (AR_SREV_9300_20_OR_LATER(ah)) + return true; + switch (val & 0x7) { case 0x1: case 0x3: From 83f0c6d1f502bd75bb4a9e31e8d64e59c6894ad1 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Mon, 21 Jan 2013 21:04:10 -0800 Subject: [PATCH 389/478] mwifiex: fix typo in PCIe adapter NULL check Add missing "!" as we are supposed to check "!card->adapter" in PCIe suspend handler. Cc: "3.2+" Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Reviewed-by: Sergey V. Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 13fbc4eb1595..b879e1338a54 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -161,7 +161,7 @@ static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state) if (pdev) { card = (struct pcie_service_card *) pci_get_drvdata(pdev); - if (!card || card->adapter) { + if (!card || !card->adapter) { pr_err("Card or adapter structure is not valid\n"); return 0; } From 6aaacd861517f89797f2f4a54624a599847262f3 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 13 Jan 2013 19:54:58 +0100 Subject: [PATCH 390/478] ath9k_hw: fix RF bank initialization ar900*_init_mode_regs needs to be called before RF banks are allocated, otherwise the storage size of RF banks isn't known. This patch fixes a memory overrun that can show up as a crash on unloading the module. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_hw.c | 12 ++++++++---- drivers/net/wireless/ath/ath9k/ar9003_hw.c | 2 +- drivers/net/wireless/ath/ath9k/hw.c | 7 ------- drivers/net/wireless/ath/ath9k/hw.h | 2 -- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 54da026f058c..f053d978540e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -23,13 +23,13 @@ /* General hardware code for the A5008/AR9001/AR9002 hadware families */ -static void ar9002_hw_init_mode_regs(struct ath_hw *ah) +static int ar9002_hw_init_mode_regs(struct ath_hw *ah) { if (AR_SREV_9271(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271); INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271); INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg); - return; + return 0; } if (ah->config.pcie_clock_req) @@ -104,7 +104,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) data = devm_kzalloc(ah->dev, size, GFP_KERNEL); if (!data) - return; + return -ENOMEM; memcpy(data, addac->ia_array, size); addac->ia_array = data; @@ -120,6 +120,7 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniCckfirJapan2484, ar9287Common_japan_2484_cck_fir_coeff_9287_1_1); } + return 0; } static void ar9280_20_hw_init_rxgain_ini(struct ath_hw *ah) @@ -415,7 +416,10 @@ int ar9002_hw_attach_ops(struct ath_hw *ah) struct ath_hw_ops *ops = ath9k_hw_ops(ah); int ret; - priv_ops->init_mode_regs = ar9002_hw_init_mode_regs; + ret = ar9002_hw_init_mode_regs(ah); + if (ret) + return ret; + priv_ops->init_mode_gain_regs = ar9002_hw_init_mode_gain_regs; ops->config_pci_powersave = ar9002_hw_configpcipowersave; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index de8a56c92e1a..6fcd6e989c55 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -704,7 +704,7 @@ void ar9003_hw_attach_ops(struct ath_hw *ah) struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); - priv_ops->init_mode_regs = ar9003_hw_init_mode_regs; + ar9003_hw_init_mode_regs(ah); priv_ops->init_mode_gain_regs = ar9003_hw_init_mode_gain_regs; ops->config_pci_powersave = ar9003_hw_configpcipowersave; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index caf0626eeaa5..a7448d1f5460 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -54,11 +54,6 @@ static void ath9k_hw_init_cal_settings(struct ath_hw *ah) ath9k_hw_private_ops(ah)->init_cal_settings(ah); } -static void ath9k_hw_init_mode_regs(struct ath_hw *ah) -{ - ath9k_hw_private_ops(ah)->init_mode_regs(ah); -} - static u32 ath9k_hw_compute_pll_control(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -670,8 +665,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) if (!AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_MRC_CCK; - ath9k_hw_init_mode_regs(ah); - if (!ah->is_pciexpress) ath9k_hw_disablepcie(ah); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index d32ebf7a2414..195613ff72ee 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -600,7 +600,6 @@ struct ath_hw_radar_conf { * @init_cal_settings: setup types of calibrations supported * @init_cal: starts actual calibration * - * @init_mode_regs: Initializes mode registers * @init_mode_gain_regs: Initialize TX/RX gain registers * * @rf_set_freq: change frequency @@ -619,7 +618,6 @@ struct ath_hw_private_ops { void (*init_cal_settings)(struct ath_hw *ah); bool (*init_cal)(struct ath_hw *ah, struct ath9k_channel *chan); - void (*init_mode_regs)(struct ath_hw *ah); void (*init_mode_gain_regs)(struct ath_hw *ah); void (*setup_calibration)(struct ath_hw *ah, struct ath9k_cal_list *currCal); From 708d019fd1d2d1bf184e797ebe934c90a6527d50 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 8 Jan 2013 13:33:03 +0800 Subject: [PATCH 391/478] drivers/net/wireless: need consider the not '\0' terminated string. in ray_cs.c: the a_current_ess_id is "Null terminated unless ESSID_SIZE long" so we need buffer it with '\0' firstly, before using strlen or %s. additional information: in drivers/net/wireless/rayctl.h: "NULL terminated unless 32 long" is a comment at line 616, 664 ESSID_SIZE is 32, at line 190 in include/uapi/linux/wireless.h: IW_ESSID_MAX_SIZE is also 32 in drivers/net/wireless/ray_cs.c: use strncpy for it, without '\0' terminated, at line 639 use memcpy for it, assume not '\0' terminated in line 1092..1097 buffer it with '\0' firstly, before using %s, in line 2576, 2598..2600 Signed-off-by: Chen Gang Signed-off-by: John W. Linville --- drivers/net/wireless/ray_cs.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 598ca1cafb95..e7cf37f550d1 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -1107,12 +1107,15 @@ static int ray_get_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); + UCHAR tmp[IW_ESSID_MAX_SIZE + 1]; /* Get the essid that was set */ memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE); + memcpy(tmp, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE); + tmp[IW_ESSID_MAX_SIZE] = '\0'; /* Push it out ! */ - wrqu->essid.length = strlen(extra); + wrqu->essid.length = strlen(tmp); wrqu->essid.flags = 1; /* active */ return 0; @@ -1842,6 +1845,8 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id) UCHAR tmp; UCHAR cmd; UCHAR status; + UCHAR memtmp[ESSID_SIZE + 1]; + if (dev == NULL) /* Note that we want interrupts with dev->start == 0 */ return IRQ_NONE; @@ -1901,17 +1906,21 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id) break; case CCS_START_NETWORK: case CCS_JOIN_NETWORK: + memcpy(memtmp, local->sparm.b4.a_current_ess_id, + ESSID_SIZE); + memtmp[ESSID_SIZE] = '\0'; + if (status == CCS_COMMAND_COMPLETE) { if (readb (&pccs->var.start_network.net_initiated) == 1) { dev_dbg(&link->dev, "ray_cs interrupt network \"%s\" started\n", - local->sparm.b4.a_current_ess_id); + memtmp); } else { dev_dbg(&link->dev, "ray_cs interrupt network \"%s\" joined\n", - local->sparm.b4.a_current_ess_id); + memtmp); } memcpy_fromio(&local->bss_id, pccs->var.start_network.bssid, @@ -1939,12 +1948,12 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id) if (status == CCS_START_NETWORK) { dev_dbg(&link->dev, "ray_cs interrupt network \"%s\" start failed\n", - local->sparm.b4.a_current_ess_id); + memtmp); local->timer.function = start_net; } else { dev_dbg(&link->dev, "ray_cs interrupt network \"%s\" join failed\n", - local->sparm.b4.a_current_ess_id); + memtmp); local->timer.function = join_net; } add_timer(&local->timer); From e03e8ddbfd1f04fc1437e5e3e7a8ec05a1355c5f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 20 Jan 2013 19:31:30 +0300 Subject: [PATCH 392/478] b43: N-PHY: fix gain in b43_nphy_get_gain_ctl_workaround_ent() There were no break statements in this switch statement so everything used the default settings. Per Walter Harms's suggestion, I've replaced the switch statement and done a little cleanup. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/b43/tables_nphy.c | 30 +++++++------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 97d4e27bf36f..aaca60c6f575 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -3226,8 +3226,6 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( { struct nphy_gain_ctl_workaround_entry *e; u8 phy_idx; - u8 tr_iso = ghz5 ? dev->dev->bus_sprom->fem.ghz5.tr_iso : - dev->dev->bus_sprom->fem.ghz2.tr_iso; if (!ghz5 && dev->phy.rev >= 6 && dev->phy.radio_rev == 11) return &nphy_gain_ctl_wa_phy6_radio11_ghz2; @@ -3249,6 +3247,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( !b43_channel_type_is_40mhz(dev->phy.channel_type)) e->cliplo_gain = 0x2d; } else if (!ghz5 && dev->phy.rev >= 5) { + static const int gain_data[] = {0x0062, 0x0064, 0x006a, 0x106a, + 0x106c, 0x1074, 0x107c, 0x207c}; + u8 tr_iso = dev->dev->bus_sprom->fem.ghz2.tr_iso; + if (ext_lna) { e->rfseq_init[0] &= ~0x4000; e->rfseq_init[1] &= ~0x4000; @@ -3256,26 +3258,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( e->rfseq_init[3] &= ~0x4000; e->init_gain &= ~0x4000; } - switch (tr_iso) { - case 0: - e->cliplo_gain = 0x0062; - case 1: - e->cliplo_gain = 0x0064; - case 2: - e->cliplo_gain = 0x006a; - case 3: - e->cliplo_gain = 0x106a; - case 4: - e->cliplo_gain = 0x106c; - case 5: - e->cliplo_gain = 0x1074; - case 6: - e->cliplo_gain = 0x107c; - case 7: - e->cliplo_gain = 0x207c; - default: - e->cliplo_gain = 0x106a; - } + if (tr_iso > 7) + tr_iso = 3; + e->cliplo_gain = gain_data[tr_iso]; + } else if (ghz5 && dev->phy.rev == 4 && ext_lna) { e->rfseq_init[0] &= ~0x4000; e->rfseq_init[1] &= ~0x4000; From f49aabf816afa5913e5001bd1db1c3efb4c6e19e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 13 Jan 2013 23:03:42 +0300 Subject: [PATCH 393/478] prism54: bug in getting auth type There is a missing break statement so SHARED_KEY authentication doesn't work. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/prism54/isl_ioctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 4e44b1af119a..1c22b81e6ef3 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -1503,6 +1503,7 @@ static int prism54_get_auth(struct net_device *ndev, case DOT11_AUTH_BOTH: case DOT11_AUTH_SK: param->value = IW_AUTH_ALG_SHARED_KEY; + break; case DOT11_AUTH_NONE: default: param->value = 0; From 84e9e8ebd369679a958200a8baca96aafb2393bb Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 17 Jan 2013 17:34:32 +0100 Subject: [PATCH 394/478] rt2x00: Improve TX status handling for BlockAckReq frames Since rt2800 hardware isn't capable of reporting the TX status of BlockAckReq frames implement the TX status handling of BARs in rt2x00lib. We keep track of all BARs that are send out and try to match incoming BAs to the appropriate BARs. This allows us to report a more or less accurate TX status for BAR frames which in turn improves BA session stability. This is loosley based on Christian Lamparter's patch for carl9170 "carl9170: fix HT peer BA session corruption". We have to walk the list of pending BARs for every rx'red BA even though most BAs don't belong to any of these BARs as they are just acknowledging an AMPDU. To keep that overhead low use RCU which allows us to walk the list of pending BARs without the need to acquire a lock. This however requires us to _copy_ relevant information from the BAR (RA, TA, control field, start sequence number) into our BAR list entry. Signed-off-by: Helmut Schaa Tested-by: Andreas Hartmann Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 6 +- drivers/net/wireless/rt2x00/rt2x00.h | 20 +++++ drivers/net/wireless/rt2x00/rt2x00dev.c | 101 +++++++++++++++++++++- drivers/net/wireless/rt2x00/rt2x00queue.c | 47 ++++++++++ 4 files changed, 169 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index f139a913c25a..a5c694f23d33 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -1296,8 +1296,7 @@ void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, !(filter_flags & FIF_PSPOLL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, - !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, 0); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, @@ -5146,8 +5145,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_REPORTS_TX_ACK_STATUS | - IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL; + IEEE80211_HW_REPORTS_TX_ACK_STATUS; /* * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 0751b35ef6dc..b52512b8ac5f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1016,6 +1016,26 @@ struct rt2x00_dev { * Protect the interrupt mask register. */ spinlock_t irqmask_lock; + + /* + * List of BlockAckReq TX entries that need driver BlockAck processing. + */ + struct list_head bar_list; + spinlock_t bar_list_lock; +}; + +struct rt2x00_bar_list_entry { + struct list_head list; + struct rcu_head head; + + struct queue_entry *entry; + int block_acked; + + /* Relevant parts of the IEEE80211 BAR header */ + __u8 ra[6]; + __u8 ta[6]; + __le16 control; + __le16 start_seq_num; }; /* diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 44f8b3f3cbed..b40a53857498 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -271,6 +271,50 @@ void rt2x00lib_dmadone(struct queue_entry *entry) } EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); +static inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_bar *bar = (void *) entry->skb->data; + struct rt2x00_bar_list_entry *bar_entry; + int ret; + + if (likely(!ieee80211_is_back_req(bar->frame_control))) + return 0; + + /* + * Unlike all other frames, the status report for BARs does + * not directly come from the hardware as it is incapable of + * matching a BA to a previously send BAR. The hardware will + * report all BARs as if they weren't acked at all. + * + * Instead the RX-path will scan for incoming BAs and set the + * block_acked flag if it sees one that was likely caused by + * a BAR from us. + * + * Remove remaining BARs here and return their status for + * TX done processing. + */ + ret = 0; + rcu_read_lock(); + list_for_each_entry_rcu(bar_entry, &rt2x00dev->bar_list, list) { + if (bar_entry->entry != entry) + continue; + + spin_lock_bh(&rt2x00dev->bar_list_lock); + /* Return whether this BAR was blockacked or not */ + ret = bar_entry->block_acked; + /* Remove the BAR from our checklist */ + list_del_rcu(&bar_entry->list); + spin_unlock_bh(&rt2x00dev->bar_list_lock); + kfree_rcu(bar_entry, head); + + break; + } + rcu_read_unlock(); + + return ret; +} + void rt2x00lib_txdone(struct queue_entry *entry, struct txdone_entry_desc *txdesc) { @@ -324,9 +368,12 @@ void rt2x00lib_txdone(struct queue_entry *entry, rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb); /* - * Determine if the frame has been successfully transmitted. + * Determine if the frame has been successfully transmitted and + * remove BARs from our check list while checking for their + * TX status. */ success = + rt2x00lib_txdone_bar_status(entry) || test_bit(TXDONE_SUCCESS, &txdesc->flags) || test_bit(TXDONE_UNKNOWN, &txdesc->flags); @@ -491,6 +538,50 @@ static void rt2x00lib_sleep(struct work_struct *work) IEEE80211_CONF_CHANGE_PS); } +static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00_bar_list_entry *entry; + struct ieee80211_bar *ba = (void *)skb->data; + + if (likely(!ieee80211_is_back(ba->frame_control))) + return; + + if (rxdesc->size < sizeof(*ba) + FCS_LEN) + return; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &rt2x00dev->bar_list, list) { + + if (ba->start_seq_num != entry->start_seq_num) + continue; + +#define TID_CHECK(a, b) ( \ + ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) == \ + ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK))) \ + + if (!TID_CHECK(ba->control, entry->control)) + continue; + +#undef TID_CHECK + + if (compare_ether_addr(ba->ra, entry->ta)) + continue; + + if (compare_ether_addr(ba->ta, entry->ra)) + continue; + + /* Mark BAR since we received the according BA */ + spin_lock_bh(&rt2x00dev->bar_list_lock); + entry->block_acked = 1; + spin_unlock_bh(&rt2x00dev->bar_list_lock); + break; + } + rcu_read_unlock(); + +} + static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb, struct rxdone_entry_desc *rxdesc) @@ -673,6 +764,12 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) */ rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); + /* + * Check for incoming BlockAcks to match to the BlockAckReqs + * we've send out. + */ + rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc); + /* * Update extra components */ @@ -1183,6 +1280,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) spin_lock_init(&rt2x00dev->irqmask_lock); mutex_init(&rt2x00dev->csr_mutex); + INIT_LIST_HEAD(&rt2x00dev->bar_list); + spin_lock_init(&rt2x00dev->bar_list_lock); set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index e488b944a034..f35d85a71bbc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -582,6 +582,48 @@ static void rt2x00queue_kick_tx_queue(struct data_queue *queue, queue->rt2x00dev->ops->lib->kick_queue(queue); } +static void rt2x00queue_bar_check(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_bar *bar = (void *) (entry->skb->data + + rt2x00dev->ops->extra_tx_headroom); + struct rt2x00_bar_list_entry *bar_entry; + + if (likely(!ieee80211_is_back_req(bar->frame_control))) + return; + + bar_entry = kmalloc(sizeof(*bar_entry), GFP_ATOMIC); + + /* + * If the alloc fails we still send the BAR out but just don't track + * it in our bar list. And as a result we will report it to mac80211 + * back as failed. + */ + if (!bar_entry) + return; + + bar_entry->entry = entry; + bar_entry->block_acked = 0; + + /* + * Copy the relevant parts of the 802.11 BAR into out check list + * such that we can use RCU for less-overhead in the RX path since + * sending BARs and processing the according BlockAck should be + * the exception. + */ + memcpy(bar_entry->ra, bar->ra, sizeof(bar->ra)); + memcpy(bar_entry->ta, bar->ta, sizeof(bar->ta)); + bar_entry->control = bar->control; + bar_entry->start_seq_num = bar->start_seq_num; + + /* + * Insert BAR into our BAR check list. + */ + spin_lock_bh(&rt2x00dev->bar_list_lock); + list_add_tail_rcu(&bar_entry->list, &rt2x00dev->bar_list); + spin_unlock_bh(&rt2x00dev->bar_list_lock); +} + int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, bool local) { @@ -680,6 +722,11 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, goto out; } + /* + * Put BlockAckReqs into our check list for driver BA processing. + */ + rt2x00queue_bar_check(entry); + set_bit(ENTRY_DATA_PENDING, &entry->flags); rt2x00queue_index_inc(entry, Q_INDEX); From 7532c7d0138a4984eb0c8b12b9c310cf8a6d6977 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 17 Jan 2013 17:41:57 -0800 Subject: [PATCH 395/478] mwifiex: correction in status codes used for association failure When AP responds with appropriate status code, we forward that code correctly to cfg80211. But sometimes when there is no response from AP, our firmware uses proprietary status codes. We will map authentication timeout to WLAN_STATUS_AUTH_TIMEOUT and other proprietary codes to WLAN_STATUS_UNSPECIFIED_FAILURE. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/fw.h | 3 +++ drivers/net/wireless/mwifiex/join.c | 20 +++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 41c85dd78084..ebe2f6a7984c 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -330,6 +330,9 @@ enum P2P_MODES { #define HOST_SLEEP_CFG_GPIO_DEF 0xff #define HOST_SLEEP_CFG_GAP_DEF 0 +#define MWIFIEX_TIMEOUT_FOR_AP_RESP 0xfffc +#define MWIFIEX_STATUS_CODE_AUTH_TIMEOUT 2 + #define CMD_F_HOSTCMD (1 << 0) #define CMD_F_CANCELED (1 << 1) diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 3c7cabeddf76..893d809ba83c 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -615,23 +615,33 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, struct ieee_types_assoc_rsp *assoc_rsp; struct mwifiex_bssdescriptor *bss_desc; u8 enable_data = true; + u16 cap_info, status_code; assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; + cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap); + status_code = le16_to_cpu(assoc_rsp->status_code); + priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, sizeof(priv->assoc_rsp_buf)); memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); - if (le16_to_cpu(assoc_rsp->status_code)) { + if (status_code) { priv->adapter->dbg.num_cmd_assoc_failure++; dev_err(priv->adapter->dev, "ASSOC_RESP: failed, status code=%d err=%#x a_id=%#x\n", - le16_to_cpu(assoc_rsp->status_code), - le16_to_cpu(assoc_rsp->cap_info_bitmap), - le16_to_cpu(assoc_rsp->a_id)); + status_code, cap_info, le16_to_cpu(assoc_rsp->a_id)); + + if (cap_info == MWIFIEX_TIMEOUT_FOR_AP_RESP) { + if (status_code == MWIFIEX_STATUS_CODE_AUTH_TIMEOUT) + ret = WLAN_STATUS_AUTH_TIMEOUT; + else + ret = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { + ret = status_code; + } - ret = le16_to_cpu(assoc_rsp->status_code); goto done; } From a2ca8ecb8ffc985e82c9570c3837408f7efe8c9d Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 19 Jan 2013 01:24:01 +0400 Subject: [PATCH 396/478] mwl8k: don't return zero on failure paths in mwl8k_probe[_hw]() If pci_iomap() fails in mwl8k_probe(), it breaks off initialization, deallocates all resources, but returns zero. There is a similar issue when priv->rxd_ops is NULL in mwl8k_probe_hw(). The patch adds proper error code return values. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a9c078899252..68357641c414 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5555,6 +5555,7 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw) if (priv->rxd_ops == NULL) { wiphy_err(hw->wiphy, "Driver does not have AP firmware image support for this hardware\n"); + rc = -ENOENT; goto err_stop_firmware; } } else { @@ -5901,6 +5902,7 @@ static int mwl8k_probe(struct pci_dev *pdev, priv->sram = pci_iomap(pdev, 0, 0x10000); if (priv->sram == NULL) { wiphy_err(hw->wiphy, "Cannot map device SRAM\n"); + rc = -EIO; goto err_iounmap; } @@ -5913,6 +5915,7 @@ static int mwl8k_probe(struct pci_dev *pdev, priv->regs = pci_iomap(pdev, 2, 0x10000); if (priv->regs == NULL) { wiphy_err(hw->wiphy, "Cannot map device registers\n"); + rc = -EIO; goto err_iounmap; } } From 662845057f2d063461c88a97b737ec7f66daece0 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 19 Jan 2013 16:56:34 +0400 Subject: [PATCH 397/478] iwlegacy: don't return zero on failure paths in il4965_pci_probe() If hardware is not ready, il4965_pci_probe() breaks off initialization, deallocates all resources, but returns zero. The patch adds -EIO as return value in this case. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/4965-mac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 10fc2493f415..f1dc04006564 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -6556,6 +6556,7 @@ il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) il4965_prepare_card_hw(il); if (!il->hw_ready) { IL_WARN("Failed, HW not ready\n"); + err = -EIO; goto out_iounmap; } From eee569e403861392dc693ab984ede5f0610dde29 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Sat, 19 Jan 2013 13:56:36 +0100 Subject: [PATCH 398/478] net: wireless/rtlwifi: fix uninitialized variable issue The use of variable packet_beacon might be uninitialized in the three files. Signed-off-by: Cong Ding Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192de/trx.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192se/trx.c | 2 +- drivers/net/wireless/rtlwifi/rtl8723ae/trx.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index f9f3861046c1..3c8754e9b394 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -452,7 +452,7 @@ static void _rtl92de_translate_rx_signal_stuff(struct ieee80211_hw *hw, u8 *praddr; u16 type, cfc; __le16 fc; - bool packet_matchbssid, packet_toself, packet_beacon; + bool packet_matchbssid, packet_toself, packet_beacon = false; tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift; hdr = (struct ieee80211_hdr *)tmp_buf; diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 0e9f6ebf078a..18645146abe1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -480,7 +480,7 @@ static void _rtl92se_translate_rx_signal_stuff(struct ieee80211_hw *hw, u8 *praddr; __le16 fc; u16 type, cfc; - bool packet_matchbssid, packet_toself, packet_beacon; + bool packet_matchbssid, packet_toself, packet_beacon = false; tmp_buf = skb->data + pstats->rx_drvinfo_size + pstats->rx_bufshift; diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index 87331d826d73..6645a506a603 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -247,7 +247,7 @@ static void _rtl8723ae_translate_rx_signal_stuff(struct ieee80211_hw *hw, u8 *psaddr; __le16 fc; u16 type; - bool packet_matchbssid, packet_toself, packet_beacon; + bool packet_matchbssid, packet_toself, packet_beacon = false; tmp_buf = skb->data + pstatus->rx_drvinfo_size + pstatus->rx_bufshift; From 0a62acb1c9da58b54cb1c9fa6604a36507a61d48 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 18:51:52 +0100 Subject: [PATCH 399/478] ath9k: stop rx after tx Completing frame transmission can fail if the rx engine is stopped prematurely, as the hw might be waiting for an ACK from the other side. Shutting down tx before rx might make the DMA shutdown more reliable. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 32417fd4bf5a..407199ba79db 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -196,10 +196,10 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); - if (!ath_stoprecv(sc)) + if (!ath_drain_all_txq(sc, retry_tx)) ret = false; - if (!ath_drain_all_txq(sc, retry_tx)) + if (!ath_stoprecv(sc)) ret = false; if (!flush) { From 1381559ba48a04ca7c98f1b4c487bd44d0b75db5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 18:51:53 +0100 Subject: [PATCH 400/478] ath9k: clean up processing of pending tx frames on reset Dropping packets from aggregation sessions is usually not a good idea, as it might upset the synchronization of the BlockAck receive window of the remote node. The use of the retry_tx parameter to reset/tx-drain functions also seemed a bit arbitrary. This patch removes this parameter altogether and ensures that pending tx frames are not dropped for no good reason. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 5 ++- drivers/net/wireless/ath/ath9k/beacon.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 41 +++++++++++------------ drivers/net/wireless/ath/ath9k/xmit.c | 43 ++++++------------------- 4 files changed, 32 insertions(+), 59 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 094a9322f092..885affd28f6e 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -334,9 +334,8 @@ void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq); void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq); void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq); void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq); -bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx); -void ath_draintxq(struct ath_softc *sc, - struct ath_txq *txq, bool retry_tx); +bool ath_drain_all_txq(struct ath_softc *sc); +void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq); void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an); void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an); void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 531fffd801a3..a129f83806f3 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -198,7 +198,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, if (sc->nvifs > 1) { ath_dbg(common, BEACON, "Flushing previous cabq traffic\n"); - ath_draintxq(sc, cabq, false); + ath_draintxq(sc, cabq); } } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 407199ba79db..3e5082c5f5f0 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -182,7 +182,7 @@ static void ath_restart_work(struct ath_softc *sc) ath_start_ani(sc); } -static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) +static bool ath_prepare_reset(struct ath_softc *sc, bool flush) { struct ath_hw *ah = sc->sc_ah; bool ret = true; @@ -196,7 +196,7 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); - if (!ath_drain_all_txq(sc, retry_tx)) + if (!ath_drain_all_txq(sc)) ret = false; if (!ath_stoprecv(sc)) @@ -255,8 +255,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) return true; } -static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, - bool retry_tx) +static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); @@ -280,7 +279,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, hchan = ah->curchan; } - if (!ath_prepare_reset(sc, retry_tx, flush)) + if (!ath_prepare_reset(sc, flush)) fastcc = false; ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", @@ -319,7 +318,7 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return -EIO; - r = ath_reset_internal(sc, hchan, false); + r = ath_reset_internal(sc, hchan); return r; } @@ -549,23 +548,21 @@ chip_reset: #undef SCHED_INTR } -static int ath_reset(struct ath_softc *sc, bool retry_tx) +static int ath_reset(struct ath_softc *sc) { - int r; + int i, r; ath9k_ps_wakeup(sc); - r = ath_reset_internal(sc, NULL, retry_tx); + r = ath_reset_internal(sc, NULL); - if (retry_tx) { - int i; - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { - if (ATH_TXQ_SETUP(sc, i)) { - spin_lock_bh(&sc->tx.txq[i].axq_lock); - ath_txq_schedule(sc, &sc->tx.txq[i]); - spin_unlock_bh(&sc->tx.txq[i].axq_lock); - } - } + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (!ATH_TXQ_SETUP(sc, i)) + continue; + + spin_lock_bh(&sc->tx.txq[i].axq_lock); + ath_txq_schedule(sc, &sc->tx.txq[i]); + spin_unlock_bh(&sc->tx.txq[i].axq_lock); } ath9k_ps_restore(sc); @@ -586,7 +583,7 @@ void ath_reset_work(struct work_struct *work) { struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work); - ath_reset(sc, true); + ath_reset(sc); } /**********************/ @@ -804,7 +801,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_hw_cfg_gpio_input(ah, ah->led_pin); } - ath_prepare_reset(sc, false, true); + ath_prepare_reset(sc, true); if (sc->rx.frag) { dev_kfree_skb_any(sc->rx.frag); @@ -1816,11 +1813,11 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop) if (drop) { ath9k_ps_wakeup(sc); spin_lock_bh(&sc->sc_pcu_lock); - drain_txq = ath_drain_all_txq(sc, false); + drain_txq = ath_drain_all_txq(sc); spin_unlock_bh(&sc->sc_pcu_lock); if (!drain_txq) - ath_reset(sc, false); + ath_reset(sc); ath9k_ps_restore(sc); ieee80211_wake_queues(hw); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index ca4a0341294f..ac9ee67fbaa9 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -378,7 +378,7 @@ static void ath_tx_count_frames(struct ath_softc *sc, struct ath_buf *bf, static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf, struct list_head *bf_q, - struct ath_tx_status *ts, int txok, bool retry) + struct ath_tx_status *ts, int txok) { struct ath_node *an = NULL; struct sk_buff *skb; @@ -490,7 +490,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, } else if (!isaggr && txok) { /* transmit completion */ acked_cnt++; - } else if ((tid->state & AGGR_CLEANUP) || !retry) { + } else if (tid->state & AGGR_CLEANUP) { /* * cleanup in progress, just fail * the un-acked sub-frames @@ -1331,23 +1331,6 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid /* Queue Management */ /********************/ -static void ath_txq_drain_pending_buffers(struct ath_softc *sc, - struct ath_txq *txq) -{ - struct ath_atx_ac *ac, *ac_tmp; - struct ath_atx_tid *tid, *tid_tmp; - - list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) { - list_del(&ac->list); - ac->sched = false; - list_for_each_entry_safe(tid, tid_tmp, &ac->tid_q, list) { - list_del(&tid->list); - tid->sched = false; - ath_tid_drain(sc, txq, tid); - } - } -} - struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) { struct ath_hw *ah = sc->sc_ah; @@ -1477,7 +1460,7 @@ static bool bf_is_ampdu_not_probing(struct ath_buf *bf) } static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, - struct list_head *list, bool retry_tx) + struct list_head *list) { struct ath_buf *bf, *lastbf; struct list_head bf_head; @@ -1505,8 +1488,7 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, txq->axq_ampdu_depth--; if (bf_isampdu(bf)) - ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, 0, - retry_tx); + ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, 0); else ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); } @@ -1518,7 +1500,7 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, * This assumes output has been stopped and * we do not need to block ath_tx_tasklet. */ -void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) +void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq) { ath_txq_lock(sc, txq); @@ -1526,8 +1508,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) int idx = txq->txq_tailidx; while (!list_empty(&txq->txq_fifo[idx])) { - ath_drain_txq_list(sc, txq, &txq->txq_fifo[idx], - retry_tx); + ath_drain_txq_list(sc, txq, &txq->txq_fifo[idx]); INCR(idx, ATH_TXFIFO_DEPTH); } @@ -1536,16 +1517,12 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) txq->axq_link = NULL; txq->axq_tx_inprogress = false; - ath_drain_txq_list(sc, txq, &txq->axq_q, retry_tx); - - /* flush any pending frames if aggregation is enabled */ - if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !retry_tx) - ath_txq_drain_pending_buffers(sc, txq); + ath_drain_txq_list(sc, txq, &txq->axq_q); ath_txq_unlock_complete(sc, txq); } -bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) +bool ath_drain_all_txq(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -1581,7 +1558,7 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) */ txq = &sc->tx.txq[i]; txq->stopped = false; - ath_draintxq(sc, txq, retry_tx); + ath_draintxq(sc, txq); } return !npend; @@ -2191,7 +2168,7 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok); ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok); } else - ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok, true); + ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) ath_txq_schedule(sc, txq); From 81b519504f3b2e72c8ff282a03ecc01879f28e31 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 18:51:54 +0100 Subject: [PATCH 401/478] ath9k: use ath_tx_process_buffer instead of open-coding similar code Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 69 +++++++++++++-------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index ac9ee67fbaa9..feacaafee959 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -604,6 +604,37 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, ath9k_queue_reset(sc, RESET_TYPE_TX_ERROR); } +static bool bf_is_ampdu_not_probing(struct ath_buf *bf) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(bf->bf_mpdu); + return bf_isampdu(bf) && !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); +} + +static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, + struct ath_tx_status *ts, struct ath_buf *bf, + struct list_head *bf_head) +{ + bool txok, flush; + + txok = !(ts->ts_status & ATH9K_TXERR_MASK); + flush = !!(ts->ts_status & ATH9K_TX_FLUSH); + txq->axq_tx_inprogress = false; + + txq->axq_depth--; + if (bf_is_ampdu_not_probing(bf)) + txq->axq_ampdu_depth--; + + if (!bf_isampdu(bf)) { + if (!flush) + ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok); + ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok); + } else + ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok); + + if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !flush) + ath_txq_schedule(sc, txq); +} + static bool ath_lookup_legacy(struct ath_buf *bf) { struct sk_buff *skb; @@ -1453,12 +1484,6 @@ int ath_cabq_update(struct ath_softc *sc) return 0; } -static bool bf_is_ampdu_not_probing(struct ath_buf *bf) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(bf->bf_mpdu); - return bf_isampdu(bf) && !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); -} - static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, struct list_head *list) { @@ -1482,15 +1507,7 @@ static void ath_drain_txq_list(struct ath_softc *sc, struct ath_txq *txq, lastbf = bf->bf_lastbf; list_cut_position(&bf_head, list, &lastbf->list); - - txq->axq_depth--; - if (bf_is_ampdu_not_probing(bf)) - txq->axq_ampdu_depth--; - - if (bf_isampdu(bf)) - ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, 0); - else - ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); + ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head); } } @@ -2152,28 +2169,6 @@ static void ath_tx_rc_status(struct ath_softc *sc, struct ath_buf *bf, tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1; } -static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, - struct ath_tx_status *ts, struct ath_buf *bf, - struct list_head *bf_head) -{ - int txok; - - txq->axq_depth--; - txok = !(ts->ts_status & ATH9K_TXERR_MASK); - txq->axq_tx_inprogress = false; - if (bf_is_ampdu_not_probing(bf)) - txq->axq_ampdu_depth--; - - if (!bf_isampdu(bf)) { - ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok); - ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok); - } else - ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok); - - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) - ath_txq_schedule(sc, txq); -} - static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hw *ah = sc->sc_ah; From 0166b4beec575f9740b7a0843675173c2e00cdbc Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 18:51:55 +0100 Subject: [PATCH 402/478] ath9k_hw: make the initval parameter to ath9k_hw_write_array const Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 2 +- drivers/net/wireless/ath/ath9k/hw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index a7448d1f5460..0d84c1cd354f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -203,7 +203,7 @@ void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, udelay(hw_delay + BASE_ACTIVATE_DELAY); } -void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array, +void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array, int column, unsigned int *writecnt) { int r; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 195613ff72ee..13b97e713f9b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1013,7 +1013,7 @@ void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, int hw_delay); bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); -void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array, +void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array, int column, unsigned int *writecnt); u32 ath9k_hw_reverse_bits(u32 val, u32 n); u16 ath9k_hw_computetxtime(struct ath_hw *ah, From f5ffe23aa0a8f6512952cf393daf86cbd6e4685d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 18:51:57 +0100 Subject: [PATCH 403/478] ath9k_hw: remove ath9k_hw_ani_setup and its variables They are no longer needed for ANI functionality Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 17 ----------------- drivers/net/wireless/ath/ath9k/ani.h | 1 - drivers/net/wireless/ath/ath9k/hw.c | 4 +--- drivers/net/wireless/ath/ath9k/hw.h | 4 ---- 4 files changed, 1 insertion(+), 25 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index e09ec40ce71a..297828791167 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -489,23 +489,6 @@ void ath9k_hw_disable_mib_counters(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_disable_mib_counters); -void ath9k_hw_ani_setup(struct ath_hw *ah) -{ - int i; - - static const int totalSizeDesired[] = { -55, -55, -55, -55, -62 }; - static const int coarseHigh[] = { -14, -14, -14, -14, -12 }; - static const int coarseLow[] = { -64, -64, -64, -64, -70 }; - static const int firpwr[] = { -78, -78, -78, -78, -80 }; - - for (i = 0; i < 5; i++) { - ah->totalSizeDesired[i] = totalSizeDesired[i]; - ah->coarse_high[i] = coarseHigh[i]; - ah->coarse_low[i] = coarseLow[i]; - ah->firpwr[i] = firpwr[i]; - } -} - void ath9k_hw_ani_init(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 1485bf5e3518..385bdba8ae50 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -147,7 +147,6 @@ struct ar5416Stats { void ath9k_enable_mib_counters(struct ath_hw *ah); void ath9k_hw_disable_mib_counters(struct ath_hw *ah); -void ath9k_hw_ani_setup(struct ath_hw *ah); void ath9k_hw_ani_init(struct ath_hw *ah); #endif /* ANI_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 0d84c1cd354f..42cf3c7f1e25 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -549,10 +549,8 @@ static int ath9k_hw_post_init(struct ath_hw *ah) ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); - if (ah->config.enable_ani) { - ath9k_hw_ani_setup(ah); + if (ah->config.enable_ani) ath9k_hw_ani_init(ah); - } return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 13b97e713f9b..0a3a1af007f7 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -864,10 +864,6 @@ struct ath_hw { /* ANI */ u32 proc_phyerr; u32 aniperiod; - int totalSizeDesired[5]; - int coarse_high[5]; - int coarse_low[5]; - int firpwr[5]; enum ath9k_ani_cmd ani_function; u32 ani_skip_count; From 9dbac67da314ccdab3bb7f429b4936e59d5389b7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 18:51:58 +0100 Subject: [PATCH 404/478] ath9k_hw: reduce struct ar5416AniState size It is kept per-channel, so removing unnecessary (or constant) fields from it can save quite a bit of memory. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 13 ++++++------- drivers/net/wireless/ath/ath9k/ani.h | 7 ------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 297828791167..a9218a198758 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -152,7 +152,8 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->ofdmNoiseImmunityLevel, immunityLevel, BEACON_RSSI(ah), - aniState->rssiThrLow, aniState->rssiThrHigh); + ATH9K_ANI_RSSI_THR_LOW, + ATH9K_ANI_RSSI_THR_HIGH); if (!scan) aniState->ofdmNoiseImmunityLevel = immunityLevel; @@ -173,7 +174,7 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, weak_sig = entry_ofdm->ofdm_weak_signal_on; if (ah->opmode == NL80211_IFTYPE_STATION && - BEACON_RSSI(ah) <= aniState->rssiThrHigh) + BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH) weak_sig = true; if (aniState->ofdmWeakSigDetect != weak_sig) @@ -216,11 +217,11 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel, ath_dbg(common, ANI, "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->cckNoiseImmunityLevel, immunityLevel, - BEACON_RSSI(ah), aniState->rssiThrLow, - aniState->rssiThrHigh); + BEACON_RSSI(ah), ATH9K_ANI_RSSI_THR_LOW, + ATH9K_ANI_RSSI_THR_HIGH); if (ah->opmode == NL80211_IFTYPE_STATION && - BEACON_RSSI(ah) <= aniState->rssiThrLow && + BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_LOW && immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI) immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI; @@ -514,8 +515,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ani->ofdmsTurn = true; - ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; - ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL; diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 385bdba8ae50..dddb1361039a 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -104,7 +104,6 @@ struct ath9k_ani_default { }; struct ar5416AniState { - struct ath9k_channel *c; u8 noiseImmunityLevel; u8 ofdmNoiseImmunityLevel; u8 cckNoiseImmunityLevel; @@ -113,15 +112,9 @@ struct ar5416AniState { u8 spurImmunityLevel; u8 firstepLevel; u8 ofdmWeakSigDetect; - u8 cckWeakSigThreshold; u32 listenTime; - int32_t rssiThrLow; - int32_t rssiThrHigh; u32 ofdmPhyErrCount; u32 cckPhyErrCount; - int16_t pktRssi[2]; - int16_t ofdmErrRssi[2]; - int16_t cckErrRssi[2]; struct ath9k_ani_default iniDef; }; From c5e818ef081c4144177fdbdeed154332cd7e4d7a Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 20 Jan 2013 18:51:59 +0100 Subject: [PATCH 405/478] ath9k_hw: remove a useless WARN_ON &ah->curchan->ani can never be NULL Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index a9218a198758..7ecd40f07a74 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -419,9 +419,6 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) return; aniState = &ah->curchan->ani; - if (WARN_ON(!aniState)) - return; - if (!ath9k_hw_ani_read_counters(ah)) return; From fe038884a83b85f2bb61c77609eacb5cf613d3fb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jan 2013 16:15:34 +0200 Subject: [PATCH 406/478] Bluetooth: Fix Class of Device indication when powering off When a HCI device is powered off the Management interface specification dictates that the class of device value is indicated as zero. This patch fixes sending of the appropriate class of device changed event when a HCI device is powered off. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fc171f222ba4..54f3ddba9139 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2987,7 +2987,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) } } else { u8 status = MGMT_STATUS_NOT_POWERED; + u8 zero_cod[] = { 0, 0, 0 }; + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); + + if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) + mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, + zero_cod, sizeof(zero_cod), NULL); } err = new_settings(hdev, match.sk); From f950a30e2433f049c17fc47caced1397d25373a6 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Fri, 18 Jan 2013 12:48:07 +0100 Subject: [PATCH 407/478] Bluetooth: Fix pair device command reply if adapter is powered off According to Bluetooth Management API specification Pair Device Command should generate command complete event on both success and failure. This fix replying with command status (which lacks address info) when adapter is powered off. Signed-off-by: Szymon Janc Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 54f3ddba9139..36b23101d651 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1939,11 +1939,15 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG(""); + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_NOT_POWERED); + err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); goto unlock; } @@ -1960,10 +1964,6 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, cp->addr.type, sec_level, auth_type); - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - if (IS_ERR(conn)) { int status; From 4ae14301c3b180adaf6b72285499e7404819a023 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:13 +0200 Subject: [PATCH 408/478] Bluetooth: Fix checking for correct mgmt_load_link_keys parameters The debug_keys parameter is only allowed to have the values 0x00 and 0x01. Any other value should result in a proper command status with MGMT_STATUS_INVALID_PARAMS. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 36b23101d651..d9b042efbf42 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1519,6 +1519,10 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_INVALID_PARAMS); } + if (cp->debug_keys != 0x00 && cp->debug_keys != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); + BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys, key_count); From e57e619f463e7841940ef1b98969e23f71f5ee8a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:14 +0200 Subject: [PATCH 409/478] Bluetooth: Fix returning proper mgmt status for Load LTKs Failures of mgmt commands should be indicated with valid mgmt status codes, and EINVAL is not one of them. Instead MGMT_STATUS_INVALID_PARAMS should be returned. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d9b042efbf42..a050eee61623 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2716,7 +2716,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, BT_ERR("load_keys: expected %u bytes, got %u bytes", len, expected_len); return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); } BT_DBG("%s key_count %u", hdev->name, key_count); From 54ad6d8a5afe1a6d162d8d229a3d8fc48b254d24 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:15 +0200 Subject: [PATCH 410/478] Bluetooth: Fix checking for proper key->master value in Load LTKs The allowed values for the key->master parameter in the Load LTKs command are 0x00 and 0x01. If there is a key in the list with some other value the command should fail with a proper invalid params response. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a050eee61623..c7ec47ce94e5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2721,6 +2721,15 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s key_count %u", hdev->name, key_count); + for (i = 0; i < key_count; i++) { + struct mgmt_ltk_info *key = &cp->keys[i]; + + if (key->master != 0x00 && key->master != 0x01) + return cmd_status(sk, hdev->id, + MGMT_OP_LOAD_LONG_TERM_KEYS, + MGMT_STATUS_INVALID_PARAMS); + } + hci_dev_lock(hdev); hci_smp_ltks_clear(hdev); From 3f706b7205456c90cdc91e21eab36e2fcf4a8bce Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:16 +0200 Subject: [PATCH 411/478] Bluetooth: Refactor valid LTK data testing into its own function This patch refactors valid LTK data testing into its own function. This will help keep the code readable since there are several tests still missing that need to be done on the LTK data. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c7ec47ce94e5..cd758994b02f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2701,6 +2701,13 @@ done: return err; } +static bool ltk_is_valid(struct mgmt_ltk_info *key) +{ + if (key->master != 0x00 && key->master != 0x01) + return false; + return true; +} + static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, void *cp_data, u16 len) { @@ -2724,7 +2731,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, for (i = 0; i < key_count; i++) { struct mgmt_ltk_info *key = &cp->keys[i]; - if (key->master != 0x00 && key->master != 0x01) + if (!ltk_is_valid(key)) return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, MGMT_STATUS_INVALID_PARAMS); From 44b20d33962a73ca14b934540e9168e0da1b49ab Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:17 +0200 Subject: [PATCH 412/478] Bluetooth: Check for valid key->authenticated value for LTKs This patch adds necessary checks for the two allowed values of the authenticated parameter of each Long Term Key, i.e. 0x00 and 0x01. If any other value is encountered the valid response is to return invalid params to user space. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index cd758994b02f..bc04c444c98e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2703,6 +2703,8 @@ done: static bool ltk_is_valid(struct mgmt_ltk_info *key) { + if (key->authenticated != 0x00 && key->authenticated != 0x01) + return false; if (key->master != 0x00 && key->master != 0x01) return false; return true; From 679efe2b4fcbe575bc4c94c410039e35c169bfb6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:18 +0200 Subject: [PATCH 413/478] Bluetooth: Add helper functions for testing bdaddr types This patch adds two helper functions to test for valid bdaddr type values. These will be particularely useful in the mgmt code to check that user space has passed valid values to the kernel. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/bluetooth.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 2554b3f5222a..9531beee09b5 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -166,6 +166,29 @@ typedef struct { #define BDADDR_LE_PUBLIC 0x01 #define BDADDR_LE_RANDOM 0x02 +static inline bool bdaddr_type_is_valid(__u8 type) +{ + switch (type) { + case BDADDR_BREDR: + case BDADDR_LE_PUBLIC: + case BDADDR_LE_RANDOM: + return true; + } + + return false; +} + +static inline bool bdaddr_type_is_le(__u8 type) +{ + switch (type) { + case BDADDR_LE_PUBLIC: + case BDADDR_LE_RANDOM: + return true; + } + + return false; +} + #define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} }) #define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} }) From 4ee71b2017336f68128515bdbe7c946a39aa9250 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:19 +0200 Subject: [PATCH 414/478] Bluetooth: Fix checking for valid address type values in mgmt commands This patch adds checks for valid address type values passed to mgmt commands. If an invalid address type is encountered the code will return a proper invalid params response. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bc04c444c98e..7dd2de1c2152 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1526,6 +1526,14 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys, key_count); + for (i = 0; i < key_count; i++) { + struct mgmt_link_key_info *key = &cp->keys[i]; + + if (key->addr.type != BDADDR_BREDR) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); + } + hci_dev_lock(hdev); hci_link_keys_clear(hdev); @@ -1573,12 +1581,17 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, struct hci_conn *conn; int err; - hci_dev_lock(hdev); - memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + + hci_dev_lock(hdev); + if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); @@ -1643,6 +1656,10 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG(""); + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { @@ -1947,6 +1964,11 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -2564,6 +2586,10 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_status(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); @@ -2589,6 +2615,10 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_status(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); @@ -2707,6 +2737,8 @@ static bool ltk_is_valid(struct mgmt_ltk_info *key) return false; if (key->master != 0x00 && key->master != 0x01) return false; + if (!bdaddr_type_is_le(key->addr.type)) + return false; return true; } From 118da70b760f04bb2b8130ced97a9f9cc173440a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:20 +0200 Subject: [PATCH 415/478] Bluetooth: Fix checking for valid disconnect parameters in unpair_device The valid values for the Disconnect parameter in the Unpair Device command are 0x00 and 0x01. If any other value is encountered the command should fail with the appropriate invalid params response. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7dd2de1c2152..e5e865d8afa8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1590,6 +1590,11 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_INVALID_PARAMS, &rp, sizeof(rp)); + if (cp->disconnect != 0x00 && cp->disconnect != 0x01) + return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { From 06a63b19e9eb90402e465d60d4c2564afd3ca211 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:21 +0200 Subject: [PATCH 416/478] Bluetooth: Fix returning proper cmd_complete for mgmt_disconnect The Disconnect Management command should return Command Complete instead of Command Status whenever possible so that user space can distinguish exactly which command failed in the case of multiple commands. This patch does the necessary changes in the disconnect command handler to return the right event to user space. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e5e865d8afa8..7b8bc7c658b2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1654,6 +1654,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_disconnect *cp = data; + struct mgmt_rp_disconnect rp; struct hci_cp_disconnect dc; struct pending_cmd *cmd; struct hci_conn *conn; @@ -1661,21 +1662,26 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG(""); + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + if (!bdaddr_type_is_valid(cp->addr.type)) - return cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_INVALID_PARAMS); + return cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); hci_dev_lock(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_POWERED); + err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); goto failed; } if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_BUSY); + err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_BUSY, &rp, sizeof(rp)); goto failed; } @@ -1686,8 +1692,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_CONNECTED); + err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp)); goto failed; } From 5d0846d416a6c8b7fda1b24aa7369818a7dfa00e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:22 +0200 Subject: [PATCH 417/478] Bluetooth: Fix returning proper cmd_complete for mgmt_block/unblock The Block/Unblock Device Management commands should return Command Complete instead of Command Status whenever possible so that user space can distinguish exactly which command failed in the case of multiple commands. This patch does the necessary changes in the command handler to return the right event to user space. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7b8bc7c658b2..e7f944f52ff2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2598,8 +2598,9 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); if (!bdaddr_type_is_valid(cp->addr.type)) - return cmd_status(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, - MGMT_STATUS_INVALID_PARAMS); + return cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); hci_dev_lock(hdev); @@ -2627,8 +2628,9 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); if (!bdaddr_type_is_valid(cp->addr.type)) - return cmd_status(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, - MGMT_STATUS_INVALID_PARAMS); + return cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); hci_dev_lock(hdev); From 60e77321985ab599fac010afdc465c3e30281a06 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2013 14:01:59 +0200 Subject: [PATCH 418/478] Bluetooth: Add LE Local Features reading support To be able to make the appropriate decisions for some LE procedures we need to know the LE features that the local controller supports. Therefore, it's important to have the LE Read Local Supported Features HCI comand as part of the HCI init sequence. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 6 ++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 45eee08157bb..521eefa033e7 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -943,6 +943,12 @@ struct hci_rp_le_read_buffer_size { __u8 le_max_pkt; } __packed; +#define HCI_OP_LE_READ_LOCAL_FEATURES 0x2003 +struct hci_rp_le_read_local_features { + __u8 status; + __u8 features[8]; +} __packed; + #define HCI_OP_LE_READ_ADV_TX_POWER 0x2007 struct hci_rp_le_read_adv_tx_power { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 769a740c104c..3f607c94e213 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -152,6 +152,7 @@ struct hci_dev { __u8 minor_class; __u8 features[8]; __u8 host_features[8]; + __u8 le_features[8]; __u8 commands[64]; __u8 hci_ver; __u16 hci_rev; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 705078a0cc39..07c8c79a9fd1 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -609,6 +609,9 @@ static void le_setup(struct hci_dev *hdev) /* Read LE Buffer Size */ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); + /* Read LE Local Supported Features */ + hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL); + /* Read LE Advertising Channel TX Power */ hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); } @@ -1090,6 +1093,19 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } +static void hci_cc_le_read_local_features(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_local_features *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (!rp->status) + memcpy(hdev->le_features, rp->features, 8); + + hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status); +} + static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2628,6 +2644,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_read_buffer_size(hdev, skb); break; + case HCI_OP_LE_READ_LOCAL_FEATURES: + hci_cc_le_read_local_features(hdev, skb); + break; + case HCI_OP_LE_READ_ADV_TX_POWER: hci_cc_le_read_adv_tx_power(hdev, skb); break; From cf1d081f6597a45e5ff63f55c893494a8ae1cdaf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2013 14:02:00 +0200 Subject: [PATCH 419/478] Bluetooth: Add support for reading LE White List Size The LE White List Size is necessary to be known before attempting to feed the controller with any addresses intended for the white list. This patch adds the necessary HCI command sending to the HCI init sequence. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 6 ++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 521eefa033e7..f1766a6f4954 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1001,6 +1001,12 @@ struct hci_cp_le_create_conn { #define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e +#define HCI_OP_LE_READ_WHITE_LIST_SIZE 0x200f +struct hci_rp_le_read_white_list_size { + __u8 status; + __u8 size; +} __packed; + #define HCI_OP_LE_CONN_UPDATE 0x2013 struct hci_cp_le_conn_update { __le16 handle; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3f607c94e213..d6ed4ac18d83 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -153,6 +153,7 @@ struct hci_dev { __u8 features[8]; __u8 host_features[8]; __u8 le_features[8]; + __u8 le_white_list_size; __u8 commands[64]; __u8 hci_ver; __u16 hci_rev; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 07c8c79a9fd1..d2fee64b728c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -614,6 +614,9 @@ static void le_setup(struct hci_dev *hdev) /* Read LE Advertising Channel TX Power */ hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); + + /* Read LE White List Size */ + hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); } static void hci_setup(struct hci_dev *hdev) @@ -1306,6 +1309,19 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, } } +static void hci_cc_le_read_white_list_size(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_white_list_size *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x size %u", hdev->name, rp->status, rp->size); + + if (!rp->status) + hdev->le_white_list_size = rp->size; + + hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status); +} + static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_le_ltk_reply *rp = (void *) skb->data; @@ -2684,6 +2700,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_set_scan_enable(hdev, skb); break; + case HCI_OP_LE_READ_WHITE_LIST_SIZE: + hci_cc_le_read_white_list_size(hdev, skb); + break; + case HCI_OP_LE_LTK_REPLY: hci_cc_le_ltk_reply(hdev, skb); break; From 9b008c0457e583e10e62d1215bed6ab26ee54906 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2013 14:02:01 +0200 Subject: [PATCH 420/478] Bluetooth: Add support for reading LE supported states The LE supported states indicate the states and state combinations that the link layer supports. This is important information for knowing what operations are possible when dealing with multiple connected devices. This patch adds reading of the supported states to the HCI init sequence. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 6 ++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index f1766a6f4954..7f12c25f1fca 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1045,6 +1045,12 @@ struct hci_rp_le_ltk_neg_reply { __le16 handle; } __packed; +#define HCI_OP_LE_READ_SUPPORTED_STATES 0x201c +struct hci_rp_le_read_supported_states { + __u8 status; + __u8 le_states[8]; +} __packed; + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d6ed4ac18d83..bcf8ffe2a843 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -154,6 +154,7 @@ struct hci_dev { __u8 host_features[8]; __u8 le_features[8]; __u8 le_white_list_size; + __u8 le_states[8]; __u8 commands[64]; __u8 hci_ver; __u16 hci_rev; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d2fee64b728c..d4fcba6ec23e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -617,6 +617,9 @@ static void le_setup(struct hci_dev *hdev) /* Read LE White List Size */ hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); + + /* Read LE Supported States */ + hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); } static void hci_setup(struct hci_dev *hdev) @@ -1346,6 +1349,19 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); } +static void hci_cc_le_read_supported_states(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_supported_states *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (!rp->status) + memcpy(hdev->le_states, rp->le_states, 8); + + hci_req_complete(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, rp->status); +} + static void hci_cc_write_le_host_supported(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2712,6 +2728,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_ltk_neg_reply(hdev, skb); break; + case HCI_OP_LE_READ_SUPPORTED_STATES: + hci_cc_le_read_supported_states(hdev, skb); + break; + case HCI_OP_WRITE_LE_HOST_SUPPORTED: hci_cc_write_le_host_supported(hdev, skb); break; From 887da9176e011a044b12ec0deff62df5faadd67c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 20 Jan 2013 17:32:41 +0200 Subject: [PATCH 421/478] mac80211: provide the vif in rssi_callback Since drivers can support several BSS / P2P Client interfaces, the rssi callback needs to inform the driver about the interface teh rssi event relates to. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 1 + include/net/mac80211.h | 1 + net/mac80211/driver-ops.h | 5 +++-- net/mac80211/mlme.c | 4 ++-- net/mac80211/trace.h | 9 ++++++--- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 02fdcea76b21..f16b81d625ad 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1156,6 +1156,7 @@ static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) } static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ece5733d113d..5c98d654fc75 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2633,6 +2633,7 @@ struct ieee80211_ops { int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask); void (*rssi_callback)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event); void (*allow_buffered_frames)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index d51afbd614d3..e75f5b9eb24f 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -845,11 +845,12 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local, } static inline void drv_rssi_callback(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, const enum ieee80211_rssi_event event) { - trace_drv_rssi_callback(local, event); + trace_drv_rssi_callback(local, sdata, event); if (local->ops->rssi_callback) - local->ops->rssi_callback(&local->hw, event); + local->ops->rssi_callback(&local->hw, &sdata->vif, event); trace_drv_return_void(local); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2d9ef20cd38d..344ef7d47c1a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2604,12 +2604,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (sig > ifmgd->rssi_max_thold && (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; - drv_rssi_callback(local, RSSI_EVENT_HIGH); + drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH); } else if (sig < ifmgd->rssi_min_thold && (last_sig >= ifmgd->rssi_max_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; - drv_rssi_callback(local, RSSI_EVENT_LOW); + drv_rssi_callback(local, sdata, RSSI_EVENT_LOW); } } diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 2a2c2e20307d..6ca53d64cb28 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1189,23 +1189,26 @@ TRACE_EVENT(drv_set_rekey_data, TRACE_EVENT(drv_rssi_callback, TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, enum ieee80211_rssi_event rssi_event), - TP_ARGS(local, rssi_event), + TP_ARGS(local, sdata, rssi_event), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY __field(u32, rssi_event) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; __entry->rssi_event = rssi_event; ), TP_printk( - LOCAL_PR_FMT " rssi_event:%d", - LOCAL_PR_ARG, __entry->rssi_event + LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event ) ); From 8147dc7f54f0f2e13c3b65fd9628994ad576610b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 Jan 2013 23:47:05 +0100 Subject: [PATCH 422/478] mac80211: fix aggregation state with current drivers For drivers that don't actually flush their queues when aggregation stop with the IEEE80211_AMPDU_TX_STOP_FLUSH or IEEE80211_AMPDU_TX_STOP_FLUSH_CONT reasons is done, like iwlwifi or iwlegacy, mac80211 can then transmit on a TID that the driver still considers busy. This happens in the following way: - IEEE80211_AMPDU_TX_STOP_FLUSH requested - driver marks TID as emptying - mac80211 removes tid_tx data, this can copy packets to the TX pending queues and also let new packets through to the driver - driver gets unexpected TX as it wasn't completely converted to the new API In iwlwifi, this lead to the following warning: WARNING: at drivers/net/wireless/iwlwifi/dvm/tx.c:442 iwlagn_tx_skb+0xc47/0xce0 Tx while agg.state = 4 Modules linked in: [...] Pid: 0, comm: kworker/0:0 Tainted: G W 3.1.0 #1 Call Trace: [] warn_slowpath_common+0x72/0xa0 [] warn_slowpath_fmt+0x33/0x40 [] iwlagn_tx_skb+0xc47/0xce0 [iwldvm] [] iwlagn_mac_tx+0x23/0x40 [iwldvm] [] __ieee80211_tx+0xf6/0x3c0 [mac80211] [] ieee80211_tx+0xd0/0x100 [mac80211] [] ieee80211_xmit+0x96/0xe0 [mac80211] [] ieee80211_subif_start_xmit+0x348/0xc80 [mac80211] [] dev_hard_start_xmit+0x337/0x6d0 [] sch_direct_xmit+0xa9/0x210 [] dev_queue_xmit+0x1b0/0x8e0 Fortunately, solving this problem is easy as the station is being destroyed, so such transmit packets can only happen due to races. Instead of trying to close the race just let the race not reach the drivers by making two changes: 1) remove the explicit aggregation session teardown in the managed mode code, the same thing will be done when the station is removed, in __sta_info_destroy. 2) When aggregation stop with AGG_STOP_DESTROY_STA is requested, leave the tid_tx data around as stopped. It will be cleared and freed in cleanup_single_sta later, but until then any racy packets will be put onto the tid_tx pending queue instead of transmitted which is fine since the station is being removed. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 17 ++++++++++------- net/mac80211/mlme.c | 9 --------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index e90911819ed4..13b7683de5a4 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -296,7 +296,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, &sta->sta, tid, NULL, 0); WARN_ON_ONCE(ret); - goto remove_tid_tx; + return 0; } if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { @@ -354,12 +354,15 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, */ } - if (reason == AGG_STOP_DESTROY_STA) { - remove_tid_tx: - spin_lock_bh(&sta->lock); - ieee80211_remove_tid_tx(sta, tid); - spin_unlock_bh(&sta->lock); - } + /* + * In the case of AGG_STOP_DESTROY_STA, the driver won't + * necessarily call ieee80211_stop_tx_ba_cb(), so this may + * seem like we can leave the tid_tx data pending forever. + * This is true, in a way, but "forever" is only until the + * station struct is actually destroyed. In the meantime, + * leaving it around ensures that we don't transmit packets + * to the driver on this TID which might confuse it. + */ return 0; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 344ef7d47c1a..62e8f890fed6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1487,7 +1487,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - struct sta_info *sta; u32 changed = 0; ASSERT_MGD_MTX(ifmgd); @@ -1519,14 +1518,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, netif_tx_stop_all_queues(sdata->dev); netif_carrier_off(sdata->dev); - mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, ifmgd->bssid); - if (sta) { - set_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); - } - mutex_unlock(&local->sta_mtx); - /* * if we want to get out of ps before disassoc (why?) we have * to do it before sending disassoc, as otherwise the null-packet From d437c86baacf265a640dfc462c75941d02c0e153 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Wed, 23 Jan 2013 20:33:58 -0800 Subject: [PATCH 423/478] ieee80211: define AKM suite selectors type 5, 6 and 7 Reference: IEEE 802.11-2012 8.4.2.27.3 "AKM suites" Signed-off-by: Bing Zhao Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index ccf9ee1dca8c..11c8bc87fdcb 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1898,7 +1898,10 @@ enum ieee80211_sa_query_action { /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X 0x000FAC01 #define WLAN_AKM_SUITE_PSK 0x000FAC02 -#define WLAN_AKM_SUITE_SAE 0x000FAC08 +#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05 +#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06 +#define WLAN_AKM_SUITE_TDLS 0x000FAC07 +#define WLAN_AKM_SUITE_SAE 0x000FAC08 #define WLAN_AKM_SUITE_FT_OVER_SAE 0x000FAC09 #define WLAN_MAX_KEY_LEN 32 From 296fcba3ba1b8888aa8f5211de1e25a78b47aeee Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 23 Jan 2013 12:18:12 -0800 Subject: [PATCH 424/478] mac80211: clean up mesh sta allocation warning This refactoring fixes a "scheduling while atomic" warning when allocating a mesh station entry while holding the RCU read lock. Fix this by creating a new function mesh_sta_info_get(), which correctly handles the locking and returns under RCU. Also move some unnecessarily #ifdefed mesh station init code from sta_info_alloc() to __mesh_sta_info_alloc(). Signed-off-by: Thomas Pedersen [change code flow to make sparse happy] Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 158 +++++++++++++++++++++++--------------- net/mac80211/sta_info.c | 5 -- 2 files changed, 95 insertions(+), 68 deletions(-) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 9e0416696a83..ef92d2705851 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -55,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) sta->plink_retries = 0; } -/* - * Allocate mesh sta entry and insert into station table - */ -static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, - u8 *hw_addr) -{ - struct sta_info *sta; - - if (sdata->local->num_sta >= MESH_MAX_PLINKS) - return NULL; - - sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); - if (!sta) - return NULL; - - sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); - sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); - sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); - - set_sta_flag(sta, WLAN_STA_WME); - - return sta; -} - /** * mesh_set_ht_prot_mode - set correct HT protection mode * @@ -309,52 +285,24 @@ free: return err; } -/** - * mesh_peer_init - initialize new mesh peer and return resulting sta_info - * - * @sdata: local meshif - * @addr: peer's address - * @elems: IEs from beacon or mesh peering frame - * - * call under RCU - */ -static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, - u8 *addr, - struct ieee802_11_elems *elems) +static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct ieee802_11_elems *elems, bool insert) { struct ieee80211_local *local = sdata->local; enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; u32 rates, basic_rates = 0; - struct sta_info *sta; - bool insert = false; sband = local->hw.wiphy->bands[band]; rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); - sta = sta_info_get(sdata, addr); - if (!sta) { - /* Userspace handles peer allocation when security is enabled */ - if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { - cfg80211_notify_new_peer_candidate(sdata->dev, addr, - elems->ie_start, - elems->total_len, - GFP_ATOMIC); - return NULL; - } - - sta = mesh_plink_alloc(sdata, addr); - if (!sta) - return NULL; - insert = true; - } - spin_lock_bh(&sta->lock); sta->last_rx = jiffies; - if (sta->plink_state == NL80211_PLINK_ESTAB) { - spin_unlock_bh(&sta->lock); - return sta; - } + + /* rates and capabilities don't change during peering */ + if (sta->plink_state == NL80211_PLINK_ESTAB) + goto out; sta->sta.supp_rates[band] = rates; if (elems->ht_cap_elem && @@ -379,22 +327,104 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, if (insert) rate_control_rate_init(sta); +out: spin_unlock_bh(&sta->lock); +} - if (insert && sta_info_insert(sta)) +static struct sta_info * +__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) +{ + struct sta_info *sta; + + if (sdata->local->num_sta >= MESH_MAX_PLINKS) return NULL; + sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); + if (!sta) + return NULL; + + sta->plink_state = NL80211_PLINK_LISTEN; + init_timer(&sta->plink_timer); + + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); + + set_sta_flag(sta, WLAN_STA_WME); + return sta; } +static struct sta_info * +mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, + struct ieee802_11_elems *elems) +{ + struct sta_info *sta = NULL; + + /* Userspace handles peer allocation when security is enabled */ + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) + cfg80211_notify_new_peer_candidate(sdata->dev, addr, + elems->ie_start, + elems->total_len, + GFP_KERNEL); + else + sta = __mesh_sta_info_alloc(sdata, addr); + + return sta; +} + +/* + * mesh_sta_info_get - return mesh sta info entry for @addr. + * + * @sdata: local meshif + * @addr: peer's address + * @elems: IEs from beacon or mesh peering frame. + * + * Return existing or newly allocated sta_info under RCU read lock. + * (re)initialize with given IEs. + */ +static struct sta_info * +mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, + u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU) +{ + struct sta_info *sta = NULL; + + rcu_read_lock(); + sta = sta_info_get(sdata, addr); + if (sta) { + mesh_sta_info_init(sdata, sta, elems, false); + } else { + rcu_read_unlock(); + /* can't run atomic */ + sta = mesh_sta_info_alloc(sdata, addr, elems); + if (!sta) { + rcu_read_lock(); + return NULL; + } + + if (sta_info_insert_rcu(sta)) + return NULL; + } + + return sta; +} + +/* + * mesh_neighbour_update - update or initialize new mesh neighbor. + * + * @sdata: local meshif + * @addr: peer's address + * @elems: IEs from beacon or mesh peering frame + * + * Initiates peering if appropriate. + */ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, u8 *hw_addr, struct ieee802_11_elems *elems) { struct sta_info *sta; - rcu_read_lock(); - sta = mesh_peer_init(sdata, hw_addr, elems); + sta = mesh_sta_info_get(sdata, hw_addr, elems); if (!sta) goto out; @@ -632,6 +662,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); + /* WARNING: Only for sta pointer, is dropped & re-acquired */ rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); @@ -735,8 +766,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } if (event == OPN_ACPT) { + rcu_read_unlock(); /* allocate sta entry if necessary and update info */ - sta = mesh_peer_init(sdata, mgmt->sa, &elems); + sta = mesh_sta_info_get(sdata, mgmt->sa, &elems); if (!sta) { mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); rcu_read_unlock(); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 9d864ed5f3da..227233c3ff7f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -380,11 +380,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); -#ifdef CONFIG_MAC80211_MESH - sta->plink_state = NL80211_PLINK_LISTEN; - init_timer(&sta->plink_timer); -#endif - return sta; } From f68d776a04bfcebc426f605c4440ffe38c583a4b Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 23 Jan 2013 12:18:13 -0800 Subject: [PATCH 425/478] mac80211: support mesh rate updates An existing mesh station entry may change its rate capabilities, so call rate_control_rate_update() to notify the rate control. Signed-off-by: Thomas Pedersen [fix compilation] Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.h | 3 ++- net/mac80211/mesh_plink.c | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index e75f5b9eb24f..434b3c4f31b5 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -569,7 +569,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local, check_sdata_in_driver(sdata); WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && - sdata->vif.type != NL80211_IFTYPE_ADHOC); + (sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); trace_drv_sta_rc_update(local, sdata, sta, changed); if (local->ops->sta_rc_update) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index ef92d2705851..4e1d406fd9b1 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -292,7 +292,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; - u32 rates, basic_rates = 0; + u32 rates, basic_rates = 0, changed = 0; sband = local->hw.wiphy->bands[band]; rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); @@ -304,6 +304,8 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, if (sta->plink_state == NL80211_PLINK_ESTAB) goto out; + if (sta->sta.supp_rates[band] != rates) + changed |= IEEE80211_RC_SUPP_RATES_CHANGED; sta->sta.supp_rates[band] = rates; if (elems->ht_cap_elem && sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) @@ -322,11 +324,15 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, elems->ht_operation, &chandef); + if (sta->ch_width != chandef.width) + changed |= IEEE80211_RC_BW_CHANGED; sta->ch_width = chandef.width; } if (insert) rate_control_rate_init(sta); + else + rate_control_rate_update(local, sband, sta, changed); out: spin_unlock_bh(&sta->lock); } From 683d41ae6755e6ae297ec09603c229795ab9566e Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Wed, 23 Jan 2013 15:15:57 +0100 Subject: [PATCH 426/478] cfg80211: fix channel check in cfg80211_can_use_iftype_chan In commit "cfg80211: check radar interface combinations" a regression was introduced which might lead to NULL dereference if the argument chan = NULL, which might happen in IBSS/wext case (and probably others). Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- net/wireless/util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 1c2795d52db0..d7873c7ae0ec 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1212,7 +1212,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_WDS: - radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR); + radar_required = !!(chan && + (chan->flags & IEEE80211_CHAN_RADAR)); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: From 782d2673655c05a1c0335fe0ee4cb62c23bcc231 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 Jan 2013 21:21:31 +0100 Subject: [PATCH 427/478] mac80211: remove redundant check There's no need to have two checks for "associated" in ieee80211_sta_restart(), make the first one locked to not race (unlikely at this point during resume) and remove the second check. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 62e8f890fed6..5913fb924b12 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3156,23 +3156,22 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - if (!ifmgd->associated) + mutex_lock(&ifmgd->mtx); + if (!ifmgd->associated) { + mutex_unlock(&ifmgd->mtx); return; + } if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; - mutex_lock(&ifmgd->mtx); - if (ifmgd->associated) { - mlme_dbg(sdata, - "driver requested disconnect after resume\n"); - ieee80211_sta_connection_lost(sdata, - ifmgd->associated->bssid, - WLAN_REASON_UNSPECIFIED); - mutex_unlock(&ifmgd->mtx); - return; - } + mlme_dbg(sdata, "driver requested disconnect after resume\n"); + ieee80211_sta_connection_lost(sdata, + ifmgd->associated->bssid, + WLAN_REASON_UNSPECIFIED); mutex_unlock(&ifmgd->mtx); + return; } + mutex_unlock(&ifmgd->mtx); if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running)) add_timer(&ifmgd->timer); From fe80123d83d1f9abca426914f768048e601f54db Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 Jan 2013 14:14:33 +0000 Subject: [PATCH 428/478] mac80211: avoid a build warning gcc cannot prove that the value of sdata->vif.type does not change between the switch() statement and the second comparison to NL80211_IFTYPE_AP, causing a harmless warning. Slightly reordering the code makes the warning go away with no functional change. Without this patch, building ARM at91sam9g45_defconfig with gcc-4.6 results in: net/mac80211/tx.c: In function 'ieee80211_subif_start_xmit': net/mac80211/tx.c:1797:22: warning: 'chanctx_conf' may be used uninitialized in this function [-Wuninitialized] Signed-off-by: Arnd Bergmann Cc: Johannes Berg Cc: "John W. Linville" Cc: "David S. Miller" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0d7381679a5c..b4a84146de0c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1784,16 +1784,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, break; /* fall through */ case NL80211_IFTYPE_AP: + if (sdata->vif.type == NL80211_IFTYPE_AP) + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto fail_rcu; fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA BSSID SA */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 24; - if (sdata->vif.type == NL80211_IFTYPE_AP) - chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) - goto fail_rcu; band = chanctx_conf->def.chan->band; break; case NL80211_IFTYPE_WDS: From 6d45a74b1f2e42e41c9931bfb35cdb789d0bb3ea Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 18 Jan 2013 11:18:44 +0530 Subject: [PATCH 429/478] cfg80211: Move the definition of struct mac_address up struct mac_address will be used by ACL related configuration ops. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 970da4420676..183033789e69 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -527,6 +527,10 @@ struct cfg80211_beacon_data { size_t probe_resp_len; }; +struct mac_address { + u8 addr[ETH_ALEN]; +}; + /** * struct cfg80211_ap_settings - AP configuration * @@ -2181,10 +2185,6 @@ struct ieee80211_iface_combination { u8 radar_detect_widths; }; -struct mac_address { - u8 addr[ETH_ALEN]; -}; - struct ieee80211_txrx_stypes { u16 tx, rx; }; From 77765eaf5cfb6b8dd98ec8b54b411d74ff6095f1 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 18 Jan 2013 11:18:45 +0530 Subject: [PATCH 430/478] cfg80211/nl80211: add API for MAC address ACLs Add API to enable drivers to implement MAC address based access control in AP/P2P GO mode. Capable drivers advertise this capability by setting the maximum number of MAC addresses in such a list in wiphy->max_acl_mac_addrs. An initial ACL may be given to the NL80211_CMD_START_AP command and/or changed later with NL80211_CMD_SET_MAC_ACL. Black- and whitelists are supported, but not simultaneously. Signed-off-by: Vasanthakumar Thiagarajan [rewrite commit log, many cleanups] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 34 ++++++++++ include/uapi/linux/nl80211.h | 51 ++++++++++++++- net/wireless/core.c | 5 ++ net/wireless/nl80211.c | 116 +++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 12 ++++ net/wireless/trace.h | 18 ++++++ 6 files changed, 234 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 183033789e69..36e076e374d2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -531,6 +531,22 @@ struct mac_address { u8 addr[ETH_ALEN]; }; +/** + * struct cfg80211_acl_data - Access control list data + * + * @acl_policy: ACL policy to be applied on the station's + entry specified by mac_addr + * @n_acl_entries: Number of MAC address entries passed + * @mac_addrs: List of MAC addresses of stations to be used for ACL + */ +struct cfg80211_acl_data { + enum nl80211_acl_policy acl_policy; + int n_acl_entries; + + /* Keep it last */ + struct mac_address mac_addrs[]; +}; + /** * struct cfg80211_ap_settings - AP configuration * @@ -550,6 +566,8 @@ struct mac_address { * @inactivity_timeout: time in seconds to determine station's inactivity. * @p2p_ctwindow: P2P CT Window * @p2p_opp_ps: P2P opportunistic PS + * @acl: ACL configuration used by the drivers which has support for + * MAC address based access control */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -566,6 +584,7 @@ struct cfg80211_ap_settings { int inactivity_timeout; u8 p2p_ctwindow; bool p2p_opp_ps; + const struct cfg80211_acl_data *acl; }; /** @@ -1800,6 +1819,13 @@ struct cfg80211_gtk_rekey_data { * * @start_p2p_device: Start the given P2P device. * @stop_p2p_device: Stop the given P2P device. + * + * @set_mac_acl: Sets MAC address control list in AP and P2P GO mode. + * Parameters include ACL policy, an array of MAC address of stations + * and the number of MAC addresses. If there is already a list in driver + * this new list replaces the existing one. Driver has to clear its ACL + * when number of MAC addresses entries is passed as 0. Drivers which + * advertise the support for MAC based ACL have to implement this callback. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -2020,6 +2046,9 @@ struct cfg80211_ops { struct wireless_dev *wdev); void (*stop_p2p_device)(struct wiphy *wiphy, struct wireless_dev *wdev); + + int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_acl_data *params); }; /* @@ -2325,6 +2354,9 @@ struct wiphy_wowlan_support { * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. * @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden. * If null, then none can be over-ridden. + * + * @max_acl_mac_addrs: Maximum number of MAC addresses that the device + * supports for ACL. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -2346,6 +2378,8 @@ struct wiphy { /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ u16 interface_modes; + u16 max_acl_mac_addrs; + u32 flags, features; u32 ap_sme_capa; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e6eeb4ba5dc5..5b7dbc1ea966 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -170,7 +170,8 @@ * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, - * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. + * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT, + * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS. * The channel to use can be set on the interface or be given using the * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP @@ -586,6 +587,16 @@ * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames * for IBSS or MESH vif. * + * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control. + * This is to be used with the drivers advertising the support of MAC + * address based access control. List of MAC addresses is passed in + * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in + * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it + * is not already done. The new list will replace any existing list. Driver + * will clear its ACL when the list of MAC addresses passed is empty. This + * command is used in AP/P2P GO mode. Driver has to make sure to clear its + * ACL list during %NL80211_CMD_STOP_AP. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -736,6 +747,8 @@ enum nl80211_commands { NL80211_CMD_SET_MCAST_RATE, + NL80211_CMD_SET_MAC_ACL, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1313,6 +1326,16 @@ enum nl80211_commands { * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode * defined in &enum nl80211_mesh_power_mode. * + * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy, + * carried in a u32 attribute + * + * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for + * MAC ACL. + * + * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum + * number of MAC addresses that a device can support for MAC + * ACL. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1585,6 +1608,12 @@ enum nl80211_attrs { NL80211_ATTR_LOCAL_MESH_POWER_MODE, + NL80211_ATTR_ACL_POLICY, + + NL80211_ATTR_MAC_ADDRS, + + NL80211_ATTR_MAC_ACL_MAX, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3248,7 +3277,7 @@ enum nl80211_probe_resp_offload_support_attr { * enum nl80211_connect_failed_reason - connection request failed reasons * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be * handled by the AP is reached. - * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist. + * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL. */ enum nl80211_connect_failed_reason { NL80211_CONN_FAIL_MAX_CLIENTS, @@ -3276,4 +3305,22 @@ enum nl80211_scan_flags { NL80211_SCAN_FLAG_AP = 1<<2, }; +/** + * enum nl80211_acl_policy - access control policy + * + * Access control policy is applied on a MAC list set by + * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to + * be used with %NL80211_ATTR_ACL_POLICY. + * + * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are + * listed in ACL, i.e. allow all the stations which are not listed + * in ACL to authenticate. + * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed + * in ACL, i.e. deny all the stations which are not listed in ACL. + */ +enum nl80211_acl_policy { + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED, + NL80211_ACL_POLICY_DENY_UNLESS_LISTED, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 0e702cdc6043..ce827242f390 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -478,6 +478,11 @@ int wiphy_register(struct wiphy *wiphy) ETH_ALEN))) return -EINVAL; + if (WARN_ON(wiphy->max_acl_mac_addrs && + (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) || + !rdev->ops->set_mac_acl))) + return -EINVAL; + if (wiphy->addresses) memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 33de80364c5c..b5978ab4ad7a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -365,6 +365,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, + [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, + [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, }; /* policy for the key attributes */ @@ -1268,6 +1270,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag dev->wiphy.ht_capa_mod_mask)) goto nla_put_failure; + if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && + dev->wiphy.max_acl_mac_addrs && + nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX, + dev->wiphy.max_acl_mac_addrs)) + goto nla_put_failure; + return genlmsg_end(msg, hdr); nla_put_failure: @@ -2491,6 +2499,97 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) return err; } +/* This function returns an error or the number of nested attributes */ +static int validate_acl_mac_addrs(struct nlattr *nl_attr) +{ + struct nlattr *attr; + int n_entries = 0, tmp; + + nla_for_each_nested(attr, nl_attr, tmp) { + if (nla_len(attr) != ETH_ALEN) + return -EINVAL; + + n_entries++; + } + + return n_entries; +} + +/* + * This function parses ACL information and allocates memory for ACL data. + * On successful return, the calling function is responsible to free the + * ACL buffer returned by this function. + */ +static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy, + struct genl_info *info) +{ + enum nl80211_acl_policy acl_policy; + struct nlattr *attr; + struct cfg80211_acl_data *acl; + int i = 0, n_entries, tmp; + + if (!wiphy->max_acl_mac_addrs) + return ERR_PTR(-EOPNOTSUPP); + + if (!info->attrs[NL80211_ATTR_ACL_POLICY]) + return ERR_PTR(-EINVAL); + + acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]); + if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED && + acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED) + return ERR_PTR(-EINVAL); + + if (!info->attrs[NL80211_ATTR_MAC_ADDRS]) + return ERR_PTR(-EINVAL); + + n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]); + if (n_entries < 0) + return ERR_PTR(n_entries); + + if (n_entries > wiphy->max_acl_mac_addrs) + return ERR_PTR(-ENOTSUPP); + + acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries), + GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) { + memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN); + i++; + } + + acl->n_acl_entries = n_entries; + acl->acl_policy = acl_policy; + + return acl; +} + +static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_acl_data *acl; + int err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!dev->ieee80211_ptr->beacon_interval) + return -EINVAL; + + acl = parse_acl_data(&rdev->wiphy, info); + if (IS_ERR(acl)) + return PTR_ERR(acl); + + err = rdev_set_mac_acl(rdev, dev, acl); + + kfree(acl); + + return err; +} + static int nl80211_parse_beacon(struct genl_info *info, struct cfg80211_beacon_data *bcn) { @@ -2734,6 +2833,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (err) return err; + if (info->attrs[NL80211_ATTR_ACL_POLICY]) { + params.acl = parse_acl_data(&rdev->wiphy, info); + if (IS_ERR(params.acl)) + return PTR_ERR(params.acl); + } + err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { wdev->preset_chandef = params.chandef; @@ -2742,6 +2847,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->ssid_len = params.ssid_len; memcpy(wdev->ssid, params.ssid, wdev->ssid_len); } + + kfree(params.acl); + return err; } @@ -7876,6 +7984,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_SET_MAC_ACL, + .doit = nl80211_set_mac_acl, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 6c0c8191f837..422d38291d66 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -875,4 +875,16 @@ static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev, rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); trace_rdev_return_void(&rdev->wiphy); } + +static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_acl_data *params) +{ + int ret; + + trace_rdev_set_mac_acl(&rdev->wiphy, dev, params); + ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 2134576f426e..8bc553199686 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1767,6 +1767,24 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device, TP_ARGS(wiphy, wdev) ); +TRACE_EVENT(rdev_set_mac_acl, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_acl_data *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u32, acl_policy) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WIPHY_ASSIGN; + __entry->acl_policy = params->acl_policy; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", acl policy: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ From 3b4797bce0050570e84bedd10e1b14e9320a3551 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Sat, 26 Jan 2013 17:00:02 -0800 Subject: [PATCH 431/478] mac80211: fix mesh_sta_info_get() reshuffle damage Before "mac80211: clean up mesh sta allocation warning" was applied, mesh_sta_info_get() was reshuffled to please sparse. As a result we neglect to initialize newly created STAs. Fix this. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 4e1d406fd9b1..81e612682bc3 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -408,6 +408,8 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, return NULL; } + mesh_sta_info_init(sdata, sta, elems, true); + if (sta_info_insert_rcu(sta)) return NULL; } From 29d7b331c73c4f1a8b3f7f4a94abe4eb834be351 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 21 Jan 2013 14:02:50 +0100 Subject: [PATCH 432/478] drivers/net/wireless/iwlegacy/4965-mac.c: adjust duplicate test Delete successive tests to the same location. This looks like simple code duplication. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @s exists@ local idexpression y; expression x,e; @@ *if ( \(x == NULL\|IS_ERR(x)\|y != 0\) ) { ... when forall return ...; } ... when != \(y = e\|y += e\|y -= e\|y |= e\|y &= e\|y++\|y--\|&y\) when != \(XT_GETPAGE(...,y)\|WMI_CMD_BUF(...)\) *if ( \(x == NULL\|IS_ERR(x)\|y != 0\) ) { ... when forall return ...; } // Signed-off-by: Julia Lawall Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/4965-mac.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index f1dc04006564..d665a3d9f08f 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -6573,9 +6573,6 @@ il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto out_free_eeprom; - if (err) - goto out_free_eeprom; - /* extract MAC Address */ il4965_eeprom_get_mac(il, il->addresses[0].addr); D_INFO("MAC address: %pM\n", il->addresses[0].addr); From def64521572d8330bfa6d855e278507100fdf180 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 22 Jan 2013 17:49:14 +0100 Subject: [PATCH 433/478] rt2x00: print chip and firmware version by default Signed-off-by: Stanislaw Gruszka Tested-by: Xose Vazquez Perez Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index b52512b8ac5f..221728090cbd 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -92,7 +92,7 @@ #define NOTICE(__dev, __msg, __args...) \ DEBUG_PRINTK(__dev, KERN_NOTICE, "Notice", __msg, ##__args) #define INFO(__dev, __msg, __args...) \ - DEBUG_PRINTK(__dev, KERN_INFO, "Info", __msg, ##__args) + DEBUG_PRINTK_MSG(__dev, KERN_INFO, "Info", __msg, ##__args) #define DEBUG(__dev, __msg, __args...) \ DEBUG_PRINTK(__dev, KERN_DEBUG, "Debug", __msg, ##__args) #define EEPROM(__dev, __msg, __args...) \ diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index ed7a1bb3f245..20c6eccce5aa 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -731,9 +731,9 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw, queue->aifs = params->aifs; queue->txop = params->txop; - INFO(rt2x00dev, - "Configured TX queue %d - CWmin: %d, CWmax: %d, Aifs: %d, TXop: %d.\n", - queue_idx, queue->cw_min, queue->cw_max, queue->aifs, queue->txop); + DEBUG(rt2x00dev, + "Configured TX queue %d - CWmin: %d, CWmax: %d, Aifs: %d, TXop: %d.\n", + queue_idx, queue->cw_min, queue->cw_max, queue->aifs, queue->txop); return 0; } From 6ccea75e701d5d452b674dd5550c0caceccb5d56 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Tue, 22 Jan 2013 16:29:03 -0800 Subject: [PATCH 434/478] mwifiex: fix invalid access of PCIe RxBD ring buffer descriptor This patch fixes invalid access of RxBD ring buffer descriptor's length and flag inside PCIe send_data_complete() routine. We are supposed to modify TxBD buffer descriptor's length and flag here. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/pcie.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 237949c070cc..7415ac8c3ad7 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -846,8 +846,8 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) card->tx_buf_list[wrdoneidx] = NULL; card->txbd_ring[wrdoneidx]->paddr = 0; - card->rxbd_ring[wrdoneidx]->len = 0; - card->rxbd_ring[wrdoneidx]->flags = 0; + card->txbd_ring[wrdoneidx]->len = 0; + card->txbd_ring[wrdoneidx]->flags = 0; card->txbd_rdptr++; if ((card->txbd_rdptr & MWIFIEX_TXBD_MASK) == num_tx_buffs) From 04ccd4a1a61a2824539e04b5d7f4ee87e8aab0ed Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Wed, 23 Jan 2013 17:38:04 +0100 Subject: [PATCH 435/478] ath9k: add debug parameters for spectral scan Export the various parameters to userspace. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/debug.c | 186 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/init.c | 7 + drivers/net/wireless/ath/ath9k/main.c | 23 +-- 4 files changed, 200 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b2d6c18d1678..342edc543665 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -754,6 +754,7 @@ struct ath_softc { /* relay(fs) channel for spectral scan */ struct rchan *rfs_chan_spec_scan; enum spectral_mode spectral_mode; + struct ath_spec_scan spec_config; int scanning; #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 6c5d313ebcb7..012446d27bc0 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1035,6 +1035,182 @@ static const struct file_operations fops_spec_scan_ctl = { .llseek = default_llseek, }; +static ssize_t read_file_spectral_short_repeat(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.short_repeat); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_short_repeat(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 1) + return -EINVAL; + + sc->spec_config.short_repeat = val; + return count; +} + +static const struct file_operations fops_spectral_short_repeat = { + .read = read_file_spectral_short_repeat, + .write = write_file_spectral_short_repeat, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_spectral_count(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.count); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_count(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 255) + return -EINVAL; + + sc->spec_config.count = val; + return count; +} + +static const struct file_operations fops_spectral_count = { + .read = read_file_spectral_count, + .write = write_file_spectral_count, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_spectral_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.period); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 255) + return -EINVAL; + + sc->spec_config.period = val; + return count; +} + +static const struct file_operations fops_spectral_period = { + .read = read_file_spectral_period, + .write = write_file_spectral_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_spectral_fft_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.fft_period); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_fft_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 15) + return -EINVAL; + + sc->spec_config.fft_period = val; + return count; +} + +static const struct file_operations fops_spectral_fft_period = { + .read = read_file_spectral_fft_period, + .write = write_file_spectral_fft_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static struct dentry *create_buf_file_handler(const char *filename, struct dentry *parent, umode_t mode, @@ -1893,6 +2069,16 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_spec_scan_ctl); + debugfs_create_file("spectral_short_repeat", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_short_repeat); + debugfs_create_file("spectral_count", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_spectral_count); + debugfs_create_file("spectral_period", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_spectral_period); + debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_fft_period); #ifdef CONFIG_ATH9K_MAC_DEBUG debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc, diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 4b1abc7da98c..c7d116c2c615 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -497,6 +497,13 @@ static void ath9k_init_misc(struct ath_softc *sc) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT; + + sc->spec_config.enabled = 0; + sc->spec_config.short_repeat = true; + sc->spec_config.count = 8; + sc->spec_config.endless = false; + sc->spec_config.period = 0xFF; + sc->spec_config.fft_period = 0xF; } static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 4b72b660f180..5432f1247e2e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1099,45 +1099,34 @@ int ath9k_spectral_scan_config(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - struct ath_spec_scan param; if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { ath_err(common, "spectrum analyzer not implemented on this hardware\n"); return -1; } - /* NOTE: this will generate a few samples ... - * - * TODO: review default parameters, and/or define an interface to set - * them. - */ - param.enabled = 1; - param.short_repeat = true; - param.count = 8; - param.endless = false; - param.period = 0xFF; - param.fft_period = 0xF; - switch (spectral_mode) { case SPECTRAL_DISABLED: - param.enabled = 0; + sc->spec_config.enabled = 0; break; case SPECTRAL_BACKGROUND: /* send endless samples. * TODO: is this really useful for "background"? */ - param.endless = 1; + sc->spec_config.endless = 1; + sc->spec_config.enabled = 1; break; case SPECTRAL_CHANSCAN: - break; case SPECTRAL_MANUAL: + sc->spec_config.endless = 0; + sc->spec_config.enabled = 1; break; default: return -1; } ath9k_ps_wakeup(sc); - ath9k_hw_ops(ah)->spectral_scan_config(ah, ¶m); + ath9k_hw_ops(ah)->spectral_scan_config(ah, &sc->spec_config); ath9k_ps_restore(sc); sc->spectral_mode = spectral_mode; From 9b99e665f6176500e8ee61d149bbe69544354b40 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Wed, 23 Jan 2013 17:38:05 +0100 Subject: [PATCH 436/478] ath9k: drop spectral packets after processing them Spectral packets are "bogus" packets and should not be further evaluated by the RX path. Statistics are added to keep track of these packets. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 1 + drivers/net/wireless/ath/ath9k/debug.h | 2 ++ drivers/net/wireless/ath/ath9k/recv.c | 37 +++++++++++++++----------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 012446d27bc0..2bbdcf89d39a 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -895,6 +895,7 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, RXS_ERR("RX-Bytes-All", rx_bytes_all); RXS_ERR("RX-Beacons", rx_beacons); RXS_ERR("RX-Frags", rx_frags); + RXS_ERR("RX-Spectral", rx_spectral); if (len > size) len = size; diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index a22c0d780700..410d6d8f1aa7 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -219,6 +219,7 @@ struct ath_tx_stats { * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. * @rx_beacons: No. of beacons received. * @rx_frags: No. of rx-fragements received. + * @rx_spectral: No of spectral packets received. */ struct ath_rx_stats { u32 rx_pkts_all; @@ -237,6 +238,7 @@ struct ath_rx_stats { u32 rx_too_many_frags_err; u32 rx_beacons; u32 rx_frags; + u32 rx_spectral; }; struct ath_stats { diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d7c129bb571b..13ee37bcdb7d 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1023,9 +1023,9 @@ static s8 fix_rssi_inv_only(u8 rssi_val) return (s8) rssi_val; } - -static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, - struct ath_rx_status *rs, u64 tsf) +/* returns 1 if this was a spectral frame, even if not handled. */ +static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, + struct ath_rx_status *rs, u64 tsf) { #ifdef CONFIG_ATH_DEBUG struct ath_hw *ah = sc->sc_ah; @@ -1044,7 +1044,14 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) - return; + return 0; + + /* check if spectral scan bit is set. This does not have to be checked + * if received through a SPECTRAL phy error, but shouldn't hurt. + */ + radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; + if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) + return 0; /* Variation in the data length is possible and will be fixed later. * Note that we only support HT20 for now. @@ -1053,14 +1060,7 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, */ if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) || (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1)) - return; - - /* check if spectral scan bit is set. This does not have to be checked - * if received through a SPECTRAL phy error, but shouldn't hurt. - */ - radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; - if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) - return; + return 1; fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20; fft_sample.tlv.length = sizeof(fft_sample) - sizeof(fft_sample.tlv); @@ -1093,7 +1093,7 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32); break; default: - return; + return 1; } /* DC value (value in the middle) is the blind spot of the spectral @@ -1115,6 +1115,9 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, fft_sample.tsf = tsf; ath_debug_send_fft_sample(sc, &fft_sample.tlv); + return 1; +#else + return 0; #endif } @@ -1202,8 +1205,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) unlikely(tsf_lower - rs.rs_tstamp > 0x10000000)) rxs->mactime += 0x100000000ULL; - if ((rs.rs_status & ATH9K_RXERR_PHY)) - ath_process_fft(sc, hdr, &rs, rxs->mactime); + if (rs.rs_status & ATH9K_RXERR_PHY) { + if (ath_process_fft(sc, hdr, &rs, rxs->mactime)) { + RX_STAT_INC(rx_spectral); + goto requeue_drop_frag; + } + } retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs, rxs, &decrypt_error); From 3a325565c7fa0ba7fd1e319c5fd07665a092fb89 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Wed, 23 Jan 2013 17:38:06 +0100 Subject: [PATCH 437/478] ath9k: reorder error codes for spectral When using the spectral scan feature, frames with phy errors are returned for further processing to the driver. However, if the frames also have an invalid CRC (which seems to happen quite often), the frame is marked with bad CRC and not with the PHY error bit. The FFT processing function will thus miss the frames. Fix this by changing the precedence in error marking. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index b42be910a83d..811007ec07a7 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -605,13 +605,13 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, * reported, then decryption and MIC errors are irrelevant, * the frame is going to be dropped either way */ - if (ads.ds_rxstatus8 & AR_CRCErr) - rs->rs_status |= ATH9K_RXERR_CRC; - else if (ads.ds_rxstatus8 & AR_PHYErr) { + if (ads.ds_rxstatus8 & AR_PHYErr) { rs->rs_status |= ATH9K_RXERR_PHY; phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode); rs->rs_phyerr = phyerr; - } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) + } else if (ads.ds_rxstatus8 & AR_CRCErr) + rs->rs_status |= ATH9K_RXERR_CRC; + else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) rs->rs_status |= ATH9K_RXERR_DECRYPT; else if (ads.ds_rxstatus8 & AR_MichaelErr) rs->rs_status |= ATH9K_RXERR_MIC; From 4ab0b0aa57a794ce299b0192e924379d7353beef Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 23 Jan 2013 20:12:39 +0100 Subject: [PATCH 438/478] ath9k: Update spectral scan output data The sample data received through the spectral scan can be either in big or little endian byteorder. This information isn't stored in the output file. Therefore it is not possible for the analyzer software to find the correct byte order. It is relative common to get the data from a low end AP in big endian mode and transfer it to another computer in little endian mode to analyze it. Therefore, it would be better to store it in network (big endian) byte order. The extension of the 8 bit bins for each bin to 16 bit is not necessary. This operation can be done in userspace or on a different machine. Instead the max_exp defining the amount of shifting required for each bin is exported to userspace. The change of the output format requires a change of the type in the sample tlv to allow the userspace program to correctly detect the bin format. Reported-by: Zefir Kurtisi Signed-off-by: Sven Eckelmann [siwu@hrz.tu-chemnitz.de: squashed patches, update commit message, rebase, fix endianess bug] Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 6 +++--- drivers/net/wireless/ath/ath9k/debug.c | 6 ++++-- drivers/net/wireless/ath/ath9k/recv.c | 14 ++++++++------ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 342edc543665..67df8644fef6 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -864,7 +864,7 @@ static inline u8 spectral_bitmap_weight(u8 *bins) * interface. */ enum ath_fft_sample_type { - ATH_FFT_SAMPLE_HT20 = 0, + ATH_FFT_SAMPLE_HT20 = 1, }; struct fft_sample_tlv { @@ -876,7 +876,7 @@ struct fft_sample_tlv { struct fft_sample_ht20 { struct fft_sample_tlv tlv; - u8 __alignment; + u8 max_exp; u16 freq; s8 rssi; @@ -888,7 +888,7 @@ struct fft_sample_ht20 { u64 tsf; - u16 data[SPECTRAL_HT20_NUM_BINS]; + u8 data[SPECTRAL_HT20_NUM_BINS]; } __packed; void ath9k_tasklet(unsigned long data); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 2bbdcf89d39a..3714b971d18e 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1236,11 +1236,13 @@ static int remove_buf_file_handler(struct dentry *dentry) void ath_debug_send_fft_sample(struct ath_softc *sc, struct fft_sample_tlv *fft_sample_tlv) { + int length; if (!sc->rfs_chan_spec_scan) return; - relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, - fft_sample_tlv->length + sizeof(*fft_sample_tlv)); + length = __be16_to_cpu(fft_sample_tlv->length) + + sizeof(*fft_sample_tlv); + relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length); } static struct rchan_callbacks rfs_spec_scan_cb = { diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 13ee37bcdb7d..2af6f19d2d2a 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1035,7 +1035,7 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, struct ath_radar_info *radar_info; struct ath_ht20_mag_info *mag_info; int len = rs->rs_datalen; - int i, dc_pos; + int dc_pos; /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT @@ -1064,8 +1064,9 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20; fft_sample.tlv.length = sizeof(fft_sample) - sizeof(fft_sample.tlv); + fft_sample.tlv.length = __cpu_to_be16(fft_sample.tlv.length); - fft_sample.freq = ah->curchan->chan->center_freq; + fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq); fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); fft_sample.noise = ah->noise; @@ -1105,14 +1106,15 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, /* mag data is at the end of the frame, in front of radar_info */ mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; - /* Apply exponent and grab further auxiliary information. */ - for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) - fft_sample.data[i] = bins[i] << mag_info->max_exp; + /* copy raw bins without scaling them */ + memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS); + fft_sample.max_exp = mag_info->max_exp & 0xf; fft_sample.max_magnitude = spectral_max_magnitude(mag_info->all_bins); + fft_sample.max_magnitude = __cpu_to_be16(fft_sample.max_magnitude); fft_sample.max_index = spectral_max_index(mag_info->all_bins); fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins); - fft_sample.tsf = tsf; + fft_sample.tsf = __cpu_to_be64(tsf); ath_debug_send_fft_sample(sc, &fft_sample.tlv); return 1; From 28f2bce9f8bbf704c86f8c684337f82c51592c81 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 24 Jan 2013 21:59:32 +0100 Subject: [PATCH 439/478] rt2x00: print warning, notice and info as default Some messages provide useful information, but are disabled without CONFIG_RT2X00_DEBUG=y, so enable them by default Signed-off-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 221728090cbd..7d21e061dc88 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -88,9 +88,9 @@ #define ERROR_PROBE(__msg, __args...) \ DEBUG_PRINTK_PROBE(KERN_ERR, "Error", __msg, ##__args) #define WARNING(__dev, __msg, __args...) \ - DEBUG_PRINTK(__dev, KERN_WARNING, "Warning", __msg, ##__args) + DEBUG_PRINTK_MSG(__dev, KERN_WARNING, "Warning", __msg, ##__args) #define NOTICE(__dev, __msg, __args...) \ - DEBUG_PRINTK(__dev, KERN_NOTICE, "Notice", __msg, ##__args) + DEBUG_PRINTK_MSG(__dev, KERN_NOTICE, "Notice", __msg, ##__args) #define INFO(__dev, __msg, __args...) \ DEBUG_PRINTK_MSG(__dev, KERN_INFO, "Info", __msg, ##__args) #define DEBUG(__dev, __msg, __args...) \ From bb9c298f3193ac5b80e47b325c690700580b6bcf Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Thu, 24 Jan 2013 22:01:10 +0100 Subject: [PATCH 440/478] rt2800usb: move "TX status missed" messages to debug level Those messages can flood in dmesg, so do not print them by default. Signed-off-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 4721cada1591..d6863289471b 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -540,9 +540,9 @@ rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID); if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) { - WARNING(entry->queue->rt2x00dev, - "TX status report missed for queue %d entry %d\n", - entry->queue->qid, entry->entry_idx); + DEBUG(entry->queue->rt2x00dev, + "TX status report missed for queue %d entry %d\n", + entry->queue->qid, entry->entry_idx); return TXDONE_UNKNOWN; } From 92941382e8b80c55a4ad06b88a3bf95110969693 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 26 Jan 2013 18:13:50 +0100 Subject: [PATCH 441/478] rt2x00: remove NOTICE We use this macro only on 3 places - remove it and replace by other appropriate macros for printing messages. Signed-off-by: Stanislaw Gruszka Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 2 +- drivers/net/wireless/rt2x00/rt2x00.h | 2 -- drivers/net/wireless/rt2x00/rt2x00dev.c | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index a5c694f23d33..a658b4bc7da2 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -80,7 +80,7 @@ static inline bool rt2800_is_305x_soc(struct rt2x00_dev *rt2x00dev) rt2x00_rf(rt2x00dev, RF3022)) return true; - NOTICE(rt2x00dev, "Unknown RF chipset on rt305x\n"); + WARNING(rt2x00dev, "Unknown RF chipset on rt305x\n"); return false; } diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 7d21e061dc88..9a3f31a543ce 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -89,8 +89,6 @@ DEBUG_PRINTK_PROBE(KERN_ERR, "Error", __msg, ##__args) #define WARNING(__dev, __msg, __args...) \ DEBUG_PRINTK_MSG(__dev, KERN_WARNING, "Warning", __msg, ##__args) -#define NOTICE(__dev, __msg, __args...) \ - DEBUG_PRINTK_MSG(__dev, KERN_NOTICE, "Notice", __msg, ##__args) #define INFO(__dev, __msg, __args...) \ DEBUG_PRINTK_MSG(__dev, KERN_INFO, "Info", __msg, ##__args) #define DEBUG(__dev, __msg, __args...) \ diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index b40a53857498..92a0aa5d7b08 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1446,7 +1446,7 @@ EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); #ifdef CONFIG_PM int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state) { - NOTICE(rt2x00dev, "Going to sleep.\n"); + DEBUG(rt2x00dev, "Going to sleep.\n"); /* * Prevent mac80211 from accessing driver while suspended. @@ -1486,7 +1486,7 @@ EXPORT_SYMBOL_GPL(rt2x00lib_suspend); int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev) { - NOTICE(rt2x00dev, "Waking up.\n"); + DEBUG(rt2x00dev, "Waking up.\n"); /* * Restore/enable extra components. From 4a5fc6d7074de72dd836fbb9c5cd79f9a491871c Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Fri, 25 Jan 2013 12:47:50 +0800 Subject: [PATCH 442/478] rt2x00: allow AP and mesh mode to operate simultaneously Allow AP and Mesh mode to operate concurrently using single radio. Verify this using fonera 2.0n featuring RT3052 chipset and also TP-LINK TL-WN727N featuring RT5370 chipset. Signed-off-by: Chun-Yeow Yeoh Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00dev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 92a0aa5d7b08..1031db66474a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1236,7 +1236,8 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev) */ if_limit = &rt2x00dev->if_limits_ap; if_limit->max = rt2x00dev->ops->max_ap_intf; - if_limit->types = BIT(NL80211_IFTYPE_AP); + if_limit->types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); /* * Build up AP interface combinations structure. From f1ab57e3a114173fdb94219937191f0c673e87dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 25 Jan 2013 11:36:25 +0100 Subject: [PATCH 443/478] ssb: trivial: use pflash helper variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville --- drivers/ssb/driver_mipscore.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index 2a7684c90243..77bc8d4c1d5f 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -189,13 +189,14 @@ static void ssb_mips_serial_init(struct ssb_mipscore *mcore) static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) { struct ssb_bus *bus = mcore->dev->bus; + struct ssb_pflash *pflash = &mcore->pflash; /* When there is no chipcommon on the bus there is 4MB flash */ if (!ssb_chipco_available(&bus->chipco)) { - mcore->pflash.present = true; - mcore->pflash.buswidth = 2; - mcore->pflash.window = SSB_FLASH1; - mcore->pflash.window_size = SSB_FLASH1_SZ; + pflash->present = true; + pflash->buswidth = 2; + pflash->window = SSB_FLASH1; + pflash->window_size = SSB_FLASH1_SZ; return; } @@ -208,14 +209,14 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) break; case SSB_CHIPCO_FLASHT_PARA: pr_debug("Found parallel flash\n"); - mcore->pflash.present = true; - mcore->pflash.window = SSB_FLASH2; - mcore->pflash.window_size = SSB_FLASH2_SZ; + pflash->present = true; + pflash->window = SSB_FLASH2; + pflash->window_size = SSB_FLASH2_SZ; if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) & SSB_CHIPCO_CFG_DS16) == 0) - mcore->pflash.buswidth = 1; + pflash->buswidth = 1; else - mcore->pflash.buswidth = 2; + pflash->buswidth = 2; break; } } From c7a4a9e3880cc8cdc1f2958796e517d9d96ff7e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 25 Jan 2013 11:36:26 +0100 Subject: [PATCH 444/478] ssb: register platform device for parallel flash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: John W. Linville --- drivers/ssb/driver_mipscore.c | 29 ++++++++++++++++++++++++++++- drivers/ssb/main.c | 8 ++++++++ drivers/ssb/ssb_private.h | 4 ++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index 77bc8d4c1d5f..33b37dac40bd 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -17,6 +18,25 @@ #include "ssb_private.h" +static const char *part_probes[] = { "bcm47xxpart", NULL }; + +static struct physmap_flash_data ssb_pflash_data = { + .part_probe_types = part_probes, +}; + +static struct resource ssb_pflash_resource = { + .name = "ssb_pflash", + .flags = IORESOURCE_MEM, +}; + +struct platform_device ssb_pflash_dev = { + .name = "physmap-flash", + .dev = { + .platform_data = &ssb_pflash_data, + }, + .resource = &ssb_pflash_resource, + .num_resources = 1, +}; static inline u32 mips_read32(struct ssb_mipscore *mcore, u16 offset) @@ -197,7 +217,7 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) pflash->buswidth = 2; pflash->window = SSB_FLASH1; pflash->window_size = SSB_FLASH1_SZ; - return; + goto ssb_pflash; } /* There is ChipCommon, so use it to read info about flash */ @@ -219,6 +239,13 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) pflash->buswidth = 2; break; } + +ssb_pflash: + if (pflash->present) { + ssb_pflash_data.width = pflash->buswidth; + ssb_pflash_resource.start = pflash->window; + ssb_pflash_resource.end = pflash->window + pflash->window_size; + } } u32 ssb_cpu_clock(struct ssb_mipscore *mcore) diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index c82c5c95fe85..db7705743a8f 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -540,6 +540,14 @@ static int ssb_devices_register(struct ssb_bus *bus) dev_idx++; } +#ifdef CONFIG_SSB_DRIVER_MIPS + if (bus->mipscore.pflash.present) { + err = platform_device_register(&ssb_pflash_dev); + if (err) + pr_err("Error registering parallel flash\n"); + } +#endif + return 0; error: /* Unwind the already registered devices. */ diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 77d942630750..53198dcec90e 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -228,6 +228,10 @@ static inline int ssb_sflash_init(struct ssb_chipcommon *cc) } #endif /* CONFIG_SSB_SFLASH */ +#ifdef CONFIG_SSB_DRIVER_MIPS +extern struct platform_device ssb_pflash_dev; +#endif + #ifdef CONFIG_SSB_DRIVER_EXTIF extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks); extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms); From 73e4dbe4ca9d5573f84818964276668b42177de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 25 Jan 2013 11:37:26 +0100 Subject: [PATCH 445/478] bcma: register platform device for parallel flash Signed-off-by: John W. Linville --- drivers/bcma/bcma_private.h | 1 + drivers/bcma/driver_mips.c | 38 ++++++++++++++++++++++++++++++++----- drivers/bcma/main.c | 8 ++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index d35294e81d15..16c1dddfda00 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -47,6 +47,7 @@ int bcma_sprom_get(struct bcma_bus *bus); /* driver_chipcommon.c */ #ifdef CONFIG_BCMA_DRIVER_MIPS void bcma_chipco_serial_init(struct bcma_drv_cc *cc); +extern struct platform_device bcma_pflash_dev; #endif /* CONFIG_BCMA_DRIVER_MIPS */ /* driver_chipcommon_pmu.c */ diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index 9fe86ee16c66..9a7f0e3ab5a3 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -14,11 +14,33 @@ #include +#include +#include #include #include #include #include +static const char *part_probes[] = { "bcm47xxpart", NULL }; + +static struct physmap_flash_data bcma_pflash_data = { + .part_probe_types = part_probes, +}; + +static struct resource bcma_pflash_resource = { + .name = "bcma_pflash", + .flags = IORESOURCE_MEM, +}; + +struct platform_device bcma_pflash_dev = { + .name = "physmap-flash", + .dev = { + .platform_data = &bcma_pflash_data, + }, + .resource = &bcma_pflash_resource, + .num_resources = 1, +}; + /* The 47162a0 hangs when reading MIPS DMP registers registers */ static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev) { @@ -211,6 +233,7 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) { struct bcma_bus *bus = mcore->core->bus; struct bcma_drv_cc *cc = &bus->drv_cc; + struct bcma_pflash *pflash = &cc->pflash; switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { case BCMA_CC_FLASHT_STSER: @@ -220,15 +243,20 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) break; case BCMA_CC_FLASHT_PARA: bcma_debug(bus, "Found parallel flash\n"); - cc->pflash.present = true; - cc->pflash.window = BCMA_SOC_FLASH2; - cc->pflash.window_size = BCMA_SOC_FLASH2_SZ; + pflash->present = true; + pflash->window = BCMA_SOC_FLASH2; + pflash->window_size = BCMA_SOC_FLASH2_SZ; if ((bcma_read32(cc->core, BCMA_CC_FLASH_CFG) & BCMA_CC_FLASH_CFG_DS) == 0) - cc->pflash.buswidth = 1; + pflash->buswidth = 1; else - cc->pflash.buswidth = 2; + pflash->buswidth = 2; + + bcma_pflash_data.width = pflash->buswidth; + bcma_pflash_resource.start = pflash->window; + bcma_pflash_resource.end = pflash->window + pflash->window_size; + break; default: bcma_err(bus, "Flash type not supported\n"); diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index d12b7da556e1..95ba5756c67e 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -149,6 +149,14 @@ static int bcma_register_cores(struct bcma_bus *bus) dev_id++; } +#ifdef CONFIG_BCMA_DRIVER_MIPS + if (bus->drv_cc.pflash.present) { + err = platform_device_register(&bcma_pflash_dev); + if (err) + bcma_err(bus, "Error registering parallel flash\n"); + } +#endif + #ifdef CONFIG_BCMA_SFLASH if (bus->drv_cc.sflash.present) { err = platform_device_register(&bcma_sflash_dev); From e882efc96dba0a48adbe460b04d552d9a749a7c0 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:17:32 +0530 Subject: [PATCH 446/478] mwl8k: Stop bsses before hw specific commands For the commands, that might change the hw characteristics of the PHY device, stop the running bsses and resume them once command is complete. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 78 ++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 224cf917744a..398b8b19ff84 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -285,6 +285,9 @@ struct mwl8k_priv { char *fw_pref; char *fw_alt; struct completion firmware_loading_complete; + + /* bitmap of running BSSes */ + u32 running_bsses; }; #define MAX_WEP_KEY_LEN 13 @@ -2156,6 +2159,8 @@ static void mwl8k_fw_unlock(struct ieee80211_hw *hw) } } +static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, + u32 bitmap); /* * Command processing. @@ -2174,6 +2179,34 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) int rc; unsigned long timeout = 0; u8 buf[32]; + u32 bitmap = 0; + + wiphy_dbg(hw->wiphy, "Posting %s [%d]\n", + mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid); + + /* Before posting firmware commands that could change the hardware + * characteristics, make sure that all BSSes are stopped temporary. + * Enable these stopped BSSes after completion of the commands + */ + + rc = mwl8k_fw_lock(hw); + if (rc) + return rc; + + if (priv->ap_fw && priv->running_bsses) { + switch (le16_to_cpu(cmd->code)) { + case MWL8K_CMD_SET_RF_CHANNEL: + case MWL8K_CMD_RADIO_CONTROL: + case MWL8K_CMD_RF_TX_POWER: + case MWL8K_CMD_TX_POWER: + case MWL8K_CMD_RF_ANTENNA: + case MWL8K_CMD_RTS_THRESHOLD: + case MWL8K_CMD_MIMO_CONFIG: + bitmap = priv->running_bsses; + mwl8k_enable_bsses(hw, false, bitmap); + break; + } + } cmd->result = (__force __le16) 0xffff; dma_size = le16_to_cpu(cmd->length); @@ -2182,13 +2215,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) if (pci_dma_mapping_error(priv->pdev, dma_addr)) return -ENOMEM; - rc = mwl8k_fw_lock(hw); - if (rc) { - pci_unmap_single(priv->pdev, dma_addr, dma_size, - PCI_DMA_BIDIRECTIONAL); - return rc; - } - priv->hostcmd_wait = &cmd_wait; iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); iowrite32(MWL8K_H2A_INT_DOORBELL, @@ -2201,7 +2227,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) priv->hostcmd_wait = NULL; - mwl8k_fw_unlock(hw); pci_unmap_single(priv->pdev, dma_addr, dma_size, PCI_DMA_BIDIRECTIONAL); @@ -2228,6 +2253,11 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) ms); } + if (bitmap) + mwl8k_enable_bsses(hw, true, bitmap); + + mwl8k_fw_unlock(hw); + return rc; } @@ -3680,8 +3710,16 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int enable) { struct mwl8k_cmd_bss_start *cmd; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + struct mwl8k_priv *priv = hw->priv; int rc; + if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid))) + return 0; + + if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid))) + return 0; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; @@ -3693,9 +3731,31 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); + if (!rc) { + if (enable) + priv->running_bsses |= (1 << mwl8k_vif->macid); + else + priv->running_bsses &= ~(1 << mwl8k_vif->macid); + } return rc; } +static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif, *tmp_vif; + struct ieee80211_vif *vif; + + list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) { + vif = mwl8k_vif->vif; + + if (!(bitmap & (1 << mwl8k_vif->macid))) + continue; + + if (vif->type == NL80211_IFTYPE_AP) + mwl8k_cmd_bss_start(hw, vif, enable); + } +} /* * CMD_BASTREAM. */ @@ -5948,6 +6008,8 @@ static int mwl8k_probe(struct pci_dev *pdev, priv->hw_restart_in_progress = false; + priv->running_bsses = 0; + return rc; err_stop_firmware: From 0f4316b97d045e9b5b270cafa4b924769699eb3d Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:17:45 +0530 Subject: [PATCH 447/478] mwl8k: Do not call mwl8k_cmd_set_rf_channel unconditionally Avoid calling mwl8k_cmd_set_rf_channel unconditionally by checking IEEE80211_CONF_CHANGE_CHANNEL. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 398b8b19ff84..78a89f6426b1 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4797,9 +4797,11 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (rc) goto out; - rc = mwl8k_cmd_set_rf_channel(hw, conf); - if (rc) - goto out; + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + rc = mwl8k_cmd_set_rf_channel(hw, conf); + if (rc) + goto out; + } if (conf->power_level > 18) conf->power_level = 18; From a246ac38cdd886bf053f67d5275a20e1a99ff586 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:17:56 +0530 Subject: [PATCH 448/478] mwl8k: Move tx/rx antenna configuration to mwl8k_probe_hw This avoids calling mwl8k_cmd_rf_antenna functions every time mwl8k_config function is called. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 78a89f6426b1..b17ef49f9303 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4814,12 +4814,6 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) goto out; } - rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3); - if (rc) - wiphy_warn(hw->wiphy, "failed to set # of RX antennas"); - rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); - if (rc) - wiphy_warn(hw->wiphy, "failed to set # of TX antennas"); } else { rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); @@ -5709,6 +5703,15 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw) goto err_free_irq; } + /* Configure Antennas */ + rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3); + if (rc) + wiphy_warn(hw->wiphy, "failed to set # of RX antennas"); + rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); + if (rc) + wiphy_warn(hw->wiphy, "failed to set # of TX antennas"); + + /* Disable interrupts */ iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); From 2acdaa7a578cf43566950a6cfb70848ff6607553 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:19:55 +0530 Subject: [PATCH 449/478] mwl8k: Announce simultaneous AP-STA support on AP fw Specify the STA support in iface_limit and in wihpy->interface_modes Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index b17ef49f9303..0a22bb173739 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5799,6 +5799,7 @@ fail: static const struct ieee80211_iface_limit ap_if_limits[] = { { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, }; static const struct ieee80211_iface_combination ap_if_comb = { @@ -5891,6 +5892,7 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) if (priv->ap_macids_supported || priv->device_info->fw_image_ap) { hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); hw->wiphy->iface_combinations = &ap_if_comb; hw->wiphy->n_iface_combinations = 1; } From d59c1cfd7c106f53272143bc3f05b0b15bdd5dfa Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:20:03 +0530 Subject: [PATCH 450/478] mwl8k: Allow adding station interface on AP firmware When user wants to add a station interface when AP firmware is loaded & in-use, allow creating it and also notify user about the same. Allow adding max one STA interface for AP fw. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 0a22bb173739..84207a8e5e0a 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2519,7 +2519,7 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) priv->hw_rev = cmd->hw_rev; mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); priv->ap_macids_supported = 0x000000ff; - priv->sta_macids_supported = 0x00000000; + priv->sta_macids_supported = 0x00000100; priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues); if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) { wiphy_warn(hw->wiphy, "fw reported %d ampdu queues" @@ -4669,12 +4669,18 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, break; case NL80211_IFTYPE_STATION: if (priv->ap_fw && di->fw_image_sta) { - /* we must load the sta fw to meet this request */ - if (!list_empty(&priv->vif_list)) - return -EBUSY; - rc = mwl8k_reload_firmware(hw, di->fw_image_sta); - if (rc) - return rc; + if (!list_empty(&priv->vif_list)) { + wiphy_warn(hw->wiphy, "AP interface is running.\n" + "Adding STA interface for WDS"); + } else { + /* we must load the sta fw to + * meet this request. + */ + rc = mwl8k_reload_firmware(hw, + di->fw_image_sta); + if (rc) + return rc; + } } macids_supported = priv->sta_macids_supported; break; From af458831c49d634027811b6cf9072cdcd03961e8 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:20:12 +0530 Subject: [PATCH 451/478] mwl8k: set mac type to MWL8K_MAC_TYPE_SECONDARY_CLIENT Set mac type for station interface on AP firmware as secondary. This allows the firmware to set specific characteristics for the STA interface. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 84207a8e5e0a..4d3f140b4d32 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3538,7 +3538,10 @@ static int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw, mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) { if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) - mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; + if (priv->ap_fw) + mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; + else + mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; else mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { From 751930cb1d6da7b7891d8ffe84877cbf21915e0d Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:20:28 +0530 Subject: [PATCH 452/478] mwl8k: Enable hw encryption for STA mode on AP fw Firmware supports hardware encryption feature for the station interface running on AP firmware. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 4d3f140b4d32..01136b539f00 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4265,8 +4265,9 @@ static int mwl8k_set_key(struct ieee80211_hw *hw, u8 encr_type; u8 *addr; struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + struct mwl8k_priv *priv = hw->priv; - if (vif->type == NL80211_IFTYPE_STATION) + if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw) return -EOPNOTSUPP; if (sta == NULL) From d994a1c867ed301aaad26f769885d78e599e1daf Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:20:35 +0530 Subject: [PATCH 453/478] mwl8k: Add/Del self entry for AP interface only Add and delete self entry in the firmware sta database for AP interface only. We do not need such an entry for STA interface. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 01136b539f00..55f1e9d96ca9 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4708,7 +4708,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, /* Set the mac address. */ mwl8k_cmd_set_mac_addr(hw, vif, vif->addr); - if (priv->ap_fw) + if (vif->type == NL80211_IFTYPE_AP) mwl8k_cmd_set_new_stn_add_self(hw, vif); priv->macids_used |= 1 << mwl8k_vif->macid; @@ -4733,7 +4733,7 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - if (priv->ap_fw) + if (vif->type == NL80211_IFTYPE_AP) mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); mwl8k_cmd_del_mac_addr(hw, vif, vif->addr); From 41bf911910fc41996878afc679c352d89477d626 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:20:42 +0530 Subject: [PATCH 454/478] mwl8k: Choose interface specific calls on vif type Choose interface specific function calls based on interface type instead of firmware types. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 55f1e9d96ca9..a9d5c09974de 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4984,11 +4984,9 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { - struct mwl8k_priv *priv = hw->priv; - - if (!priv->ap_fw) + if (vif->type == NL80211_IFTYPE_STATION) mwl8k_bss_info_changed_sta(hw, vif, info, changed); - else + if (vif->type == NL80211_IFTYPE_AP) mwl8k_bss_info_changed_ap(hw, vif, info, changed); } From dcee7438caa409948347c0a3821e3ab6e7459984 Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 25 Jan 2013 16:20:50 +0530 Subject: [PATCH 455/478] mwl8k: Do not call STA specific cmds not supported by the AP fw While using STA mode in the AP firmware, avoid calling some firmware commands which are not supported by the AP firmware. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Nishant Sarmukadam Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a9d5c09974de..2031130d860b 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4881,7 +4881,8 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rcu_read_unlock(); } - if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) { + if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc && + !priv->ap_fw) { rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates); if (rc) goto out; @@ -4889,6 +4890,25 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rc = mwl8k_cmd_use_fixed_rate_sta(hw); if (rc) goto out; + } else { + if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc && + priv->ap_fw) { + int idx; + int rate; + + /* Use AP firmware specific rate command. + */ + idx = ffs(vif->bss_conf.basic_rates); + if (idx) + idx--; + + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rate = mwl8k_rates_24[idx].hw_value; + else + rate = mwl8k_rates_50[idx].hw_value; + + mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); + } } if (changed & BSS_CHANGED_ERP_PREAMBLE) { @@ -4898,13 +4918,13 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto out; } - if (changed & BSS_CHANGED_ERP_SLOT) { + if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw) { rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot); if (rc) goto out; } - if (vif->bss_conf.assoc && + if (vif->bss_conf.assoc && !priv->ap_fw && (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT))) { rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); From baf58b0e8f0e460e252e40f5fdf393b01a3cef7d Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Fri, 25 Jan 2013 10:32:30 -0800 Subject: [PATCH 456/478] rtl8723ae: Fix misspellings sucess->success Signed-off-by: Anatol Pomozov Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8723ae/fw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c index f55b1767ef57..3b71dab4f3b2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c @@ -252,7 +252,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw, u16 box_reg = 0, box_extreg = 0; u8 u1tmp; bool isfw_rd = false; - bool bwrite_sucess = false; + bool bwrite_success = false; u8 wait_h2c_limmit = 100; u8 wait_writeh2c_limmit = 100; u8 boxcontent[4], boxextcontent[2]; @@ -291,7 +291,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw, } } - while (!bwrite_sucess) { + while (!bwrite_success) { wait_writeh2c_limmit--; if (wait_writeh2c_limmit == 0) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, @@ -429,7 +429,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw, break; } - bwrite_sucess = true; + bwrite_success = true; rtlhal->last_hmeboxnum = boxnum + 1; if (rtlhal->last_hmeboxnum == 4) From 3220712d3aa4f5df12b7305ce340f6d216a84477 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 26 Jan 2013 00:56:03 +0400 Subject: [PATCH 457/478] mwifiex: don't return zero on failure paths in mwifiex_pcie_init() If pci_iomap() fails in mwifiex_pcie_init(), it breaks off initialization, deallocates all resources, but returns zero. The patch adds -EIO as return value in this case. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Acked-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/pcie.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 7415ac8c3ad7..df88e65595c8 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1985,6 +1985,7 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) card->pci_mmap = pci_iomap(pdev, 0, 0); if (!card->pci_mmap) { dev_err(adapter->dev, "iomap(0) error\n"); + ret = -EIO; goto err_iomap0; } ret = pci_request_region(pdev, 2, DRV_NAME); @@ -1995,6 +1996,7 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) card->pci_mmap1 = pci_iomap(pdev, 2, 0); if (!card->pci_mmap1) { dev_err(adapter->dev, "iomap(2) error\n"); + ret = -EIO; goto err_iomap2; } From 5b2e2eccab80e2f56d06df582e473fb0d8630f35 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 25 Jan 2013 18:23:00 -0800 Subject: [PATCH 458/478] mwifiex: do not overwrite error code from lower layer driver Instead of converting it to a bogus error code -1, we should return the original error code from lower layer driver. This error code will be printed so it may give user some clues on what has happened. Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 10 ++++------ drivers/net/wireless/mwifiex/usb.c | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 5a1c1d0e5599..e35b67a9e6a6 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -332,7 +332,7 @@ mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, u32 pkt_len, u32 port) { struct sdio_mmc_card *card = adapter->card; - int ret = -1; + int ret; u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; @@ -350,8 +350,7 @@ mwifiex_write_data_sync(struct mwifiex_adapter *adapter, sdio_claim_host(card->func); - if (!sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size)) - ret = 0; + ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); sdio_release_host(card->func); @@ -365,7 +364,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, u32 len, u32 port, u8 claim) { struct sdio_mmc_card *card = adapter->card; - int ret = -1; + int ret; u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; @@ -376,8 +375,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, if (claim) sdio_claim_host(card->func); - if (!sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size)) - ret = 0; + ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); if (claim) sdio_release_host(card->func); diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 5d4a10a8a005..f90fe21e5bfd 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -672,7 +672,7 @@ static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, *len, &actual_length, timeout); if (ret) { dev_err(adapter->dev, "usb_bulk_msg for tx failed: %d\n", ret); - ret = -1; + return ret; } *len = actual_length; @@ -691,7 +691,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, *len, &actual_length, timeout); if (ret) { dev_err(adapter->dev, "usb_bulk_msg for rx failed: %d\n", ret); - ret = -1; + return ret; } *len = actual_length; From a6ca2e10f795111a90a4efabb07717258669e03d Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 26 Jan 2013 21:38:35 +0100 Subject: [PATCH 459/478] ssb: add gpio_to_irq The old bcm47xx gpio code had support for gpio_to_irq, but the new code did not provide this function, but returned -ENXIO all the time. This patch adds the missing function. arch/mips/bcm47xx/wgt634u.c calls gpio_to_irq() and got the correct irq number with the old gpio handling code. With this patch the code in wgt634u.c should work again. I do not have a wgt634u to test this. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/ssb/driver_gpio.c | 22 ++++++++++++++++++++++ include/linux/ssb/ssb_driver_mips.h | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/drivers/ssb/driver_gpio.c b/drivers/ssb/driver_gpio.c index 97ac0a38e3d0..accabe39b320 100644 --- a/drivers/ssb/driver_gpio.c +++ b/drivers/ssb/driver_gpio.c @@ -74,6 +74,16 @@ static void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned gpio) ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0); } +static int ssb_gpio_chipco_to_irq(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + if (bus->bustype == SSB_BUSTYPE_SSB) + return ssb_mips_irq(bus->chipco.dev) + 2; + else + return -EINVAL; +} + static int ssb_gpio_chipco_init(struct ssb_bus *bus) { struct gpio_chip *chip = &bus->gpio; @@ -86,6 +96,7 @@ static int ssb_gpio_chipco_init(struct ssb_bus *bus) chip->set = ssb_gpio_chipco_set_value; chip->direction_input = ssb_gpio_chipco_direction_input; chip->direction_output = ssb_gpio_chipco_direction_output; + chip->to_irq = ssb_gpio_chipco_to_irq; chip->ngpio = 16; /* There is just one SoC in one device and its GPIO addresses should be * deterministic to address them more easily. The other buses could get @@ -134,6 +145,16 @@ static int ssb_gpio_extif_direction_output(struct gpio_chip *chip, return 0; } +static int ssb_gpio_extif_to_irq(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + if (bus->bustype == SSB_BUSTYPE_SSB) + return ssb_mips_irq(bus->extif.dev) + 2; + else + return -EINVAL; +} + static int ssb_gpio_extif_init(struct ssb_bus *bus) { struct gpio_chip *chip = &bus->gpio; @@ -144,6 +165,7 @@ static int ssb_gpio_extif_init(struct ssb_bus *bus) chip->set = ssb_gpio_extif_set_value; chip->direction_input = ssb_gpio_extif_direction_input; chip->direction_output = ssb_gpio_extif_direction_output; + chip->to_irq = ssb_gpio_extif_to_irq; chip->ngpio = 5; /* There is just one SoC in one device and its GPIO addresses should be * deterministic to address them more easily. The other buses could get diff --git a/include/linux/ssb/ssb_driver_mips.h b/include/linux/ssb/ssb_driver_mips.h index 07a9c7a2e088..afe79d40a99e 100644 --- a/include/linux/ssb/ssb_driver_mips.h +++ b/include/linux/ssb/ssb_driver_mips.h @@ -45,6 +45,11 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore) { } +static inline unsigned int ssb_mips_irq(struct ssb_device *dev) +{ + return 0; +} + #endif /* CONFIG_SSB_DRIVER_MIPS */ #endif /* LINUX_SSB_MIPSCORE_H_ */ From 8f1ca2683225afa21b827ff620a6225c390771a9 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 26 Jan 2013 21:39:44 +0100 Subject: [PATCH 460/478] bcma: add gpio_to_irq The old bcm47xx gpio code had support for gpio_to_irq, but the new code did not provide this function, but returned -ENXIO all the time. This patch adds the missing function. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_gpio.c | 11 +++++++++++ include/linux/bcma/bcma_driver_mips.h | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index 9a6f585da2d9..0b5df538dfd9 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -73,6 +73,16 @@ static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio) bcma_chipco_gpio_pullup(cc, 1 << gpio, 0); } +static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) +{ + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); + + if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) + return bcma_core_irq(cc->core); + else + return -EINVAL; +} + int bcma_gpio_init(struct bcma_drv_cc *cc) { struct gpio_chip *chip = &cc->gpio; @@ -85,6 +95,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc) chip->set = bcma_gpio_set_value; chip->direction_input = bcma_gpio_direction_input; chip->direction_output = bcma_gpio_direction_output; + chip->to_irq = bcma_gpio_to_irq; chip->ngpio = 16; /* There is just one SoC in one device and its GPIO addresses should be * deterministic to address them more easily. The other buses could get diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h index 0d1ea297851a..fb61f3fb4ddb 100644 --- a/include/linux/bcma/bcma_driver_mips.h +++ b/include/linux/bcma/bcma_driver_mips.h @@ -42,13 +42,18 @@ struct bcma_drv_mips { #ifdef CONFIG_BCMA_DRIVER_MIPS extern void bcma_core_mips_init(struct bcma_drv_mips *mcore); extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore); + +extern unsigned int bcma_core_irq(struct bcma_device *core); #else static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { } static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { } + +static inline unsigned int bcma_core_irq(struct bcma_device *core) +{ + return 0; +} #endif extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore); -extern unsigned int bcma_core_irq(struct bcma_device *core); - #endif /* LINUX_BCMA_DRIVER_MIPS_H_ */ From 00bcd0d699c7b3c294c1b276709e069b2903bc32 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 26 Jan 2013 22:19:35 +0100 Subject: [PATCH 461/478] iwlegacy: fix antenna bitmask This patch is based on "iwlwifi: fix antenna bitmask". (362b0563b28506d53) Like the new iwlagn devices, the old 4965N device only supports a maximum of three antennas. Hence only three bits are used, the fourth bit is likely the A-MPDU indicator. Cc: Stanislaw Gruszka Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/commands.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h index 25dd7d28d022..829d3b9aaa17 100644 --- a/drivers/net/wireless/iwlegacy/commands.h +++ b/drivers/net/wireless/iwlegacy/commands.h @@ -1134,7 +1134,7 @@ struct il_wep_cmd { #define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) -#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0xf0 +#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70 #define RX_RES_PHY_FLAGS_ANTENNA_POS 4 #define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) From 44ceaf371b683b0e90cbc0fb111c6d90b87d79cf Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 27 Jan 2013 10:55:41 -0800 Subject: [PATCH 462/478] net: iwlegacy: remove unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: drivers/net/wireless/iwlegacy/4965.c: In function ‘il4965_post_associate’: drivers/net/wireless/iwlegacy/4965.c:1751:25: warning: variable ‘conf’ set but not used [-Wunused-but-set-variable] seen when building allmodconfig on x86_64 with W=1 by removing the unused variable. Signed-off-by: Guenter Roeck Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/4965.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/iwlegacy/4965.c b/drivers/net/wireless/iwlegacy/4965.c index 5db11714e047..91eb2d07fdb8 100644 --- a/drivers/net/wireless/iwlegacy/4965.c +++ b/drivers/net/wireless/iwlegacy/4965.c @@ -1748,7 +1748,6 @@ static void il4965_post_associate(struct il_priv *il) { struct ieee80211_vif *vif = il->vif; - struct ieee80211_conf *conf = NULL; int ret = 0; if (!vif || !il->is_open) @@ -1759,8 +1758,6 @@ il4965_post_associate(struct il_priv *il) il_scan_cancel_timeout(il, 200); - conf = &il->hw->conf; - il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; il_commit_rxon(il); From 44ba973699b831414c3f8eef68ee5a7fe1208a05 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 27 Jan 2013 16:40:34 -0600 Subject: [PATCH 463/478] rtlwifi: Eliminate two empty routines Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rc.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c index c1e065f136ba..204f46c4510d 100644 --- a/drivers/net/wireless/rtlwifi/rc.c +++ b/drivers/net/wireless/rtlwifi/rc.c @@ -217,19 +217,6 @@ static void rtl_tx_status(void *ppriv, } } -static void rtl_rate_init(void *ppriv, - struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) -{ -} - -static void rtl_rate_update(void *ppriv, - struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - u32 changed) -{ -} - static void *rtl_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { @@ -274,8 +261,6 @@ static struct rate_control_ops rtl_rate_ops = { .free = rtl_rate_free, .alloc_sta = rtl_rate_alloc_sta, .free_sta = rtl_rate_free_sta, - .rate_init = rtl_rate_init, - .rate_update = rtl_rate_update, .tx_status = rtl_tx_status, .get_rate = rtl_get_rate, }; From 485f107d341cb1d09e010e0466b02a1ad026f489 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sun, 27 Jan 2013 23:42:48 +0100 Subject: [PATCH 464/478] mwifiex: avoid out of bounds access in mwifiex_get_common_rates. Check that the array indexes are in-bounds before accessing the rate2 and tmp arrays. Found with the following semantic patch: @@ identifier t; identifier idx; expression E; statement S; @@ * for (... ; <+... t[idx] ...+> && idx < E ; ...) S Signed-off-by: Cyril Roelandt Acked-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/join.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 893d809ba83c..f0a634d237c1 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -157,8 +157,8 @@ static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1, memset(rate1, 0, rate1_size); - for (i = 0; rate2[i] && i < rate2_size; i++) { - for (j = 0; tmp[j] && j < rate1_size; j++) { + for (i = 0; i < rate2_size && rate2[i]; i++) { + for (j = 0; j < rate1_size && tmp[j]; j++) { /* Check common rate, excluding the bit for basic rate */ if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { From 7269494e12449e8bd4850e69b653562e369558b3 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:30:56 +0200 Subject: [PATCH 465/478] wil6210: Detect FW error In the firmware, added is ability to report internal errors using IRQ. Catch this IRQ and notify user space via netlink User space get notified like (udevadm monitor --kernel --property): KERNEL[12660.320520] change /devices/pci0000:00/0000:00:1c.1/0000:02:00.0/0000:03:01.0/0000:05:00.0/net/wlan12 (net) ACTION=change DEVPATH=/devices/pci0000:00/0000:00:1c.1/0000:02:00.0/0000:03:01.0/0000:05:00.0/net/wlan12 DEVTYPE=wlan EVENT=FW_ERROR IFINDEX=6 INTERFACE=wlan12 SEQNUM=2489 SOURCE=wil6210 SUBSYSTEM=net Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/interrupt.c | 22 +++++++++++++++++++- drivers/net/wireless/ath/wil6210/wil6210.h | 8 +++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 38049da71049..d2109d53674a 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -38,7 +38,9 @@ #define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) -#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) +#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | \ + ISR_MISC_MBOX_EVT | \ + ISR_MISC_FW_ERROR) #define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ BIT_DMA_PSEUDO_CAUSE_TX | \ @@ -228,6 +230,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) return IRQ_HANDLED; } +static void wil_notify_fw_error(struct wil6210_priv *wil) +{ + struct device *dev = &wil_to_ndev(wil)->dev; + char *envp[3] = { + [0] = "SOURCE=wil6210", + [1] = "EVENT=FW_ERROR", + [2] = NULL, + }; + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); +} + static irqreturn_t wil6210_irq_misc(int irq, void *cookie) { struct wil6210_priv *wil = cookie; @@ -244,6 +257,13 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) wil6210_mask_irq_misc(wil); + if (isr & ISR_MISC_FW_ERROR) { + wil_dbg_IRQ(wil, "IRQ: Firmware error\n"); + clear_bit(wil_status_fwready, &wil->status); + wil_notify_fw_error(wil); + isr &= ~ISR_MISC_FW_ERROR; + } + if (isr & ISR_MISC_FW_READY) { wil_dbg_IRQ(wil, "IRQ: FW ready\n"); /** diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 9bcfffa4006c..3bd244750d1e 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -101,8 +101,7 @@ struct RGF_ICR { #define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */ #define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0) #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1) - #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28) - #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29) + #define BIT_DMA_EP_MISC_ICR_FW_INT(n) BIT(28+n) /* n = [0..3] */ /* Interrupt moderation control */ #define RGF_DMA_ITR_CNT_TRSH (0x881c5c) @@ -121,8 +120,9 @@ struct RGF_ICR { #define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 /* ISR register bits */ -#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 -#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 +#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT(0) +#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT(1) +#define ISR_MISC_FW_ERROR BIT_DMA_EP_MISC_ICR_FW_INT(3) /* Hardware definitions end */ From 4789d728e5e5e2dbf347d793917523db8776548c Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:30:57 +0200 Subject: [PATCH 466/478] wil6210: rearrange IRQ debug printing Make printings from IRQ appears in dmesg in chronological order Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/interrupt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index d2109d53674a..660bacf32d01 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -390,6 +390,8 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie) if (wil6210_debug_irq_mask(wil, pseudo_cause)) return IRQ_NONE; + wil_dbg_IRQ(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); + wil6210_mask_irq_pseudo(wil); /* Discover real IRQ cause @@ -421,8 +423,6 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie) if (rc != IRQ_WAKE_THREAD) wil6210_unmask_irq_pseudo(wil); - wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause); - return rc; } From 2057ebb2bd5da3aab1603d546f7a019b9a971ee8 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:30:58 +0200 Subject: [PATCH 467/478] wil6210: remove raw wil_dbg() calls Introduce debug category "MISC", convert all raw wil_dbg() to this category. This improves dynamic debug manageability Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 8 +++--- drivers/net/wireless/ath/wil6210/main.c | 28 ++++++++++----------- drivers/net/wireless/ath/wil6210/pcie_bus.c | 4 +-- drivers/net/wireless/ath/wil6210/txrx.c | 8 +++--- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 116f4e807ae1..56f39b654199 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -228,8 +228,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } /* 0-based channel indexes */ cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; - wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch, - request->channels[i]->center_freq); + wil_dbg_MISC(wil, "Scan for ch %d : %d MHz\n", ch, + request->channels[i]->center_freq); } return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + @@ -425,8 +425,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, return -EINVAL; } - wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, - channel->center_freq, info->privacy ? "secure" : "open"); + wil_dbg_MISC(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, + channel->center_freq, info->privacy ? "secure" : "open"); print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, info->ssid, info->ssid_len); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 95fcd361322b..90786dfc5ff1 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -64,7 +64,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; - wil_dbg(wil, "%s()\n", __func__); + wil_dbg_MISC(wil, "%s()\n", __func__); wil_link_off(wil); clear_bit(wil_status_fwconnected, &wil->status); @@ -99,7 +99,7 @@ static void wil_connect_timer_fn(ulong x) { struct wil6210_priv *wil = (void *)x; - wil_dbg(wil, "Connect timeout\n"); + wil_dbg_MISC(wil, "Connect timeout\n"); /* reschedule to thread context - disconnect won't * run from atomic context @@ -109,7 +109,7 @@ static void wil_connect_timer_fn(ulong x) int wil_priv_init(struct wil6210_priv *wil) { - wil_dbg(wil, "%s()\n", __func__); + wil_dbg_MISC(wil, "%s()\n", __func__); mutex_init(&wil->mutex); mutex_init(&wil->wmi_mutex); @@ -162,7 +162,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) static void wil_target_reset(struct wil6210_priv *wil) { - wil_dbg(wil, "Resetting...\n"); + wil_dbg_MISC(wil, "Resetting...\n"); /* register write */ #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) @@ -202,7 +202,7 @@ static void wil_target_reset(struct wil6210_priv *wil) msleep(2000); - wil_dbg(wil, "Reset completed\n"); + wil_dbg_MISC(wil, "Reset completed\n"); #undef W #undef S @@ -225,8 +225,8 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil) wil_err(wil, "Firmware not ready\n"); return -ETIME; } else { - wil_dbg(wil, "FW ready after %d ms\n", - jiffies_to_msecs(to-left)); + wil_dbg_MISC(wil, "FW ready after %d ms\n", + jiffies_to_msecs(to-left)); } return 0; } @@ -278,7 +278,7 @@ void wil_link_on(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); - wil_dbg(wil, "%s()\n", __func__); + wil_dbg_MISC(wil, "%s()\n", __func__); netif_carrier_on(ndev); netif_tx_wake_all_queues(ndev); @@ -288,7 +288,7 @@ void wil_link_off(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); - wil_dbg(wil, "%s()\n", __func__); + wil_dbg_MISC(wil, "%s()\n", __func__); netif_tx_stop_all_queues(ndev); netif_carrier_off(ndev); @@ -311,27 +311,27 @@ static int __wil_up(struct wil6210_priv *wil) wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: - wil_dbg(wil, "type: STATION\n"); + wil_dbg_MISC(wil, "type: STATION\n"); bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_AP: - wil_dbg(wil, "type: AP\n"); + wil_dbg_MISC(wil, "type: AP\n"); bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_CLIENT: - wil_dbg(wil, "type: P2P_CLIENT\n"); + wil_dbg_MISC(wil, "type: P2P_CLIENT\n"); bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_GO: - wil_dbg(wil, "type: P2P_GO\n"); + wil_dbg_MISC(wil, "type: P2P_GO\n"); bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_MONITOR: - wil_dbg(wil, "type: Monitor\n"); + wil_dbg_MISC(wil, "type: Monitor\n"); bi = 0; ndev->type = ARPHRD_IEEE80211_RADIOTAP; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 0fc83edd6bad..ab1cadebe1c4 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -53,7 +53,7 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) } wil->n_msi = use_msi; if (wil->n_msi) { - wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi); + wil_dbg_MISC(wil, "Setup %d MSI interrupts\n", use_msi); rc = pci_enable_msi_block(pdev, wil->n_msi); if (rc && (wil->n_msi == 3)) { wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); @@ -65,7 +65,7 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) wil->n_msi = 0; } } else { - wil_dbg(wil, "MSI interrupts disabled, use INTx\n"); + wil_dbg_MISC(wil, "MSI interrupts disabled, use INTx\n"); } rc = wil6210_init_irq(wil, pdev->irq); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index f29c294413cf..241dd0e90543 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -100,8 +100,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) d->dma.status = TX_DMA_STATUS_DU; } - wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, - vring->va, (unsigned long long)vring->pa, vring->ctx); + wil_dbg_MISC(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, + vring->va, (unsigned long long)vring->pa, vring->ctx); return 0; } @@ -528,8 +528,8 @@ int wil_rx_init(struct wil6210_priv *wil) vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); - wil_dbg(wil, "Rx init: status %d tail 0x%08x\n", - le32_to_cpu(evt.evt.status), vring->hwtail); + wil_dbg_MISC(wil, "Rx init: status %d tail 0x%08x\n", + le32_to_cpu(evt.evt.status), vring->hwtail); rc = wil_rx_refill(wil, vring->size); if (rc) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 3bd244750d1e..a9b6f70f585f 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -275,6 +275,7 @@ struct wil6210_priv { #define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) #define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) #define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) +#define wil_dbg_MISC(wil, fmt, arg...) wil_dbg(wil, "DBG[MISC]" fmt, ##arg) #define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \ groupsize, buf, len, ascii) \ From 47e19af9a791d8862cdffb754cc2347b6389d487 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:30:59 +0200 Subject: [PATCH 468/478] wil6210: Refactor rx init/fini Move WMI related operations to wmi.c as helper functions Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 54 +--------------- drivers/net/wireless/ath/wil6210/wil6210.h | 2 + drivers/net/wireless/ath/wil6210/wmi.c | 73 ++++++++++++++++++++++ 3 files changed, 77 insertions(+), 52 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 241dd0e90543..f3b523b47060 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -484,53 +484,18 @@ void wil_rx_handle(struct wil6210_priv *wil) int wil_rx_init(struct wil6210_priv *wil) { - struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *wdev = wil->wdev; struct vring *vring = &wil->vring_rx; int rc; - struct wmi_cfg_rx_chain_cmd cmd = { - .action = WMI_RX_CHAIN_ADD, - .rx_sw_ring = { - .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), - }, - .mid = 0, /* TODO - what is it? */ - .decap_trans_type = WMI_DECAP_TYPE_802_3, - }; - struct { - struct wil6210_mbox_hdr_wmi wmi; - struct wmi_cfg_rx_chain_done_event evt; - } __packed evt; vring->size = WIL6210_RX_RING_SIZE; rc = wil_vring_alloc(wil, vring); if (rc) return rc; - cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); - cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size); - if (wdev->iftype == NL80211_IFTYPE_MONITOR) { - struct ieee80211_channel *ch = wdev->preset_chandef.chan; - - cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); - if (ch) - cmd.sniffer_cfg.channel = ch->hw_value - 1; - cmd.sniffer_cfg.phy_info_mode = - cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); - cmd.sniffer_cfg.phy_support = - cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) - ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); - } - /* typical time for secure PCP is 840ms */ - rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), - WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); + rc = wmi_rx_chain_add(wil, vring); if (rc) goto err_free; - vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); - - wil_dbg_MISC(wil, "Rx init: status %d tail 0x%08x\n", - le32_to_cpu(evt.evt.status), vring->hwtail); - rc = wil_rx_refill(wil, vring->size); if (rc) goto err_free; @@ -547,22 +512,7 @@ void wil_rx_fini(struct wil6210_priv *wil) struct vring *vring = &wil->vring_rx; if (vring->va) { - int rc; - struct wmi_cfg_rx_chain_cmd cmd = { - .action = cpu_to_le32(WMI_RX_CHAIN_DEL), - .rx_sw_ring = { - .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), - }, - }; - struct { - struct wil6210_mbox_hdr_wmi wmi; - struct wmi_cfg_rx_chain_done_event cfg; - } __packed wmi_rx_cfg_reply; - - rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), - WMI_CFG_RX_CHAIN_DONE_EVENTID, - &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), - 100); + wmi_rx_chain_del(wil); wil_vring_free(wil, vring, 0); } } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index a9b6f70f585f..9b2ebdd20de8 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -329,6 +329,8 @@ int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, const void *mac_addr, int key_len, const void *key); int wmi_echo(struct wil6210_priv *wil); int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); +int wmi_rx_chain_del(struct wil6210_priv *wil); +int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring); int wil6210_init_irq(struct wil6210_priv *wil, int irq); void wil6210_fini_irq(struct wil6210_priv *wil, int irq); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 12915f6e7617..d109c3f7e5e9 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -18,8 +18,10 @@ #include #include #include +#include #include "wil6210.h" +#include "txrx.h" #include "wmi.h" /** @@ -853,6 +855,77 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) return rc; } +int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) +{ + struct wireless_dev *wdev = wil->wdev; + struct net_device *ndev = wil_to_ndev(wil); + struct wmi_cfg_rx_chain_cmd cmd = { + .action = WMI_RX_CHAIN_ADD, + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + .ring_mem_base = cpu_to_le64(vring->pa), + .ring_size = cpu_to_le16(vring->size), + }, + .mid = 0, /* TODO - what is it? */ + .decap_trans_type = WMI_DECAP_TYPE_802_3, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event evt; + } __packed evt; + int rc; + + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { + struct ieee80211_channel *ch = wdev->preset_chandef.chan; + + cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); + if (ch) + cmd.sniffer_cfg.channel = ch->hw_value - 1; + cmd.sniffer_cfg.phy_info_mode = + cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); + cmd.sniffer_cfg.phy_support = + cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) + ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); + } + /* typical time for secure PCP is 840ms */ + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); + if (rc) + return rc; + + vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); + + wil_dbg_MISC(wil, "Rx init: status %d tail 0x%08x\n", + le32_to_cpu(evt.evt.status), vring->hwtail); + + if (le32_to_cpu(evt.evt.status) != WMI_CFG_RX_CHAIN_SUCCESS) + rc = -EINVAL; + + return rc; +} + +int wmi_rx_chain_del(struct wil6210_priv *wil) +{ + int rc; + struct wmi_cfg_rx_chain_cmd cmd = { + .action = cpu_to_le32(WMI_RX_CHAIN_DEL), + .rx_sw_ring = { + .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cfg_rx_chain_done_event cfg; + } __packed wmi_rx_cfg_reply; + + rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), + WMI_CFG_RX_CHAIN_DONE_EVENTID, + &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), + 100); + + return rc; +} + void wmi_event_flush(struct wil6210_priv *wil) { struct pending_wmi_event *evt, *t; From 795ce734380b729e076131528e485e98a580d3e8 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:00 +0200 Subject: [PATCH 469/478] wil6210: Count Tx statistics on Tx completion This allows to account for Tx errors Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index f3b523b47060..e7ea5384a614 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -752,8 +752,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) } switch (rc) { case 0: - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; + /* statistics will be updated on the tx_complete */ dev_kfree_skb_any(skb); return NETDEV_TX_OK; case -ENOMEM: @@ -777,6 +776,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) */ void wil_tx_complete(struct wil6210_priv *wil, int ringid) { + struct net_device *ndev = wil_to_ndev(wil); struct device *dev = wil_to_dev(wil); struct vring *vring = &wil->vring_tx[ringid]; @@ -804,6 +804,13 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); skb = vring->ctx[vring->swtail]; if (skb) { + if (d->dma.error == 0) { + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + } else { + ndev->stats.tx_errors++; + } + dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); dev_kfree_skb_any(skb); vring->ctx[vring->swtail] = NULL; From cb901733b4d6f6d1f00354f62de7df826cf1dfe8 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:01 +0200 Subject: [PATCH 470/478] wil6210: Fix: Tx stall Due to multi-tx-queue design, wil_start_xmit() used to be executed concurrently for different queues. Then, these transmits delivered to the same queue, creating race. As result, Tx descriptor may be skipped, causing stall in hardware. Convert to single Tx queue fixes it. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/netdev.c | 27 +--------------------- drivers/net/wireless/ath/wil6210/wil6210.h | 2 -- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 3068b5cb53a7..073dc7d39259 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -35,35 +35,10 @@ static int wil_stop(struct net_device *ndev) return wil_down(wil); } -/* - * AC to queue mapping - * - * AC_VO -> queue 3 - * AC_VI -> queue 2 - * AC_BE -> queue 1 - * AC_BK -> queue 0 - */ -static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) -{ - static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; - struct wil6210_priv *wil = ndev_to_wil(ndev); - u16 rc; - - skb->priority = cfg80211_classify8021d(skb); - - rc = wil_1d_to_queue[skb->priority]; - - wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority, - (int)rc); - - return rc; -} - static const struct net_device_ops wil_netdev_ops = { .ndo_open = wil_open, .ndo_stop = wil_stop, .ndo_start_xmit = wil_start_xmit, - .ndo_select_queue = wil_select_queue, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; @@ -97,7 +72,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); - ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); + ndev = alloc_netdev(0, "wlan%d", ether_setup); if (!ndev) { dev_err(dev, "alloc_netdev_mqs failed\n"); rc = -ENOMEM; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 9b2ebdd20de8..d95728636f6a 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -36,8 +36,6 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL6210_MEM_SIZE (2*1024*1024UL) -#define WIL6210_TX_QUEUES (4) - #define WIL6210_RX_RING_SIZE (128) #define WIL6210_TX_RING_SIZE (128) #define WIL6210_MAX_TX_RINGS (24) From 241804cb7b09d05c1e79735fa51521b211f51fd1 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:02 +0200 Subject: [PATCH 471/478] wil6210: Call skb_orphan() right before Rx indication Other parts of Rx path (BACK logic) will need to access associated data Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index e7ea5384a614..649f50476105 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -430,6 +430,8 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) int rc; unsigned int len = skb->len; + skb_orphan(skb); + if (in_interrupt()) rc = netif_rx(skb); else @@ -464,8 +466,6 @@ void wil_rx_handle(struct wil6210_priv *wil) wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); - skb_orphan(skb); - if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { skb->dev = ndev; skb_reset_mac_header(skb); From b98917d742bcf519b15894e78c17f2eb287b3584 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:03 +0200 Subject: [PATCH 472/478] wil6210: Fix "don't scan after connect" logic When connect times out, scan was not re-enabled. Strictly say, it is firmware issue - it should issue "disconnect" event but it does not. Compensate in the driver. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/main.c | 2 ++ drivers/net/wireless/ath/wil6210/wmi.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 90786dfc5ff1..3e9a71f125be 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -85,6 +85,8 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) wil_vring_fini_tx(wil, i); + + clear_bit(wil_status_dontscan, &wil->status); } static void wil_disconnect_worker(struct work_struct *work) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index d109c3f7e5e9..dffb0026309c 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -459,7 +459,6 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, wil->sinfo_gen++; wil6210_disconnect(wil, evt->bssid); - clear_bit(wil_status_dontscan, &wil->status); } static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) From 56f004b4672972b6c936573eca835ed90787097a Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:04 +0200 Subject: [PATCH 473/478] wil6210: Separate common code for mbox regs caching to function Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/main.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 3e9a71f125be..4c1533088fe5 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -109,6 +109,15 @@ static void wil_connect_timer_fn(ulong x) schedule_work(&wil->disconnect_worker); } +static void wil_cache_mbox_regs(struct wil6210_priv *wil) +{ + /* make shadow copy of registers that should not change on run time */ + wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, + sizeof(struct wil6210_mbox_ctl)); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); + wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); +} + int wil_priv_init(struct wil6210_priv *wil) { wil_dbg_MISC(wil, "%s()\n", __func__); @@ -138,11 +147,7 @@ int wil_priv_init(struct wil6210_priv *wil) return -EAGAIN; } - /* make shadow copy of registers that should not change on run time */ - wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, - sizeof(struct wil6210_mbox_ctl)); - wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); - wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + wil_cache_mbox_regs(wil); return 0; } @@ -260,11 +265,7 @@ int wil_reset(struct wil6210_priv *wil) wil->pending_connect_cid = -1; INIT_COMPLETION(wil->wmi_ready); - /* make shadow copy of registers that should not change on run time */ - wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, - sizeof(struct wil6210_mbox_ctl)); - wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); - wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + wil_cache_mbox_regs(wil); /* TODO: release MAC reset */ wil6210_enable_irq(wil); From e08b5906ccf323e2ad7b0448329b7cbcfda4c94e Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:05 +0200 Subject: [PATCH 474/478] wil6210: Reorder reset preparation sequence Disable interrupts first to prevent spurious WMI events arrival Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 4c1533088fe5..aa598f37899b 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -250,14 +250,14 @@ int wil_reset(struct wil6210_priv *wil) cancel_work_sync(&wil->disconnect_worker); wil6210_disconnect(wil, NULL); - wmi_event_flush(wil); - - flush_workqueue(wil->wmi_wq); - flush_workqueue(wil->wmi_wq_conn); - wil6210_disable_irq(wil); wil->status = 0; + wmi_event_flush(wil); + + flush_workqueue(wil->wmi_wq_conn); + flush_workqueue(wil->wmi_wq); + /* TODO: put MAC in reset */ wil_target_reset(wil); From 7743882d6f59b675809b59b5ac570677c94c426f Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:06 +0200 Subject: [PATCH 475/478] wil6210: fix checkpatch CamelCase warnings Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 4 +- drivers/net/wireless/ath/wil6210/interrupt.c | 32 +++++------ drivers/net/wireless/ath/wil6210/main.c | 26 ++++----- drivers/net/wireless/ath/wil6210/pcie_bus.c | 4 +- drivers/net/wireless/ath/wil6210/txrx.c | 30 +++++------ drivers/net/wireless/ath/wil6210/wil6210.h | 12 ++--- drivers/net/wireless/ath/wil6210/wmi.c | 56 ++++++++++---------- 7 files changed, 82 insertions(+), 82 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 56f39b654199..4b48f2ee1b68 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -228,7 +228,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } /* 0-based channel indexes */ cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; - wil_dbg_MISC(wil, "Scan for ch %d : %d MHz\n", ch, + wil_dbg_misc(wil, "Scan for ch %d : %d MHz\n", ch, request->channels[i]->center_freq); } @@ -425,7 +425,7 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, return -EINVAL; } - wil_dbg_MISC(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, + wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, channel->center_freq, info->privacy ? "secure" : "open"); print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, info->ssid, info->ssid_len); diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 660bacf32d01..865c33f1b1ce 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -96,7 +96,7 @@ static void wil6210_mask_irq_misc(struct wil6210_priv *wil) static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) { - wil_dbg_IRQ(wil, "%s()\n", __func__); + wil_dbg_irq(wil, "%s()\n", __func__); iowrite32(WIL6210_IRQ_DISABLE, wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); @@ -127,7 +127,7 @@ static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) { - wil_dbg_IRQ(wil, "%s()\n", __func__); + wil_dbg_irq(wil, "%s()\n", __func__); set_bit(wil_status_irqen, &wil->status); @@ -137,7 +137,7 @@ static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) void wil6210_disable_irq(struct wil6210_priv *wil) { - wil_dbg_IRQ(wil, "%s()\n", __func__); + wil_dbg_irq(wil, "%s()\n", __func__); wil6210_mask_irq_tx(wil); wil6210_mask_irq_rx(wil); @@ -147,7 +147,7 @@ void wil6210_disable_irq(struct wil6210_priv *wil) void wil6210_enable_irq(struct wil6210_priv *wil) { - wil_dbg_IRQ(wil, "%s()\n", __func__); + wil_dbg_irq(wil, "%s()\n", __func__); iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + offsetof(struct RGF_ICR, ICC)); @@ -169,7 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_RX_ICR) + offsetof(struct RGF_ICR, ICR)); - wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr); + wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); if (!isr) { wil_err(wil, "spurious IRQ: RX\n"); @@ -179,7 +179,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) wil6210_mask_irq_rx(wil); if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { - wil_dbg_IRQ(wil, "RX done\n"); + wil_dbg_irq(wil, "RX done\n"); isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; wil_rx_handle(wil); } @@ -199,7 +199,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_TX_ICR) + offsetof(struct RGF_ICR, ICR)); - wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr); + wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); if (!isr) { wil_err(wil, "spurious IRQ: TX\n"); @@ -210,13 +210,13 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { uint i; - wil_dbg_IRQ(wil, "TX done\n"); + wil_dbg_irq(wil, "TX done\n"); isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; for (i = 0; i < 24; i++) { u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); if (isr & mask) { isr &= ~mask; - wil_dbg_IRQ(wil, "TX done(%i)\n", i); + wil_dbg_irq(wil, "TX done(%i)\n", i); wil_tx_complete(wil, i); } } @@ -248,7 +248,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICR)); - wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr); + wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr); if (!isr) { wil_err(wil, "spurious IRQ: MISC\n"); @@ -258,14 +258,14 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) wil6210_mask_irq_misc(wil); if (isr & ISR_MISC_FW_ERROR) { - wil_dbg_IRQ(wil, "IRQ: Firmware error\n"); + wil_dbg_irq(wil, "IRQ: Firmware error\n"); clear_bit(wil_status_fwready, &wil->status); wil_notify_fw_error(wil); isr &= ~ISR_MISC_FW_ERROR; } if (isr & ISR_MISC_FW_READY) { - wil_dbg_IRQ(wil, "IRQ: FW ready\n"); + wil_dbg_irq(wil, "IRQ: FW ready\n"); /** * Actual FW ready indicated by the * WMI_FW_READY_EVENTID @@ -288,10 +288,10 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) struct wil6210_priv *wil = cookie; u32 isr = wil->isr_misc; - wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr); + wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); if (isr & ISR_MISC_MBOX_EVT) { - wil_dbg_IRQ(wil, "MBOX event\n"); + wil_dbg_irq(wil, "MBOX event\n"); wmi_recv_cmd(wil); isr &= ~ISR_MISC_MBOX_EVT; } @@ -313,7 +313,7 @@ static irqreturn_t wil6210_thread_irq(int irq, void *cookie) { struct wil6210_priv *wil = cookie; - wil_dbg_IRQ(wil, "Thread IRQ\n"); + wil_dbg_irq(wil, "Thread IRQ\n"); /* Discover real IRQ cause */ if (wil->isr_misc) wil6210_irq_misc_thread(irq, cookie); @@ -390,7 +390,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie) if (wil6210_debug_irq_mask(wil, pseudo_cause)) return IRQ_NONE; - wil_dbg_IRQ(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); + wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); wil6210_mask_irq_pseudo(wil); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index aa598f37899b..d160ea84e76a 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -64,7 +64,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; - wil_dbg_MISC(wil, "%s()\n", __func__); + wil_dbg_misc(wil, "%s()\n", __func__); wil_link_off(wil); clear_bit(wil_status_fwconnected, &wil->status); @@ -101,7 +101,7 @@ static void wil_connect_timer_fn(ulong x) { struct wil6210_priv *wil = (void *)x; - wil_dbg_MISC(wil, "Connect timeout\n"); + wil_dbg_misc(wil, "Connect timeout\n"); /* reschedule to thread context - disconnect won't * run from atomic context @@ -120,7 +120,7 @@ static void wil_cache_mbox_regs(struct wil6210_priv *wil) int wil_priv_init(struct wil6210_priv *wil) { - wil_dbg_MISC(wil, "%s()\n", __func__); + wil_dbg_misc(wil, "%s()\n", __func__); mutex_init(&wil->mutex); mutex_init(&wil->wmi_mutex); @@ -169,7 +169,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) static void wil_target_reset(struct wil6210_priv *wil) { - wil_dbg_MISC(wil, "Resetting...\n"); + wil_dbg_misc(wil, "Resetting...\n"); /* register write */ #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) @@ -209,7 +209,7 @@ static void wil_target_reset(struct wil6210_priv *wil) msleep(2000); - wil_dbg_MISC(wil, "Reset completed\n"); + wil_dbg_misc(wil, "Reset completed\n"); #undef W #undef S @@ -232,7 +232,7 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil) wil_err(wil, "Firmware not ready\n"); return -ETIME; } else { - wil_dbg_MISC(wil, "FW ready after %d ms\n", + wil_dbg_misc(wil, "FW ready after %d ms\n", jiffies_to_msecs(to-left)); } return 0; @@ -281,7 +281,7 @@ void wil_link_on(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); - wil_dbg_MISC(wil, "%s()\n", __func__); + wil_dbg_misc(wil, "%s()\n", __func__); netif_carrier_on(ndev); netif_tx_wake_all_queues(ndev); @@ -291,7 +291,7 @@ void wil_link_off(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); - wil_dbg_MISC(wil, "%s()\n", __func__); + wil_dbg_misc(wil, "%s()\n", __func__); netif_tx_stop_all_queues(ndev); netif_carrier_off(ndev); @@ -314,27 +314,27 @@ static int __wil_up(struct wil6210_priv *wil) wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: - wil_dbg_MISC(wil, "type: STATION\n"); + wil_dbg_misc(wil, "type: STATION\n"); bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_AP: - wil_dbg_MISC(wil, "type: AP\n"); + wil_dbg_misc(wil, "type: AP\n"); bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_CLIENT: - wil_dbg_MISC(wil, "type: P2P_CLIENT\n"); + wil_dbg_misc(wil, "type: P2P_CLIENT\n"); bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_GO: - wil_dbg_MISC(wil, "type: P2P_GO\n"); + wil_dbg_misc(wil, "type: P2P_GO\n"); bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_MONITOR: - wil_dbg_MISC(wil, "type: Monitor\n"); + wil_dbg_misc(wil, "type: Monitor\n"); bi = 0; ndev->type = ARPHRD_IEEE80211_RADIOTAP; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index ab1cadebe1c4..81c35c6e3832 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -53,7 +53,7 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) } wil->n_msi = use_msi; if (wil->n_msi) { - wil_dbg_MISC(wil, "Setup %d MSI interrupts\n", use_msi); + wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi); rc = pci_enable_msi_block(pdev, wil->n_msi); if (rc && (wil->n_msi == 3)) { wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); @@ -65,7 +65,7 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) wil->n_msi = 0; } } else { - wil_dbg_MISC(wil, "MSI interrupts disabled, use INTx\n"); + wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n"); } rc = wil6210_init_irq(wil, pdev->irq); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 649f50476105..f0d091313805 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -100,7 +100,7 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) d->dma.status = TX_DMA_STATUS_DU; } - wil_dbg_MISC(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, + wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, vring->va, (unsigned long long)vring->pa, vring->ctx); return 0; @@ -353,8 +353,8 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) wil_rx_add_radiotap_header(wil, skb, d); - wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); - wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4, + wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); wil_vring_advance_head(vring, 1); @@ -369,7 +369,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, */ ftype = wil_rxdesc_ftype(d) << 2; if (ftype != IEEE80211_FTYPE_DATA) { - wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype); + wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype); /* TODO: process it */ kfree_skb(skb); return NULL; @@ -461,9 +461,9 @@ void wil_rx_handle(struct wil6210_priv *wil) wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); return; } - wil_dbg_TXRX(wil, "%s()\n", __func__); + wil_dbg_txrx(wil, "%s()\n", __func__); while (NULL != (skb = wil_vring_reap_rx(wil, v))) { - wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { @@ -639,7 +639,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, uint i = swhead; dma_addr_t pa; - wil_dbg_TXRX(wil, "%s()\n", __func__); + wil_dbg_txrx(wil, "%s()\n", __func__); if (avail < vring->size/8) netif_tx_stop_all_queues(wil_to_ndev(wil)); @@ -656,9 +656,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), + wil_dbg_txrx(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), skb->data, (unsigned long long)pa); - wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1, + wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); if (unlikely(dma_mapping_error(dev, pa))) @@ -687,12 +687,12 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); - wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4, + wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); /* advance swhead */ wil_vring_advance_head(vring, nr_frags + 1); - wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); + wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); /* hold reference to skb * to prevent skb release before accounting @@ -725,7 +725,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) struct vring *vring; int rc; - wil_dbg_TXRX(wil, "%s()\n", __func__); + wil_dbg_txrx(wil, "%s()\n", __func__); if (!test_bit(wil_status_fwready, &wil->status)) { wil_err(wil, "FW not ready\n"); goto drop; @@ -785,7 +785,7 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) return; } - wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid); + wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid); while (!wil_vring_is_empty(vring)) { volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; @@ -794,11 +794,11 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) if (!(d->dma.status & TX_DMA_STATUS_DU)) break; - wil_dbg_TXRX(wil, + wil_dbg_txrx(wil, "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", vring->swtail, d->dma.length, d->dma.status, d->dma.error); - wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4, + wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index d95728636f6a..69e5162be28a 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -270,18 +270,18 @@ struct wil6210_priv { #define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) #define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) -#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) -#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) -#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) -#define wil_dbg_MISC(wil, fmt, arg...) wil_dbg(wil, "DBG[MISC]" fmt, ##arg) +#define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) +#define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) +#define wil_dbg_wmi(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) +#define wil_dbg_misc(wil, fmt, arg...) wil_dbg(wil, "DBG[MISC]" fmt, ##arg) -#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \ +#define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize, \ groupsize, buf, len, ascii) \ wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\ prefix_type, rowsize, \ groupsize, buf, len, ascii) -#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \ +#define wil_hex_dump_wmi(prefix_str, prefix_type, rowsize, \ groupsize, buf, len, ascii) \ wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\ prefix_type, rowsize, \ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index dffb0026309c..91f41a71d243 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -215,7 +215,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) } /* next head */ next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); - wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); + wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); /* wait till FW finish with previous command */ for (retry = 5; retry > 0; retry--) { r->tail = ioread32(wil->csr + HOST_MBOX + @@ -236,10 +236,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) } cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); /* set command */ - wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len); - wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, + wil_dbg_wmi(wil, "WMI command 0x%04x [%d]\n", cmdid, len); + wil_hex_dump_wmi("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, sizeof(cmd), true); - wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, + wil_hex_dump_wmi("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); @@ -275,7 +275,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) struct wmi_ready_event *evt = d; u32 ver = le32_to_cpu(evt->sw_version); - wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); + wil_dbg_wmi(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); if (!is_valid_ether_addr(ndev->dev_addr)) { memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); @@ -288,7 +288,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, int len) { - wil_dbg_WMI(wil, "WMI: FW ready\n"); + wil_dbg_wmi(wil, "WMI: FW ready\n"); set_bit(wil_status_fwready, &wil->status); /* reuse wmi_ready for the firmware ready indication */ @@ -311,11 +311,11 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) u32 d_len = le32_to_cpu(data->info.len); u16 d_status = le16_to_cpu(data->info.status); - wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n", + wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n", data->info.channel, data->info.mcs, data->info.snr); - wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, + wil_dbg_wmi(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, le16_to_cpu(data->info.stype)); - wil_dbg_WMI(wil, "qid %d mid %d cid %d\n", + wil_dbg_wmi(wil, "qid %d mid %d cid %d\n", data->info.qid, data->info.mid, data->info.cid); if (!channel) { @@ -331,13 +331,13 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, u.beacon.variable); - wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap); + wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap); bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, tsf, cap, bi, ie_buf, ie_len, signal, GFP_KERNEL); if (bss) { - wil_dbg_WMI(wil, "Added BSS %pM\n", + wil_dbg_wmi(wil, "Added BSS %pM\n", rx_mgmt_frame->bssid); cfg80211_put_bss(bss); } else { @@ -353,7 +353,7 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, struct wmi_scan_complete_event *data = d; bool aborted = (data->status != 0); - wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); cfg80211_scan_done(wil->scan_request, aborted); wil->scan_request = NULL; } else { @@ -388,9 +388,9 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) return; } ch = evt->channel + 1; - wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n", + wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n", evt->bssid, ch, evt->cid); - wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, + wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, evt->assoc_info, len - sizeof(*evt), true); /* figure out IE's */ @@ -452,7 +452,7 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, { struct wmi_disconnect_event *evt = d; - wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n", + wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n", evt->bssid, evt->protocol_reason_status, evt->disconnect_reason); @@ -477,7 +477,7 @@ static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); - wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n" + wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n" "BF status 0x%08x SNR 0x%08x\n" "Tx Tpt %d goodput %d Rx goodput %d\n" "Sectors(rx:tx) my %d:%d peer %d:%d\n", @@ -502,7 +502,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, struct sk_buff *skb; struct ethhdr *eth; - wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len, + wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len, evt->src_mac); if (eapol_len > 196) { /* TODO: revisit size limit */ @@ -600,15 +600,15 @@ void wmi_recv_cmd(struct wil6210_priv *wil) iowrite32(0, wil->csr + HOSTADDR(r->tail) + offsetof(struct wil6210_mbox_ring_desc, sync)); /* indicate */ - wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n", + wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), hdr.flags); if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { - wil_dbg_WMI(wil, "WMI event 0x%04x\n", + wil_dbg_wmi(wil, "WMI event 0x%04x\n", evt->event.wmi.id); } - wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1, + wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1, &evt->event.hdr, sizeof(hdr) + len, true); /* advance tail */ @@ -624,7 +624,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) { int q = queue_work(wil->wmi_wq, &wil->wmi_event_worker); - wil_dbg_WMI(wil, "queue_work -> %d\n", q); + wil_dbg_wmi(wil, "queue_work -> %d\n", q); } } } @@ -651,7 +651,7 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, cmdid, reply_id, to_msec); rc = -ETIME; } else { - wil_dbg_WMI(wil, + wil_dbg_wmi(wil, "wmi_call(0x%04x->0x%04x) completed in %d msec\n", cmdid, reply_id, to_msec - jiffies_to_msecs(remain)); @@ -681,7 +681,7 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) memcpy(cmd.mac, addr, ETH_ALEN); - wil_dbg_WMI(wil, "Set MAC %pM\n", addr); + wil_dbg_wmi(wil, "Set MAC %pM\n", addr); return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); } @@ -779,7 +779,7 @@ int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) skb_set_mac_header(skb, 0); eth = eth_hdr(skb); - wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); + wil_dbg_wmi(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) goto found_dest; @@ -894,7 +894,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); - wil_dbg_MISC(wil, "Rx init: status %d tail 0x%08x\n", + wil_dbg_misc(wil, "Rx init: status %d tail 0x%08x\n", le32_to_cpu(evt.evt.status), vring->hwtail); if (le32_to_cpu(evt.evt.status) != WMI_CFG_RX_CHAIN_SUCCESS) @@ -929,7 +929,7 @@ void wmi_event_flush(struct wil6210_priv *wil) { struct pending_wmi_event *evt, *t; - wil_dbg_WMI(wil, "%s()\n", __func__); + wil_dbg_wmi(wil, "%s()\n", __func__); list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { list_del(&evt->list); @@ -971,7 +971,7 @@ static void wmi_event_handle(struct wil6210_priv *wil, wmi_evt_call_handler(wil, id, evt_data, len - sizeof(*wmi)); } - wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id); + wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id); complete(&wil->wmi_ready); return; } @@ -1036,7 +1036,7 @@ void wmi_connect_worker(struct work_struct *work) return; } - wil_dbg_WMI(wil, "Configure for connection CID %d\n", + wil_dbg_wmi(wil, "Configure for connection CID %d\n", wil->pending_connect_cid); rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, From afda8bb50b950b5ee598e3dd0388b06ad1226eed Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:07 +0200 Subject: [PATCH 476/478] wil6210: checkpatch warnings Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 1 - drivers/net/wireless/ath/wil6210/interrupt.c | 1 - drivers/net/wireless/ath/wil6210/main.c | 4 ++-- drivers/net/wireless/ath/wil6210/netdev.c | 4 ++-- drivers/net/wireless/ath/wil6210/txrx.c | 3 +-- drivers/net/wireless/ath/wil6210/wmi.c | 1 - 6 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 4b48f2ee1b68..002851fceb2f 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -204,7 +204,6 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, break; default: return -EOPNOTSUPP; - } /* FW don't support scan after connection attempt */ diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 865c33f1b1ce..dc97e7b2609c 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -52,7 +52,6 @@ static inline void wil_icr_clear(u32 x, void __iomem *addr) { - } #else /* defined(CONFIG_WIL6210_ISR_COR) */ /* configure to Write-1-to-Clear mode */ diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index d160ea84e76a..761c389586d4 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -80,7 +80,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) GFP_KERNEL); break; default: - ; + break; } for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) @@ -357,7 +357,7 @@ static int __wil_up(struct wil6210_priv *wil) wmi_set_channel(wil, channel->hw_value); break; default: - ; + break; } /* MAC address - pre-requisite for other commands */ diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 073dc7d39259..8ce2e33dce20 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -39,8 +39,8 @@ static const struct net_device_ops wil_netdev_ops = { .ndo_open = wil_open, .ndo_stop = wil_stop, .ndo_start_xmit = wil_start_xmit, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, }; void *wil_if_alloc(struct device *dev, void __iomem *csr) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index f0d091313805..48cfa7e13781 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -758,8 +758,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) case -ENOMEM: return NETDEV_TX_BUSY; default: - ; /* goto drop; */ - break; + break; /* goto drop; */ } drop: netif_tx_stop_all_queues(ndev); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 91f41a71d243..8178cc0ce650 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -188,7 +188,6 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) wil_err(wil, "WMI size too large: %d bytes, max is %d\n", (int)(sizeof(cmd) + len), r->entry_size); return -ERANGE; - } might_sleep(); From 2acb4220b264f9d229db01ccc390b8beb37b55af Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:08 +0200 Subject: [PATCH 477/478] wil6210: Never delete Rx chain with firmware Firmware crash on attempt to delete Rx chain. Driver part of Rx chain removed only in preparation for the target reset; as reset is the only flow that removes Rx chain in the firmware. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 4 +--- drivers/net/wireless/ath/wil6210/wil6210.h | 1 - drivers/net/wireless/ath/wil6210/wmi.c | 22 ---------------------- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 48cfa7e13781..198595118396 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -511,10 +511,8 @@ void wil_rx_fini(struct wil6210_priv *wil) { struct vring *vring = &wil->vring_rx; - if (vring->va) { - wmi_rx_chain_del(wil); + if (vring->va) wil_vring_free(wil, vring, 0); - } } int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 69e5162be28a..aea961ff8f08 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -327,7 +327,6 @@ int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, const void *mac_addr, int key_len, const void *key); int wmi_echo(struct wil6210_priv *wil); int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); -int wmi_rx_chain_del(struct wil6210_priv *wil); int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring); int wil6210_init_irq(struct wil6210_priv *wil, int irq); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 8178cc0ce650..0b70e17cd1fb 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -902,28 +902,6 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) return rc; } -int wmi_rx_chain_del(struct wil6210_priv *wil) -{ - int rc; - struct wmi_cfg_rx_chain_cmd cmd = { - .action = cpu_to_le32(WMI_RX_CHAIN_DEL), - .rx_sw_ring = { - .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), - }, - }; - struct { - struct wil6210_mbox_hdr_wmi wmi; - struct wmi_cfg_rx_chain_done_event cfg; - } __packed wmi_rx_cfg_reply; - - rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), - WMI_CFG_RX_CHAIN_DONE_EVENTID, - &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), - 100); - - return rc; -} - void wmi_event_flush(struct wil6210_priv *wil) { struct pending_wmi_event *evt, *t; From c331997b6c9ad7f4b8075e6e60d3caa6e36f5938 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 28 Jan 2013 18:31:09 +0200 Subject: [PATCH 478/478] wil6210: fix wil_vring_init_tx status In case vring setup with the firmware failed, success status was returned. fix it. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 198595118396..64b971fdc3cc 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -565,6 +565,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) { wil_err(wil, "Tx config failed, status 0x%02x\n", reply.cmd.status); + rc = -EINVAL; goto out_free; } vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);