summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2012-04-12 20:12:31 -0400
committerDavid S. Miller <davem@davemloft.net>2012-04-12 20:12:31 -0400
commit816a7854d56f19aeba3c83e1ed8c2c0ef275365a (patch)
treec3b82ad98006fc5fa7b408c142e75a6053195dfa
parent011e3c63251be832d23df9f0697626ab7b354d02 (diff)
parent7eab0f64a9eba5405222fdef0ede2468bf495efd (diff)
downloadlinux-3.10-816a7854d56f19aeba3c83e1ed8c2c0ef275365a.tar.gz
linux-3.10-816a7854d56f19aeba3c83e1ed8c2c0ef275365a.tar.bz2
linux-3.10-816a7854d56f19aeba3c83e1ed8c2c0ef275365a.zip
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
-rw-r--r--Documentation/DocBook/80211.tmpl2
-rw-r--r--Documentation/networking/mac80211-auth-assoc-deauth.txt10
-rw-r--r--drivers/net/wireless/ath/ath5k/ani.c44
-rw-r--r--drivers/net/wireless/ath/ath5k/ath5k.h29
-rw-r--r--drivers/net/wireless/ath/ath5k/attach.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c22
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.c17
-rw-r--r--drivers/net/wireless/ath/ath5k/desc.c6
-rw-r--r--drivers/net/wireless/ath/ath5k/dma.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/initvals.c5
-rw-r--r--drivers/net/wireless/ath/ath5k/led.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/pci.c4
-rw-r--r--drivers/net/wireless/ath/ath5k/phy.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/qcu.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/reset.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/sysfs.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/main.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/txrx.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile5
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.c49
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.h6
-rw-r--r--drivers/net/wireless/ath/ath9k/ar5008_phy.c38
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h10
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c49
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h5
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c24
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h21
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs.c80
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs.h8
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c300
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h104
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs_pri_detector.c390
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs_pri_detector.h52
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c11
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_hst.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c73
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c19
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c49
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/rc.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c39
-rw-r--r--drivers/net/wireless/ath/carl9170/cmd.h6
-rw-r--r--drivers/net/wireless/ath/carl9170/fw.c2
-rw-r--r--drivers/net/wireless/ath/main.c4
-rw-r--r--drivers/net/wireless/ath/regd.c4
-rw-r--r--drivers/net/wireless/b43/main.c16
-rw-r--r--drivers/net/wireless/b43/xmit.c2
-rw-r--r--drivers/net/wireless/b43legacy/xmit.c14
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/d11.h2
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.h9
-rw-r--r--drivers/net/wireless/ipw2x00/libipw.h55
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c4
-rw-r--r--drivers/net/wireless/iwlegacy/4965-rs.c2
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig8
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-2000.c19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-hw.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-lib.c24
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rs.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rx.c7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rxon.c261
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c144
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c786
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.h23
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c484
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h15
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c58
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h24
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-mac80211.c14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-notif-wait.c44
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-notif-wait.h21
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-op-mode.h17
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-pci.c3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-phy-db.c273
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-phy-db.h123
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-power.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c53
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-shared.h32
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-testmode.c9
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h148
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c514
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c218
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie.c270
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h95
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-ucode.c133
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c32
-rw-r--r--drivers/net/wireless/mwifiex/11n.c17
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c81
-rw-r--r--drivers/net/wireless/mwifiex/fw.h23
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h49
-rw-r--r--drivers/net/wireless/mwifiex/join.c14
-rw-r--r--drivers/net/wireless/mwifiex/main.h14
-rw-r--r--drivers/net/wireless/mwifiex/scan.c17
-rw-r--r--drivers/net/wireless/mwifiex/sdio.h8
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c98
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c80
-rw-r--r--drivers/net/wireless/mwifiex/sta_event.c15
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c33
-rw-r--r--drivers/net/wireless/p54/main.c11
-rw-r--r--drivers/net/wireless/p54/p54.h1
-rw-r--r--drivers/net/wireless/p54/p54usb.c195
-rw-r--r--drivers/net/wireless/p54/p54usb.h3
-rw-r--r--drivers/net/wireless/p54/txrx.c3
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00config.c5
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00leds.c16
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c10
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c41
-rw-r--r--drivers/net/wireless/rtlwifi/cam.c5
-rw-r--r--drivers/net/wireless/rtlwifi/rc.c3
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/trx.h7
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/trx.h8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/def.h7
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/fw.h6
-rw-r--r--drivers/net/wireless/rtlwifi/wifi.h24
-rw-r--r--drivers/net/wireless/wl12xx/main.c5
-rw-r--r--include/linux/ieee80211.h32
-rw-r--r--include/linux/nl80211.h41
-rw-r--r--include/net/cfg80211.h33
-rw-r--r--include/net/mac80211.h142
-rw-r--r--net/mac80211/Kconfig11
-rw-r--r--net/mac80211/Makefile3
-rw-r--r--net/mac80211/agg-rx.c18
-rw-r--r--net/mac80211/agg-tx.c57
-rw-r--r--net/mac80211/cfg.c60
-rw-r--r--net/mac80211/chan.c26
-rw-r--r--net/mac80211/debugfs_netdev.c83
-rw-r--r--net/mac80211/debugfs_sta.c5
-rw-r--r--net/mac80211/driver-ops.h41
-rw-r--r--net/mac80211/driver-trace.h55
-rw-r--r--net/mac80211/ht.c9
-rw-r--r--net/mac80211/ibss.c22
-rw-r--r--net/mac80211/ieee80211_i.h66
-rw-r--r--net/mac80211/iface.c150
-rw-r--r--net/mac80211/main.c12
-rw-r--r--net/mac80211/mesh.c53
-rw-r--r--net/mac80211/mesh.h25
-rw-r--r--net/mac80211/mesh_hwmp.c28
-rw-r--r--net/mac80211/mesh_plink.c17
-rw-r--r--net/mac80211/mesh_sync.c296
-rw-r--r--net/mac80211/mlme.c303
-rw-r--r--net/mac80211/pm.c4
-rw-r--r--net/mac80211/rate.h7
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c15
-rw-r--r--net/mac80211/rx.c10
-rw-r--r--net/mac80211/sta_info.c16
-rw-r--r--net/mac80211/sta_info.h11
-rw-r--r--net/mac80211/tx.c79
-rw-r--r--net/mac80211/util.c193
-rw-r--r--net/mac80211/wme.c46
-rw-r--r--net/mac80211/wme.h3
-rw-r--r--net/wireless/core.c5
-rw-r--r--net/wireless/mesh.c3
-rw-r--r--net/wireless/mlme.c59
-rw-r--r--net/wireless/nl80211.c61
-rw-r--r--net/wireless/nl80211.h4
-rw-r--r--net/wireless/reg.c10
-rw-r--r--net/wireless/scan.c2
165 files changed, 5416 insertions, 3008 deletions
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl
index c5ac6929c41..f3e214f9e25 100644
--- a/Documentation/DocBook/80211.tmpl
+++ b/Documentation/DocBook/80211.tmpl
@@ -516,7 +516,7 @@
!Finclude/net/mac80211.h ieee80211_start_tx_ba_cb_irqsafe
!Finclude/net/mac80211.h ieee80211_stop_tx_ba_session
!Finclude/net/mac80211.h ieee80211_stop_tx_ba_cb_irqsafe
-!Finclude/net/mac80211.h rate_control_changed
+!Finclude/net/mac80211.h ieee80211_rate_control_changed
!Finclude/net/mac80211.h ieee80211_tx_rate_control
!Finclude/net/mac80211.h rate_control_send_low
</chapter>
diff --git a/Documentation/networking/mac80211-auth-assoc-deauth.txt b/Documentation/networking/mac80211-auth-assoc-deauth.txt
index e0a2aa585ca..d7a15fe91bf 100644
--- a/Documentation/networking/mac80211-auth-assoc-deauth.txt
+++ b/Documentation/networking/mac80211-auth-assoc-deauth.txt
@@ -23,7 +23,7 @@ BA session stop & deauth/disassoc frames
end note
end
-mac80211->driver: config(channel, non-HT)
+mac80211->driver: config(channel, channel type)
mac80211->driver: bss_info_changed(set BSSID, basic rate bitmap)
mac80211->driver: sta_state(AP, exists)
@@ -51,7 +51,7 @@ note over mac80211,driver: cleanup like for authenticate
end
alt not previously authenticated (FT)
-mac80211->driver: config(channel, non-HT)
+mac80211->driver: config(channel, channel type)
mac80211->driver: bss_info_changed(set BSSID, basic rate bitmap)
mac80211->driver: sta_state(AP, exists)
mac80211->driver: sta_state(AP, authenticated)
@@ -67,10 +67,6 @@ end
mac80211->driver: set up QoS parameters
-alt is HT channel
-mac80211->driver: config(channel, HT params)
-end
-
mac80211->driver: bss_info_changed(QoS, HT, associated with AID)
mac80211->userspace: associated
@@ -95,5 +91,5 @@ mac80211->driver: sta_state(AP,exists)
mac80211->driver: sta_state(AP,not-exists)
mac80211->driver: turn off powersave
mac80211->driver: bss_info_changed(clear BSSID, not associated, no QoS, ...)
-mac80211->driver: config(non-HT channel type)
+mac80211->driver: config(channel type to non-HT)
mac80211->userspace: disconnected
diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c
index 35e93704c4e..5c008757662 100644
--- a/drivers/net/wireless/ath/ath5k/ani.c
+++ b/drivers/net/wireless/ath/ath5k/ani.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
@@ -728,33 +730,25 @@ void
ath5k_ani_print_counters(struct ath5k_hw *ah)
{
/* clears too */
- printk(KERN_NOTICE "ACK fail\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_ACK_FAIL));
- printk(KERN_NOTICE "RTS fail\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_RTS_FAIL));
- printk(KERN_NOTICE "RTS success\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_RTS_OK));
- printk(KERN_NOTICE "FCS error\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_FCS_FAIL));
+ pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL));
+ pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL));
+ pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK));
+ pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL));
/* no clear */
- printk(KERN_NOTICE "tx\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX));
- printk(KERN_NOTICE "rx\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX));
- printk(KERN_NOTICE "busy\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR));
- printk(KERN_NOTICE "cycles\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE));
-
- printk(KERN_NOTICE "AR5K_PHYERR_CNT1\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1));
- printk(KERN_NOTICE "AR5K_PHYERR_CNT2\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2));
- printk(KERN_NOTICE "AR5K_OFDM_FIL_CNT\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT));
- printk(KERN_NOTICE "AR5K_CCK_FIL_CNT\t%d\n",
- ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT));
+ pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX));
+ pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX));
+ pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR));
+ pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE));
+
+ pr_notice("AR5K_PHYERR_CNT1\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1));
+ pr_notice("AR5K_PHYERR_CNT2\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2));
+ pr_notice("AR5K_OFDM_FIL_CNT\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT));
+ pr_notice("AR5K_CCK_FIL_CNT\t%d\n",
+ ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT));
}
#endif
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 8d434b8f585..55ef93dd743 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -76,26 +76,29 @@
GENERIC DRIVER DEFINITIONS
\****************************/
-#define ATH5K_PRINTF(fmt, ...) \
- printk(KERN_WARNING "%s: " fmt, __func__, ##__VA_ARGS__)
+#define ATH5K_PRINTF(fmt, ...) \
+ pr_warn("%s: " fmt, __func__, ##__VA_ARGS__)
-#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \
- printk(_level "ath5k %s: " _fmt, \
- ((_sc) && (_sc)->hw) ? wiphy_name((_sc)->hw->wiphy) : "", \
- ##__VA_ARGS__)
+void __printf(3, 4)
+_ath5k_printk(const struct ath5k_hw *ah, const char *level,
+ const char *fmt, ...);
-#define ATH5K_PRINTK_LIMIT(_sc, _level, _fmt, ...) do { \
- if (net_ratelimit()) \
- ATH5K_PRINTK(_sc, _level, _fmt, ##__VA_ARGS__); \
- } while (0)
+#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \
+ _ath5k_printk(_sc, _level, _fmt, ##__VA_ARGS__)
-#define ATH5K_INFO(_sc, _fmt, ...) \
+#define ATH5K_PRINTK_LIMIT(_sc, _level, _fmt, ...) \
+do { \
+ if (net_ratelimit()) \
+ ATH5K_PRINTK(_sc, _level, _fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define ATH5K_INFO(_sc, _fmt, ...) \
ATH5K_PRINTK(_sc, KERN_INFO, _fmt, ##__VA_ARGS__)
-#define ATH5K_WARN(_sc, _fmt, ...) \
+#define ATH5K_WARN(_sc, _fmt, ...) \
ATH5K_PRINTK_LIMIT(_sc, KERN_WARNING, _fmt, ##__VA_ARGS__)
-#define ATH5K_ERR(_sc, _fmt, ...) \
+#define ATH5K_ERR(_sc, _fmt, ...) \
ATH5K_PRINTK_LIMIT(_sc, KERN_ERR, _fmt, ##__VA_ARGS__)
/*
diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c
index d7114c75fe9..7106547a14d 100644
--- a/drivers/net/wireless/ath/ath5k/attach.c
+++ b/drivers/net/wireless/ath/ath5k/attach.c
@@ -20,6 +20,8 @@
* Attach/Detach Functions and helpers *
\*************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/pci.h>
#include <linux/slab.h>
#include "ath5k.h"
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 0e643b016b3..3007bba12d9 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -40,6 +40,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -3038,3 +3040,23 @@ ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable)
ath5k_hw_set_rx_filter(ah, rfilt);
ah->filter_flags = rfilt;
}
+
+void _ath5k_printk(const struct ath5k_hw *ah, const char *level,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (ah && ah->hw)
+ printk("%s" pr_fmt("%s: %pV"),
+ level, wiphy_name(ah->hw->wiphy), &vaf);
+ else
+ printk("%s" pr_fmt("%pV"), level, &vaf);
+
+ va_end(args);
+}
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
index e5e8f45d86a..9d00dab666a 100644
--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -57,6 +57,9 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/export.h>
#include <linux/moduleparam.h>
@@ -247,10 +250,10 @@ static ssize_t write_file_beacon(struct file *file,
if (strncmp(buf, "disable", 7) == 0) {
AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
- printk(KERN_INFO "debugfs disable beacons\n");
+ pr_info("debugfs disable beacons\n");
} else if (strncmp(buf, "enable", 6) == 0) {
AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
- printk(KERN_INFO "debugfs enable beacons\n");
+ pr_info("debugfs enable beacons\n");
}
return count;
}
@@ -450,19 +453,19 @@ static ssize_t write_file_antenna(struct file *file,
if (strncmp(buf, "diversity", 9) == 0) {
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
- printk(KERN_INFO "ath5k debug: enable diversity\n");
+ pr_info("debug: enable diversity\n");
} else if (strncmp(buf, "fixed-a", 7) == 0) {
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A);
- printk(KERN_INFO "ath5k debugfs: fixed antenna A\n");
+ pr_info("debug: fixed antenna A\n");
} else if (strncmp(buf, "fixed-b", 7) == 0) {
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B);
- printk(KERN_INFO "ath5k debug: fixed antenna B\n");
+ pr_info("debug: fixed antenna B\n");
} else if (strncmp(buf, "clear", 5) == 0) {
for (i = 0; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) {
ah->stats.antenna_rx[i] = 0;
ah->stats.antenna_tx[i] = 0;
}
- printk(KERN_INFO "ath5k debug: cleared antenna stats\n");
+ pr_info("debug: cleared antenna stats\n");
}
return count;
}
@@ -632,7 +635,7 @@ static ssize_t write_file_frameerrors(struct file *file,
st->txerr_fifo = 0;
st->txerr_filt = 0;
st->tx_all_count = 0;
- printk(KERN_INFO "ath5k debug: cleared frameerrors stats\n");
+ pr_info("debug: cleared frameerrors stats\n");
}
return count;
}
diff --git a/drivers/net/wireless/ath/ath5k/desc.c b/drivers/net/wireless/ath/ath5k/desc.c
index f8bfa3ac2af..bd8d4392d68 100644
--- a/drivers/net/wireless/ath/ath5k/desc.c
+++ b/drivers/net/wireless/ath/ath5k/desc.c
@@ -21,6 +21,8 @@
Hardware Descriptor Functions
\******************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
@@ -441,10 +443,8 @@ ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc,
struct ath5k_tx_status *ts)
{
- struct ath5k_hw_2w_tx_ctl *tx_ctl;
struct ath5k_hw_tx_status *tx_status;
- tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
tx_status = &desc->ud.ds_tx5210.tx_stat;
/* No frame has been send or error */
@@ -495,11 +495,9 @@ ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc,
struct ath5k_tx_status *ts)
{
- struct ath5k_hw_4w_tx_ctl *tx_ctl;
struct ath5k_hw_tx_status *tx_status;
u32 txstat0, txstat1;
- tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
tx_status = &desc->ud.ds_tx5212.tx_stat;
txstat1 = ACCESS_ONCE(tx_status->tx_status_1);
diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c
index 5cc9aa81469..ce86f158423 100644
--- a/drivers/net/wireless/ath/ath5k/dma.c
+++ b/drivers/net/wireless/ath/ath5k/dma.c
@@ -29,6 +29,8 @@
* status registers (ISR).
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c
index cd708c15b77..4026c906cc7 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath/ath5k/eeprom.c
@@ -21,6 +21,8 @@
* EEPROM access functions and helpers *
\*************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/slab.h>
#include "ath5k.h"
diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c
index a1ea78e05b4..ee1c2fa8b59 100644
--- a/drivers/net/wireless/ath/ath5k/initvals.c
+++ b/drivers/net/wireless/ath/ath5k/initvals.c
@@ -19,6 +19,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
@@ -1574,8 +1576,7 @@ ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool skip_pcu)
/* AR5K_MODE_11B */
if (mode > 2) {
- ATH5K_ERR(ah,
- "unsupported channel mode: %d\n", mode);
+ ATH5K_ERR(ah, "unsupported channel mode: %d\n", mode);
return -EINVAL;
}
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
index c1151c72371..b9f708a45f4 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -39,6 +39,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/pci.h>
#include "ath5k.h"
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 5c532995541..22b80af0f47 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -41,6 +41,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <net/mac80211.h>
#include <asm/unaligned.h>
diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c
index 849fa060ebc..53424e8e6d8 100644
--- a/drivers/net/wireless/ath/ath5k/pci.c
+++ b/drivers/net/wireless/ath/ath5k/pci.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/nl80211.h>
#include <linux/pci.h>
#include <linux/pci-aspm.h>
@@ -347,7 +349,7 @@ init_ath5k_pci(void)
ret = pci_register_driver(&ath5k_pci_driver);
if (ret) {
- printk(KERN_ERR "ath5k_pci: can't register pci driver\n");
+ pr_err("pci: can't register pci driver\n");
return ret;
}
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index 3a2845489a1..8b71a2d947e 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -22,6 +22,8 @@
* PHY related functions *
\***********************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index 30b50f93417..a6de200538c 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -20,6 +20,8 @@
Queue Control Unit, DCF Control Unit Functions
\********************************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
index 200f165c0c6..0c2dd4771c3 100644
--- a/drivers/net/wireless/ath/ath5k/reset.c
+++ b/drivers/net/wireless/ath/ath5k/reset.c
@@ -23,6 +23,8 @@
Reset function and helpers
\****************************/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <asm/unaligned.h>
#include <linux/pci.h> /* To determine if a card is pci-e */
diff --git a/drivers/net/wireless/ath/ath5k/sysfs.c b/drivers/net/wireless/ath/ath5k/sysfs.c
index 9364da7bd13..04cf0ca7261 100644
--- a/drivers/net/wireless/ath/ath5k/sysfs.c
+++ b/drivers/net/wireless/ath/ath5k/sysfs.c
@@ -1,3 +1,5 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/device.h>
#include <linux/pci.h>
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 00d38952b5f..bdcc68fb1e3 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -15,6 +15,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/moduleparam.h>
#include <linux/inetdevice.h>
#include <linux/export.h>
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index 03cae142f17..eb7cc2f5b96 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -16,6 +16,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/export.h>
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index 229e1922ebe..07071fce8a0 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -15,6 +15,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "core.h"
#include "hif-ops.h"
#include "cfg80211.h"
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
index f85353fd179..521f0be990f 100644
--- a/drivers/net/wireless/ath/ath6kl/txrx.c
+++ b/drivers/net/wireless/ath/ath6kl/txrx.c
@@ -15,6 +15,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "core.h"
#include "debug.h"
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 27d95fe5ade..3f0b8472378 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -11,7 +11,10 @@ ath9k-$(CONFIG_ATH9K_PCI) += pci.o
ath9k-$(CONFIG_ATH9K_AHB) += ahb.o
ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
-ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o
+ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \
+ dfs.o \
+ dfs_pattern_detector.o \
+ dfs_pri_detector.o
obj-$(CONFIG_ATH9K) += ath9k.o
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index 7e0ea4e9833..47a9fb4a116 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = {
{ 5, 4, 1 }, /* lvl 5 */
{ 6, 5, 1 }, /* lvl 6 */
{ 7, 6, 1 }, /* lvl 7 */
- { 7, 7, 1 }, /* lvl 8 */
- { 7, 8, 0 } /* lvl 9 */
+ { 7, 6, 0 }, /* lvl 8 */
+ { 7, 7, 0 } /* lvl 9 */
};
#define ATH9K_ANI_OFDM_NUM_LEVEL \
ARRAY_SIZE(ofdm_level_table)
@@ -91,8 +91,8 @@ static const struct ani_cck_level_entry cck_level_table[] = {
{ 4, 0 }, /* lvl 4 */
{ 5, 0 }, /* lvl 5 */
{ 6, 0 }, /* lvl 6 */
- { 7, 0 }, /* lvl 7 (only for high rssi) */
- { 8, 0 } /* lvl 8 (only for high rssi) */
+ { 6, 0 }, /* lvl 7 (only for high rssi) */
+ { 7, 0 } /* lvl 8 (only for high rssi) */
};
#define ATH9K_ANI_CCK_NUM_LEVEL \
@@ -290,16 +290,9 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel)
ATH9K_ANI_FIRSTEP_LEVEL,
entry_ofdm->fir_step_level);
- if ((ah->opmode != NL80211_IFTYPE_STATION &&
- ah->opmode != NL80211_IFTYPE_ADHOC) ||
- aniState->noiseFloor <= aniState->rssiThrHigh) {
- if (aniState->ofdmWeakSigDetectOff)
- /* force on ofdm weak sig detect */
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- true);
- else if (aniState->ofdmWeakSigDetectOff ==
- entry_ofdm->ofdm_weak_signal_on)
+ if ((aniState->noiseFloor >= aniState->rssiThrHigh) &&
+ (!aniState->ofdmWeakSigDetectOff !=
+ entry_ofdm->ofdm_weak_signal_on)) {
ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
entry_ofdm->ofdm_weak_signal_on);
@@ -717,26 +710,30 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
ofdmPhyErrRate, aniState->cckNoiseImmunityLevel,
cckPhyErrRate, aniState->ofdmsTurn);
- if (aniState->listenTime > 5 * ah->aniperiod) {
- if (ofdmPhyErrRate <= ah->config.ofdm_trig_low &&
- cckPhyErrRate <= ah->config.cck_trig_low) {
+ if (aniState->listenTime > ah->aniperiod) {
+ if (cckPhyErrRate < ah->config.cck_trig_low &&
+ ((ofdmPhyErrRate < ah->config.ofdm_trig_low &&
+ aniState->ofdmNoiseImmunityLevel <
+ ATH9K_ANI_OFDM_DEF_LEVEL) ||
+ (ofdmPhyErrRate < ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI &&
+ aniState->ofdmNoiseImmunityLevel >=
+ ATH9K_ANI_OFDM_DEF_LEVEL))) {
ath9k_hw_ani_lower_immunity(ah);
aniState->ofdmsTurn = !aniState->ofdmsTurn;
- }
- ath9k_ani_restart(ah);
- } else if (aniState->listenTime > ah->aniperiod) {
- /* check to see if need to raise immunity */
- if (ofdmPhyErrRate > ah->config.ofdm_trig_high &&
- (cckPhyErrRate <= ah->config.cck_trig_high ||
- aniState->ofdmsTurn)) {
+ } else if ((ofdmPhyErrRate > ah->config.ofdm_trig_high &&
+ aniState->ofdmNoiseImmunityLevel >=
+ ATH9K_ANI_OFDM_DEF_LEVEL) ||
+ (ofdmPhyErrRate >
+ ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI &&
+ aniState->ofdmNoiseImmunityLevel <
+ ATH9K_ANI_OFDM_DEF_LEVEL)) {
ath9k_hw_ani_ofdm_err_trigger(ah);
- ath9k_ani_restart(ah);
aniState->ofdmsTurn = false;
} else if (cckPhyErrRate > ah->config.cck_trig_high) {
ath9k_hw_ani_cck_err_trigger(ah);
- ath9k_ani_restart(ah);
aniState->ofdmsTurn = true;
}
+ ath9k_ani_restart(ah);
}
}
EXPORT_SYMBOL(ath9k_hw_ani_monitor);
diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
index 83029d6c7b2..72e2b874e17 100644
--- a/drivers/net/wireless/ath/ath9k/ani.h
+++ b/drivers/net/wireless/ath/ath9k/ani.h
@@ -25,11 +25,13 @@
/* units are errors per second */
#define ATH9K_ANI_OFDM_TRIG_HIGH_OLD 500
-#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW 1000
+#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW 3500
+#define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000
/* units are errors per second */
#define ATH9K_ANI_OFDM_TRIG_LOW_OLD 200
#define ATH9K_ANI_OFDM_TRIG_LOW_NEW 400
+#define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900
/* units are errors per second */
#define ATH9K_ANI_CCK_TRIG_HIGH_OLD 200
@@ -53,7 +55,7 @@
#define ATH9K_ANI_RSSI_THR_LOW 7
#define ATH9K_ANI_PERIOD_OLD 100
-#define ATH9K_ANI_PERIOD_NEW 1000
+#define ATH9K_ANI_PERIOD_NEW 300
/* in ms */
#define ATH9K_ANI_POLLINTERVAL_OLD 100
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index d7d8e919914..52ff5caf2d0 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -1047,46 +1047,8 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah,
break;
}
case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
- static const int m1ThreshLow[] = { 127, 50 };
- static const int m2ThreshLow[] = { 127, 40 };
- static const int m1Thresh[] = { 127, 0x4d };
- static const int m2Thresh[] = { 127, 0x40 };
- static const int m2CountThr[] = { 31, 16 };
- static const int m2CountThrLow[] = { 63, 48 };
u32 on = param ? 1 : 0;
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
- m1ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
- m2ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M1_THRESH,
- m1Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2_THRESH,
- m2Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2COUNT_THR,
- m2CountThr[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
- m2CountThrLow[on]);
-
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
- m1ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
- m2ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH,
- m1Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH,
- m2Thresh[on]);
-
if (on)
REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
index 46c79a3d473..952cb2b4656 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
@@ -777,11 +777,11 @@ static const u32 ar9300Common_rx_gain_table_2p2[][2] = {
{0x0000a074, 0x00000000},
{0x0000a078, 0x00000000},
{0x0000a07c, 0x00000000},
- {0x0000a080, 0x22222229},
- {0x0000a084, 0x1d1d1d1d},
- {0x0000a088, 0x1d1d1d1d},
- {0x0000a08c, 0x1d1d1d1d},
- {0x0000a090, 0x171d1d1d},
+ {0x0000a080, 0x1a1a1a1a},
+ {0x0000a084, 0x1a1a1a1a},
+ {0x0000a088, 0x1a1a1a1a},
+ {0x0000a08c, 0x1a1a1a1a},
+ {0x0000a090, 0x171a1a1a},
{0x0000a094, 0x11111717},
{0x0000a098, 0x00030311},
{0x0000a09c, 0x00000000},
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index bc992b237ae..79070bf04ea 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -823,55 +823,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
* on == 0 means more noise imm
*/
u32 on = param ? 1 : 0;
- /*
- * make register setting for default
- * (weak sig detect ON) come from INI file
- */
- int m1ThreshLow = on ?
- aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
- int m2ThreshLow = on ?
- aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
- int m1Thresh = on ?
- aniState->iniDef.m1Thresh : m1Thresh_off;
- int m2Thresh = on ?
- aniState->iniDef.m2Thresh : m2Thresh_off;
- int m2CountThr = on ?
- aniState->iniDef.m2CountThr : m2CountThr_off;
- int m2CountThrLow = on ?
- aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
- int m1ThreshLowExt = on ?
- aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
- int m2ThreshLowExt = on ?
- aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
- int m1ThreshExt = on ?
- aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
- int m2ThreshExt = on ?
- aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
-
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
- m1ThreshLow);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
- m2ThreshLow);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M1_THRESH, m1Thresh);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2_THRESH, m2Thresh);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2COUNT_THR, m2CountThr);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
- m2CountThrLow);
-
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt);
if (on)
REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 8c84049682a..0a37631390d 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -26,6 +26,7 @@
#include "debug.h"
#include "common.h"
#include "mci.h"
+#include "dfs.h"
/*
* Header for the ath9k.ko driver core *only* -- hw code nor any other driver
@@ -430,6 +431,8 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status);
void ath_reset_work(struct work_struct *work);
void ath_hw_check(struct work_struct *work);
void ath_hw_pll_work(struct work_struct *work);
+void ath_rx_poll(unsigned long data);
+void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon);
void ath_paprd_calibrate(struct work_struct *work);
void ath_ani_calibrate(unsigned long data);
void ath_start_ani(struct ath_common *common);
@@ -670,6 +673,7 @@ struct ath_softc {
struct ath_beacon_config cur_beacon_conf;
struct delayed_work tx_complete_work;
struct delayed_work hw_pll_work;
+ struct timer_list rx_poll_timer;
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
struct ath_btcoex btcoex;
@@ -680,6 +684,7 @@ struct ath_softc {
struct ath_ant_comb ant_comb;
u8 ant_tx, ant_rx;
+ struct dfs_pattern_detector *dfs_detector;
};
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 ff47b32ecaf..04edce941cb 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -524,6 +524,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PR("hw-put-tx-buf: ", puttxbuf);
PR("hw-tx-start: ", txstart);
PR("hw-tx-proc-desc: ", txprocdesc);
+ PR("TX-Failed: ", txfailed);
len += snprintf(buf + len, size - len,
"%s%11p%11p%10p%10p\n", "txq-memory-address:",
sc->tx.txq_map[WME_AC_BE],
@@ -910,6 +911,21 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
len += snprintf(buf + len, size - len,
"%22s : %10u\n", "DECRYPT BUSY ERR",
sc->debug.stats.rxstats.decrypt_busy_err);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-LENGTH-ERR",
+ sc->debug.stats.rxstats.rx_len_err);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-OOM-ERR",
+ sc->debug.stats.rxstats.rx_oom_err);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-RATE-ERR",
+ sc->debug.stats.rxstats.rx_rate_err);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-DROP-RXFLUSH",
+ sc->debug.stats.rxstats.rx_drop_rxflush);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-TOO-MANY-FRAGS",
+ sc->debug.stats.rxstats.rx_too_many_frags_err);
PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING);
@@ -944,6 +960,12 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
len += snprintf(buf + len, size - len,
"%22s : %10u\n", "RX-Bytes-All",
sc->debug.stats.rxstats.rx_bytes_all);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-Beacons",
+ sc->debug.stats.rxstats.rx_beacons);
+ len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-Frags",
+ sc->debug.stats.rxstats.rx_frags);
if (len > size)
len = size;
@@ -958,7 +980,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
{
-#define RX_STAT_INC(c) sc->debug.stats.rxstats.c++
#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\
[sc->debug.rsidx].c)
@@ -1004,7 +1025,6 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
#endif
-#undef RX_STAT_INC
#undef RX_PHY_ERR_INC
#undef RX_SAMP_DBG
}
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 64fcfad467b..17f6cc27af3 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -113,6 +113,7 @@ struct ath_interrupt_stats {
* @puttxbuf: Number of times hardware was given txbuf to write.
* @txstart: Number of times hardware was told to start tx.
* @txprocdesc: Number of times tx descriptor was processed
+ * @txfailed: Out-of-memory or other errors in xmit path.
*/
struct ath_tx_stats {
u32 tx_pkts_all;
@@ -135,8 +136,11 @@ struct ath_tx_stats {
u32 puttxbuf;
u32 txstart;
u32 txprocdesc;
+ u32 txfailed;
};
+#define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++)
+
/**
* struct ath_rx_stats - RX Statistics
* @rx_pkts_all: No. of total frames received, including ones that
@@ -153,6 +157,13 @@ struct ath_tx_stats {
* @post_delim_crc_err: Post-Frame delimiter CRC error detections
* @decrypt_busy_err: Decryption interruptions counter
* @phy_err_stats: Individual PHY error statistics
+ * @rx_len_err: No. of frames discarded due to bad length.
+ * @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.
*/
struct ath_rx_stats {
u32 rx_pkts_all;
@@ -165,6 +176,13 @@ struct ath_rx_stats {
u32 post_delim_crc_err;
u32 decrypt_busy_err;
u32 phy_err_stats[ATH9K_PHYERR_MAX];
+ u32 rx_len_err;
+ u32 rx_oom_err;
+ u32 rx_rate_err;
+ u32 rx_too_many_frags_err;
+ u32 rx_drop_rxflush;
+ u32 rx_beacons;
+ u32 rx_frags;
};
enum ath_reset_type {
@@ -174,6 +192,7 @@ enum ath_reset_type {
RESET_TYPE_TX_ERROR,
RESET_TYPE_TX_HANG,
RESET_TYPE_PLL_HANG,
+ RESET_TYPE_MAC_HANG,
__RESET_TYPE_MAX
};
@@ -247,6 +266,8 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs);
#else
+#define RX_STAT_INC(c) /* NOP */
+
static inline int ath9k_init_debug(struct ath_hw *ah)
{
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c
index f4f56aff1e9..92891f5fd45 100644
--- a/drivers/net/wireless/ath/ath9k/dfs.c
+++ b/drivers/net/wireless/ath/ath9k/dfs.c
@@ -21,17 +21,6 @@
#include "dfs.h"
#include "dfs_debug.h"
-/*
- * TODO: move into or synchronize this with generic header
- * as soon as IF is defined
- */
-struct dfs_radar_pulse {
- u16 freq;
- u64 ts;
- u32 width;
- u8 rssi;
-};
-
/* internal struct to pass radar data */
struct ath_radar_data {
u8 pulse_bw_info;
@@ -60,44 +49,44 @@ static u32 dur_to_usecs(struct ath_hw *ah, u32 dur)
#define EXT_CH_RADAR_FOUND 0x02
static bool
ath9k_postprocess_radar_event(struct ath_softc *sc,
- struct ath_radar_data *are,
- struct dfs_radar_pulse *drp)
+ struct ath_radar_data *ard,
+ struct pulse_event *pe)
{
u8 rssi;
u16 dur;
ath_dbg(ath9k_hw_common(sc->sc_ah), DFS,
"pulse_bw_info=0x%x, pri,ext len/rssi=(%u/%u, %u/%u)\n",
- are->pulse_bw_info,
- are->pulse_length_pri, are->rssi,
- are->pulse_length_ext, are->ext_rssi);
+ ard->pulse_bw_info,
+ ard->pulse_length_pri, ard->rssi,
+ ard->pulse_length_ext, ard->ext_rssi);
/*
* Only the last 2 bits of the BW info are relevant, they indicate
* which channel the radar was detected in.
*/
- are->pulse_bw_info &= 0x03;
+ ard->pulse_bw_info &= 0x03;
- switch (are->pulse_bw_info) {
+ switch (ard->pulse_bw_info) {
case PRI_CH_RADAR_FOUND:
/* radar in ctrl channel */
- dur = are->pulse_length_pri;
+ dur = ard->pulse_length_pri;
DFS_STAT_INC(sc, pri_phy_errors);
/*
* cannot use ctrl channel RSSI
* if extension channel is stronger
*/
- rssi = (are->ext_rssi >= (are->rssi + 3)) ? 0 : are->rssi;
+ rssi = (ard->ext_rssi >= (ard->rssi + 3)) ? 0 : ard->rssi;
break;
case EXT_CH_RADAR_FOUND:
/* radar in extension channel */
- dur = are->pulse_length_ext;
+ dur = ard->pulse_length_ext;
DFS_STAT_INC(sc, ext_phy_errors);
/*
* cannot use extension channel RSSI
* if control channel is stronger
*/
- rssi = (are->rssi >= (are->ext_rssi + 12)) ? 0 : are->ext_rssi;
+ rssi = (ard->rssi >= (ard->ext_rssi + 12)) ? 0 : ard->ext_rssi;
break;
case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND):
/*
@@ -107,14 +96,14 @@ ath9k_postprocess_radar_event(struct ath_softc *sc,
* Radiated testing, when pulse is on DC, different pri and
* ext durations are reported, so take the larger of the two
*/
- if (are->pulse_length_ext >= are->pulse_length_pri)
- dur = are->pulse_length_ext;
+ if (ard->pulse_length_ext >= ard->pulse_length_pri)
+ dur = ard->pulse_length_ext;
else
- dur = are->pulse_length_pri;
+ dur = ard->pulse_length_pri;
DFS_STAT_INC(sc, dc_phy_errors);
/* when both are present use stronger one */
- rssi = (are->rssi < are->ext_rssi) ? are->ext_rssi : are->rssi;
+ rssi = (ard->rssi < ard->ext_rssi) ? ard->ext_rssi : ard->rssi;
break;
default:
/*
@@ -137,8 +126,8 @@ ath9k_postprocess_radar_event(struct ath_softc *sc,
*/
/* convert duration to usecs */
- drp->width = dur_to_usecs(sc->sc_ah, dur);
- drp->rssi = rssi;
+ pe->width = dur_to_usecs(sc->sc_ah, dur);
+ pe->rssi = rssi;
DFS_STAT_INC(sc, pulses_detected);
return true;
@@ -155,12 +144,12 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
struct ath_radar_data ard;
u16 datalen;
char *vdata_end;
- struct dfs_radar_pulse drp;
+ struct pulse_event pe;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- if ((!(rs->rs_phyerr != ATH9K_PHYERR_RADAR)) &&
- (!(rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT))) {
+ if ((rs->rs_phyerr != ATH9K_PHYERR_RADAR) &&
+ (rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT)) {
ath_dbg(common, DFS,
"Error: rs_phyer=0x%x not a radar error\n",
rs->rs_phyerr);
@@ -189,27 +178,20 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
ard.pulse_bw_info = vdata_end[-1];
ard.pulse_length_ext = vdata_end[-2];
ard.pulse_length_pri = vdata_end[-3];
-
- ath_dbg(common, DFS,
- "bw_info=%d, length_pri=%d, length_ext=%d, "
- "rssi_pri=%d, rssi_ext=%d\n",
- ard.pulse_bw_info, ard.pulse_length_pri, ard.pulse_length_ext,
- ard.rssi, ard.ext_rssi);
-
- drp.freq = ah->curchan->channel;
- drp.ts = mactime;
- if (ath9k_postprocess_radar_event(sc, &ard, &drp)) {
+ pe.freq = ah->curchan->channel;
+ pe.ts = mactime;
+ if (ath9k_postprocess_radar_event(sc, &ard, &pe)) {
+ struct dfs_pattern_detector *pd = sc->dfs_detector;
static u64 last_ts;
ath_dbg(common, DFS,
"ath9k_dfs_process_phyerr: channel=%d, ts=%llu, "
"width=%d, rssi=%d, delta_ts=%llu\n",
- drp.freq, drp.ts, drp.width, drp.rssi, drp.ts-last_ts);
- last_ts = drp.ts;
- /*
- * TODO: forward pulse to pattern detector
- *
- * ieee80211_add_radar_pulse(drp.freq, drp.ts,
- * drp.width, drp.rssi);
- */
+ pe.freq, pe.ts, pe.width, pe.rssi, pe.ts-last_ts);
+ last_ts = pe.ts;
+ if (pd != NULL && pd->add_pulse(pd, &pe)) {
+ /*
+ * TODO: forward radar event to DFS management layer
+ */
+ }
}
}
diff --git a/drivers/net/wireless/ath/ath9k/dfs.h b/drivers/net/wireless/ath/ath9k/dfs.h
index c2412857f12..3c839f06a06 100644
--- a/drivers/net/wireless/ath/ath9k/dfs.h
+++ b/drivers/net/wireless/ath/ath9k/dfs.h
@@ -17,6 +17,7 @@
#ifndef ATH9K_DFS_H
#define ATH9K_DFS_H
+#include "dfs_pattern_detector.h"
#if defined(CONFIG_ATH9K_DFS_CERTIFIED)
/**
@@ -31,13 +32,14 @@
*
* The radar information provided as raw payload data is validated and
* filtered for false pulses. Events passing all tests are forwarded to
- * the upper layer for pattern detection.
+ * the DFS detector for pattern detection.
*/
void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
struct ath_rx_status *rs, u64 mactime);
#else
-static inline void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
- struct ath_rx_status *rs, u64 mactime) { }
+static inline void
+ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
+ struct ath_rx_status *rs, u64 mactime) { }
#endif
#endif /* ATH9K_DFS_H */
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c
new file mode 100644
index 00000000000..ea2a6cf7ef2
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * 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 <linux/slab.h>
+#include <linux/export.h>
+
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+
+/*
+ * tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE 16
+
+/**
+ * struct radar_types - contains array of patterns defined for one DFS domain
+ * @domain: DFS regulatory domain
+ * @num_radar_types: number of radar types to follow
+ * @radar_types: radar types array
+ */
+struct radar_types {
+ enum nl80211_dfs_regions region;
+ u32 num_radar_types;
+ const struct radar_detector_specs *radar_types;
+};
+
+/* percentage on ppb threshold to trigger detection */
+#define MIN_PPB_THRESH 50
+#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
+#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
+
+#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \
+{ \
+ ID, WMIN, WMAX, (PRF2PRI(PMAX) - PRI_TOLERANCE), \
+ (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, \
+}
+
+/* radar types as defined by ETSI EN-301-893 v1.5.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v15[] = {
+ ETSI_PATTERN(0, 0, 1, 700, 700, 1, 18),
+ ETSI_PATTERN(1, 0, 5, 200, 1000, 1, 10),
+ ETSI_PATTERN(2, 0, 15, 200, 1600, 1, 15),
+ ETSI_PATTERN(3, 0, 15, 2300, 4000, 1, 25),
+ ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20),
+ ETSI_PATTERN(5, 0, 2, 300, 400, 3, 10),
+ ETSI_PATTERN(6, 0, 2, 400, 1200, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v15 = {
+ .region = NL80211_DFS_ETSI,
+ .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v15),
+ .radar_types = etsi_radar_ref_types_v15,
+};
+
+/* for now, we support ETSI radar types, FCC and JP are TODO */
+static const struct radar_types *dfs_domains[] = {
+ &etsi_radar_types_v15,
+};
+
+/**
+ * get_dfs_domain_radar_types() - get radar types for a given DFS domain
+ * @param domain DFS domain
+ * @return radar_types ptr on success, NULL if DFS domain is not supported
+ */
+static const struct radar_types *
+get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
+{
+ u32 i;
+ for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+ if (dfs_domains[i]->region == region)
+ return dfs_domains[i];
+ }
+ return NULL;
+}
+
+/**
+ * struct channel_detector - detector elements for a DFS channel
+ * @head: list_head
+ * @freq: frequency for this channel detector in MHz
+ * @detectors: array of dynamically created detector elements for this freq
+ *
+ * Channel detectors are required to provide multi-channel DFS detection, e.g.
+ * to support off-channel scanning. A pattern detector has a list of channels
+ * radar pulses have been reported for in the past.
+ */
+struct channel_detector {
+ struct list_head head;
+ u16 freq;
+ struct pri_detector **detectors;
+};
+
+/* channel_detector_reset() - reset detector lines for a given channel */
+static void channel_detector_reset(struct dfs_pattern_detector *dpd,
+ struct channel_detector *cd)
+{
+ u32 i;
+ if (cd == NULL)
+ return;
+ for (i = 0; i < dpd->num_radar_types; i++)
+ cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts);
+}
+
+/* channel_detector_exit() - destructor */
+static void channel_detector_exit(struct dfs_pattern_detector *dpd,
+ struct channel_detector *cd)
+{
+ u32 i;
+ if (cd == NULL)
+ return;
+ list_del(&cd->head);
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ struct pri_detector *de = cd->detectors[i];
+ if (de != NULL)
+ de->exit(de);
+ }
+ kfree(cd->detectors);
+ kfree(cd);
+}
+
+static struct channel_detector *
+channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq)
+{
+ u32 sz, i;
+ struct channel_detector *cd;
+
+ cd = kmalloc(sizeof(*cd), GFP_KERNEL);
+ if (cd == NULL)
+ goto fail;
+
+ INIT_LIST_HEAD(&cd->head);
+ cd->freq = freq;
+ sz = sizeof(cd->detectors) * dpd->num_radar_types;
+ cd->detectors = kzalloc(sz, GFP_KERNEL);
+ if (cd->detectors == NULL)
+ goto fail;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+ struct pri_detector *de = pri_detector_init(rs);
+ if (de == NULL)
+ goto fail;
+ cd->detectors[i] = de;
+ }
+ list_add(&cd->head, &dpd->channel_detectors);
+ return cd;
+
+fail:
+ pr_err("failed to allocate channel_detector for freq=%d\n", freq);
+ channel_detector_exit(dpd, cd);
+ return NULL;
+}
+
+/**
+ * channel_detector_get() - get channel detector for given frequency
+ * @param dpd instance pointer
+ * @param freq frequency in MHz
+ * @return pointer to channel detector on success, NULL otherwise
+ *
+ * Return existing channel detector for the given frequency or return a
+ * newly create one.
+ */
+static struct channel_detector *
+channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq)
+{
+ struct channel_detector *cd;
+ list_for_each_entry(cd, &dpd->channel_detectors, head) {
+ if (cd->freq == freq)
+ return cd;
+ }
+ return channel_detector_create(dpd, freq);
+}
+
+/*
+ * DFS Pattern Detector
+ */
+
+/* dpd_reset(): reset all channel detectors */
+static void dpd_reset(struct dfs_pattern_detector *dpd)
+{
+ struct channel_detector *cd;
+ if (!list_empty(&dpd->channel_detectors))
+ list_for_each_entry(cd, &dpd->channel_detectors, head)
+ channel_detector_reset(dpd, cd);
+
+}
+static void dpd_exit(struct dfs_pattern_detector *dpd)
+{
+ struct channel_detector *cd, *cd0;
+ if (!list_empty(&dpd->channel_detectors))
+ list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+ channel_detector_exit(dpd, cd);
+ kfree(dpd);
+}
+
+static bool
+dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
+{
+ u32 i;
+ bool ts_wraparound;
+ struct channel_detector *cd;
+
+ if (dpd->region == NL80211_DFS_UNSET) {
+ /*
+ * pulses received for a non-supported or un-initialized
+ * domain are treated as detected radars
+ */
+ return true;
+ }
+
+ cd = channel_detector_get(dpd, event->freq);
+ if (cd == NULL)
+ return false;
+
+ ts_wraparound = (event->ts < dpd->last_pulse_ts);
+ dpd->last_pulse_ts = event->ts;
+ if (ts_wraparound) {
+ /*
+ * reset detector on time stamp wraparound
+ * with monotonic time stamps, this should never happen
+ */
+ pr_warn("DFS: time stamp wraparound detected, resetting\n");
+ dpd_reset(dpd);
+ }
+ /* do type individual pattern matching */
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (cd->detectors[i]->add_pulse(cd->detectors[i], event) != 0) {
+ channel_detector_reset(dpd, cd);
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool dpd_set_domain(struct dfs_pattern_detector *dpd,
+ enum nl80211_dfs_regions region)
+{
+ const struct radar_types *rt;
+ struct channel_detector *cd, *cd0;
+
+ if (dpd->region == region)
+ return true;
+
+ dpd->region = NL80211_DFS_UNSET;
+
+ rt = get_dfs_domain_radar_types(region);
+ if (rt == NULL)
+ return false;
+
+ /* delete all channel detectors for previous DFS domain */
+ if (!list_empty(&dpd->channel_detectors))
+ list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head)
+ channel_detector_exit(dpd, cd);
+ dpd->radar_spec = rt->radar_types;
+ dpd->num_radar_types = rt->num_radar_types;
+
+ dpd->region = region;
+ return true;
+}
+
+static struct dfs_pattern_detector default_dpd = {
+ .exit = dpd_exit,
+ .set_domain = dpd_set_domain,
+ .add_pulse = dpd_add_pulse,
+ .region = NL80211_DFS_UNSET,
+};
+
+struct dfs_pattern_detector *
+dfs_pattern_detector_init(enum nl80211_dfs_regions region)
+{
+ struct dfs_pattern_detector *dpd;
+ dpd = kmalloc(sizeof(*dpd), GFP_KERNEL);
+ if (dpd == NULL) {
+ pr_err("allocation of dfs_pattern_detector failed\n");
+ return NULL;
+ }
+ *dpd = default_dpd;
+ INIT_LIST_HEAD(&dpd->channel_detectors);
+
+ if (dpd->set_domain(dpd, region))
+ return dpd;
+
+ pr_err("Could not set DFS domain to %d. ", region);
+ return NULL;
+}
+EXPORT_SYMBOL(dfs_pattern_detector_init);
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h
new file mode 100644
index 00000000000..fd0328a3099
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * 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 DFS_PATTERN_DETECTOR_H
+#define DFS_PATTERN_DETECTOR_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/nl80211.h>
+
+/**
+ * struct pulse_event - describing pulses reported by PHY
+ * @ts: pulse time stamp in us
+ * @freq: channel frequency in MHz
+ * @width: pulse duration in us
+ * @rssi: rssi of radar event
+ */
+struct pulse_event {
+ u64 ts;
+ u16 freq;
+ u8 width;
+ u8 rssi;
+};
+
+/**
+ * struct radar_detector_specs - detector specs for a radar pattern type
+ * @type_id: pattern type, as defined by regulatory
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
+ */
+struct radar_detector_specs {
+ u8 type_id;
+ u8 width_min;
+ u8 width_max;
+ u16 pri_min;
+ u16 pri_max;
+ u8 num_pri;
+ u8 ppb;
+ u8 ppb_thresh;
+ u8 max_pri_tolerance;
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @exit(): destructor
+ * @set_domain(): set DFS domain, resets detector lines upon domain changes
+ * @add_pulse(): add radar pulse to detector, returns true on detection
+ * @region: active DFS region, NL80211_DFS_UNSET until set
+ * @num_radar_types: number of different radar types
+ * @last_pulse_ts: time stamp of last valid pulse in usecs
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+ void (*exit)(struct dfs_pattern_detector *dpd);
+ bool (*set_domain)(struct dfs_pattern_detector *dpd,
+ enum nl80211_dfs_regions region);
+ bool (*add_pulse)(struct dfs_pattern_detector *dpd,
+ struct pulse_event *pe);
+
+ enum nl80211_dfs_regions region;
+ u8 num_radar_types;
+ u64 last_pulse_ts;
+
+ const struct radar_detector_specs *radar_spec;
+ struct list_head channel_detectors;
+};
+
+/**
+ * dfs_pattern_detector_init() - constructor for pattern detector class
+ * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation
+ * @return instance pointer on success, NULL otherwise
+ */
+#if defined(CONFIG_ATH9K_DFS_CERTIFIED)
+extern struct dfs_pattern_detector *
+dfs_pattern_detector_init(enum nl80211_dfs_regions region);
+#else
+static inline struct dfs_pattern_detector *
+dfs_pattern_detector_init(enum nl80211_dfs_regions region)
+{
+ return NULL;
+}
+#endif /* CONFIG_ATH9K_DFS_CERTIFIED */
+
+#endif /* DFS_PATTERN_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c
new file mode 100644
index 00000000000..025e88a64fa
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * 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 <linux/slab.h>
+
+#include "dfs_pattern_detector.h"
+#include "dfs_pri_detector.h"
+
+/**
+ * struct pri_sequence - sequence of pulses matching one PRI
+ * @head: list_head
+ * @pri: pulse repetition interval (PRI) in usecs
+ * @dur: duration of sequence in usecs
+ * @count: number of pulses in this sequence
+ * @count_falses: number of not matching pulses in this sequence
+ * @first_ts: time stamp of first pulse in usecs
+ * @last_ts: time stamp of last pulse in usecs
+ * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
+ */
+struct pri_sequence {
+ struct list_head head;
+ u32 pri;
+ u32 dur;
+ u32 count;
+ u32 count_falses;
+ u64 first_ts;
+ u64 last_ts;
+ u64 deadline_ts;
+};
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+ struct list_head head;
+ u64 ts;
+};
+
+/**
+ * pde_get_multiple() - get number of multiples considering a given tolerance
+ * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
+ */
+static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
+{
+ u32 remainder;
+ u32 factor;
+ u32 delta;
+
+ if (fraction == 0)
+ return 0;
+
+ delta = (val < fraction) ? (fraction - val) : (val - fraction);
+
+ if (delta <= tolerance)
+ /* val and fraction are within tolerance */
+ return 1;
+
+ factor = val / fraction;
+ remainder = val % fraction;
+ if (remainder > tolerance) {
+ /* no exact match */
+ if ((fraction - remainder) <= tolerance)
+ /* remainder is within tolerance */
+ factor++;
+ else
+ factor = 0;
+ }
+ return factor;
+}
+
+/**
+ * DOC: Singleton Pulse and Sequence Pools
+ *
+ * Instances of pri_sequence and pulse_elem are kept in singleton pools to
+ * reduce the number of dynamic allocations. They are shared between all
+ * instances and grow up to the peak number of simultaneously used objects.
+ *
+ * Memory is freed after all references to the pools are released.
+ */
+static u32 singleton_pool_references;
+static LIST_HEAD(pulse_pool);
+static LIST_HEAD(pseq_pool);
+
+static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
+{
+ struct list_head *l = &pde->pulses;
+ if (list_empty(l))
+ return NULL;
+ return list_entry(l->prev, struct pulse_elem, head);
+}
+
+static bool pulse_queue_dequeue(struct pri_detector *pde)
+{
+ struct pulse_elem *p = pulse_queue_get_tail(pde);
+ if (p != NULL) {
+ list_del_init(&p->head);
+ pde->count--;
+ /* give it back to pool */
+ list_add(&p->head, &pulse_pool);
+ }
+ return (pde->count > 0);
+}
+
+/* remove pulses older than window */
+static void pulse_queue_check_window(struct pri_detector *pde)
+{
+ u64 min_valid_ts;
+ struct pulse_elem *p;
+
+ /* there is no delta time with less than 2 pulses */
+ if (pde->count < 2)
+ return;
+
+ if (pde->last_ts <= pde->window_size)
+ return;
+
+ min_valid_ts = pde->last_ts - pde->window_size;
+ while ((p = pulse_queue_get_tail(pde)) != NULL) {
+ if (p->ts >= min_valid_ts)
+ return;
+ pulse_queue_dequeue(pde);
+ }
+}
+
+static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
+{
+ struct pulse_elem *p;
+ if (!list_empty(&pulse_pool)) {
+ p = list_first_entry(&pulse_pool, struct pulse_elem, head);
+ list_del(&p->head);
+ } else {
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ pr_err("failed to allocate pulse_elem\n");
+ return false;
+ }
+ }
+ INIT_LIST_HEAD(&p->head);
+ p->ts = ts;
+ list_add(&p->head, &pde->pulses);
+ pde->count++;
+ pde->last_ts = ts;
+ pulse_queue_check_window(pde);
+ if (pde->count >= pde->max_count)
+ pulse_queue_dequeue(pde);
+ return true;
+}
+
+static bool pseq_handler_create_sequences(struct pri_detector *pde,
+ u64 ts, u32 min_count)
+{
+ struct pulse_elem *p;
+ list_for_each_entry(p, &pde->pulses, head) {
+ struct pri_sequence ps, *new_ps;
+ struct pulse_elem *p2;
+ u32 tmp_false_count;
+ u64 min_valid_ts;
+ u32 delta_ts = ts - p->ts;
+
+ if (delta_ts < pde->rs->pri_min)
+ /* ignore too small pri */
+ continue;
+
+ if (delta_ts > pde->rs->pri_max)
+ /* stop on too large pri (sorted list) */
+ break;
+
+ /* build a new sequence with new potential pri */
+ ps.count = 2;
+ ps.count_falses = 0;
+ ps.first_ts = p->ts;
+ ps.last_ts = ts;
+ ps.pri = ts - p->ts;
+ ps.dur = ps.pri * (pde->rs->ppb - 1)
+ + 2 * pde->rs->max_pri_tolerance;
+
+ p2 = p;
+ tmp_false_count = 0;
+ min_valid_ts = ts - ps.dur;
+ /* check which past pulses are candidates for new sequence */
+ list_for_each_entry_continue(p2, &pde->pulses, head) {
+ u32 factor;
+ if (p2->ts < min_valid_ts)
+ /* stop on crossing window border */
+ break;
+ /* check if pulse match (multi)PRI */
+ factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
+ pde->rs->max_pri_tolerance);
+ if (factor > 0) {
+ ps.count++;
+ ps.first_ts = p2->ts;
+ /*
+ * on match, add the intermediate falses
+ * and reset counter
+ */
+ ps.count_falses += tmp_false_count;
+ tmp_false_count = 0;
+ } else {
+ /* this is a potential false one */
+ tmp_false_count++;
+ }
+ }
+ if (ps.count < min_count)
+ /* did not reach minimum count, drop sequence */
+ continue;
+
+ /* this is a valid one, add it */
+ ps.deadline_ts = ps.first_ts + ps.dur;
+
+ if (!list_empty(&pseq_pool)) {
+ new_ps = list_first_entry(&pseq_pool,
+ struct pri_sequence, head);
+ list_del(&new_ps->head);
+ } else {
+ new_ps = kmalloc(sizeof(*new_ps), GFP_KERNEL);
+ if (new_ps == NULL)
+ return false;
+ }
+ memcpy(new_ps, &ps, sizeof(ps));
+ INIT_LIST_HEAD(&new_ps->head);
+ list_add(&new_ps->head, &pde->sequences);
+ }
+ return true;
+}
+
+/* check new ts and add to all matching existing sequences */
+static u32
+pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
+{
+ u32 max_count = 0;
+ struct pri_sequence *ps, *ps2;
+ list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
+ u32 delta_ts;
+ u32 factor;
+
+ /* first ensure that sequence is within window */
+ if (ts > ps->deadline_ts) {
+ list_del_init(&ps->head);
+ list_add(&ps->head, &pseq_pool);
+ continue;
+ }
+
+ delta_ts = ts - ps->last_ts;
+ factor = pde_get_multiple(delta_ts, ps->pri,
+ pde->rs->max_pri_tolerance);
+ if (factor > 0) {
+ ps->last_ts = ts;
+ ps->count++;
+
+ if (max_count < ps->count)
+ max_count = ps->count;
+ } else {
+ ps->count_falses++;
+ }
+ }
+ return max_count;
+}
+
+static struct pri_sequence *
+pseq_handler_check_detection(struct pri_detector *pde)
+{
+ struct pri_sequence *ps;
+
+ if (list_empty(&pde->sequences))
+ return NULL;
+
+ list_for_each_entry(ps, &pde->sequences, head) {
+ /*
+ * we assume to have enough matching confidence if we
+ * 1) have enough pulses
+ * 2) have more matching than false pulses
+ */
+ if ((ps->count >= pde->rs->ppb_thresh) &&
+ (ps->count * pde->rs->num_pri >= ps->count_falses))
+ return ps;
+ }
+ return NULL;
+}
+
+
+/* free pulse queue and sequences list and give objects back to pools */
+static void pri_detector_reset(struct pri_detector *pde, u64 ts)
+{
+ struct pri_sequence *ps, *ps0;
+ struct pulse_elem *p, *p0;
+ list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
+ list_del_init(&ps->head);
+ list_add(&ps->head, &pseq_pool);
+ }
+ list_for_each_entry_safe(p, p0, &pde->pulses, head) {
+ list_del_init(&p->head);
+ list_add(&p->head, &pulse_pool);
+ }
+ pde->count = 0;
+ pde->last_ts = ts;
+}
+
+static void pri_detector_exit(struct pri_detector *de)
+{
+ pri_detector_reset(de, 0);
+
+ singleton_pool_references--;
+ if (singleton_pool_references == 0) {
+ /* free singleton pools with no references left */
+ struct pri_sequence *ps, *ps0;
+ struct pulse_elem *p, *p0;
+
+ list_for_each_entry_safe(p, p0, &pulse_pool, head) {
+ list_del(&p->head);
+ kfree(p);
+ }
+ list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+ list_del(&ps->head);
+ kfree(ps);
+ }
+ }
+ kfree(de);
+}
+
+static bool pri_detector_add_pulse(struct pri_detector *de,
+ struct pulse_event *event)
+{
+ u32 max_updated_seq;
+ struct pri_sequence *ps;
+ u64 ts = event->ts;
+ const struct radar_detector_specs *rs = de->rs;
+
+ /* ignore pulses not within width range */
+ if ((rs->width_min > event->width) || (rs->width_max < event->width))
+ return false;
+
+ if ((ts - de->last_ts) < rs->max_pri_tolerance)
+ /* if delta to last pulse is too short, don't use this pulse */
+ return false;
+ de->last_ts = ts;
+
+ max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
+
+ if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
+ pr_err("failed to create pulse sequences\n");
+ pri_detector_reset(de, ts);
+ return false;
+ }
+
+ ps = pseq_handler_check_detection(de);
+
+ if (ps != NULL) {
+ pr_info("DFS: radar found: pri=%d, count=%d, count_false=%d\n",
+ ps->pri, ps->count, ps->count_falses);
+ pri_detector_reset(de, ts);
+ return true;
+ }
+ pulse_queue_enqueue(de, ts);
+ return false;
+}
+
+struct pri_detector *
+pri_detector_init(const struct radar_detector_specs *rs)
+{
+ struct pri_detector *de;
+ de = kzalloc(sizeof(*de), GFP_KERNEL);
+ if (de == NULL)
+ return NULL;
+ de->exit = pri_detector_exit;
+ de->add_pulse = pri_detector_add_pulse;
+ de->reset = pri_detector_reset;
+
+ INIT_LIST_HEAD(&de->sequences);
+ INIT_LIST_HEAD(&de->pulses);
+ de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
+ de->max_count = rs->ppb * 2;
+ de->rs = rs;
+
+ singleton_pool_references++;
+ return de;
+}
diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h
new file mode 100644
index 00000000000..81cde9f28e4
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012 Neratec Solutions AG
+ *
+ * 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 DFS_PRI_DETECTOR_H
+#define DFS_PRI_DETECTOR_H
+
+#include <linux/list.h>
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @exit(): destructor
+ * @add_pulse(): add pulse event, returns true if pattern was detected
+ * @reset(): clear states and reset to given time stamp
+ * @rs: detector specs for this detector element
+ * @last_ts: last pulse time stamp considered for this element in usecs
+ * @sequences: list_head holding potential pulse sequences
+ * @pulses: list connecting pulse_elem objects
+ * @count: number of pulses in queue
+ * @max_count: maximum number of pulses to be queued
+ * @window_size: window size back from newest pulse time stamp in usecs
+ */
+struct pri_detector {
+ void (*exit) (struct pri_detector *de);
+ bool (*add_pulse)(struct pri_detector *de, struct pulse_event *e);
+ void (*reset) (struct pri_detector *de, u64 ts);
+
+/* private: internal use only */
+ const struct radar_detector_specs *rs;
+ u64 last_ts;
+ struct list_head sequences;
+ struct list_head pulses;
+ u32 count;
+ u32 max_count;
+ u32 window_size;
+};
+
+struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs);
+
+#endif /* DFS_PRI_DETECTOR_H */
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index de5ee15ee63..25213d521bc 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "htc.h"
MODULE_AUTHOR("Atheros Communications");
@@ -711,7 +713,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
- hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
hw->queues = 4;
hw->channel_change_time = 5000;
@@ -966,9 +969,7 @@ int ath9k_htc_resume(struct htc_target *htc_handle)
static int __init ath9k_htc_init(void)
{
if (ath9k_hif_usb_init() < 0) {
- printk(KERN_ERR
- "ath9k_htc: No USB devices found,"
- " driver not installed.\n");
+ pr_err("No USB devices found, driver not installed\n");
return -ENODEV;
}
@@ -979,6 +980,6 @@ module_init(ath9k_htc_init);
static void __exit ath9k_htc_exit(void)
{
ath9k_hif_usb_exit();
- printk(KERN_INFO "ath9k_htc: Driver unloaded\n");
+ pr_info("Driver unloaded\n");
}
module_exit(ath9k_htc_exit);
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index c25226a32dd..4a9570dfba7 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "htc.h"
static int htc_issue_send(struct htc_target *target, struct sk_buff* skb,
@@ -461,7 +463,7 @@ int ath9k_htc_hw_init(struct htc_target *target,
char *product, u32 drv_info)
{
if (ath9k_htc_probe_device(target, dev, devid, product, drv_info)) {
- printk(KERN_ERR "Failed to initialize the device\n");
+ pr_err("Failed to initialize the device\n");
return -ENODEV;
}
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 6c69e4e8b1c..d1345a8a2b1 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1491,11 +1491,84 @@ static void ath9k_hw_apply_gpio_override(struct ath_hw *ah)
}
}
+static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states,
+ int *hang_state, int *hang_pos)
+{
+ static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */
+ u32 chain_state, dcs_pos, i;
+
+ for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) {
+ chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f;
+ for (i = 0; i < 3; i++) {
+ if (chain_state == dcu_chain_state[i]) {
+ *hang_state = chain_state;
+ *hang_pos = dcs_pos;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#define DCU_COMPLETE_STATE 1
+#define DCU_COMPLETE_STATE_MASK 0x3
+#define NUM_STATUS_READS 50
+static bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
+{
+ u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4;
+ u32 i, hang_pos, hang_state, num_state = 6;
+
+ comp_state = REG_READ(ah, AR_DMADBG_6);
+
+ if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) {
+ ath_dbg(ath9k_hw_common(ah), RESET,
+ "MAC Hang signature not found at DCU complete\n");
+ return false;
+ }
+
+ chain_state = REG_READ(ah, dcs_reg);
+ if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
+ goto hang_check_iter;
+
+ dcs_reg = AR_DMADBG_5;
+ num_state = 4;
+ chain_state = REG_READ(ah, dcs_reg);
+ if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
+ goto hang_check_iter;
+
+ ath_dbg(ath9k_hw_common(ah), RESET,
+ "MAC Hang signature 1 not found\n");
+ return false;
+
+hang_check_iter:
+ ath_dbg(ath9k_hw_common(ah), RESET,
+ "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n",
+ chain_state, comp_state, hang_state, hang_pos);
+
+ for (i = 0; i < NUM_STATUS_READS; i++) {
+ chain_state = REG_READ(ah, dcs_reg);
+ chain_state = (chain_state >> (5 * hang_pos)) & 0x1f;
+ comp_state = REG_READ(ah, AR_DMADBG_6);
+
+ if (((comp_state & DCU_COMPLETE_STATE_MASK) !=
+ DCU_COMPLETE_STATE) ||
+ (chain_state != hang_state))
+ return false;
+ }
+
+ ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n");
+
+ return true;
+}
+
bool ath9k_hw_check_alive(struct ath_hw *ah)
{
int count = 50;
u32 reg;
+ if (AR_SREV_9300(ah))
+ return !ath9k_hw_detect_mac_hang(ah);
+
if (AR_SREV_9285_12_OR_LATER(ah))
return true;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index cb006458fc4..7a6b9f69a7b 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/ath9k_platform.h>
@@ -519,6 +521,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
atomic_set(&ah->intr_ref_cnt, -1);
sc->sc_ah = ah;
+ sc->dfs_detector = dfs_pattern_detector_init(NL80211_DFS_UNSET);
+
if (!pdata) {
ah->ah_flags |= AH_USE_EEPROM;
sc->sc_ah->led_pin = -1;
@@ -676,6 +680,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+ hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
hw->queues = 4;
hw->max_rates = 4;
@@ -779,6 +784,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
goto error_world;
}
+ setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc);
sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
ath_init_leds(sc);
@@ -821,6 +827,8 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
ath9k_hw_deinit(sc->sc_ah);
+ if (sc->dfs_detector != NULL)
+ sc->dfs_detector->exit(sc->dfs_detector);
kfree(sc->sc_ah);
sc->sc_ah = NULL;
@@ -866,17 +874,14 @@ static int __init ath9k_init(void)
/* Register rate control algorithm */
error = ath_rate_control_register();
if (error != 0) {
- printk(KERN_ERR
- "ath9k: Unable to register rate control "
- "algorithm: %d\n",
- error);
+ pr_err("Unable to register rate control algorithm: %d\n",
+ error);
goto err_out;
}
error = ath_pci_init();
if (error < 0) {
- printk(KERN_ERR
- "ath9k: No PCI devices found, driver not installed.\n");
+ pr_err("No PCI devices found, driver not installed\n");
error = -ENODEV;
goto err_rate_unregister;
}
@@ -905,6 +910,6 @@ static void __exit ath9k_exit(void)
ath_ahb_exit();
ath_pci_exit();
ath_rate_control_unregister();
- printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
+ pr_info("%s: Driver unloaded\n", dev_info);
}
module_exit(ath9k_exit);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 2504ab00558..c8d12395718 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -241,6 +241,7 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
sc->hw_busy_count = 0;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
ath9k_debug_samp_bb_mac(sc);
ath9k_hw_disable_interrupts(ah);
@@ -282,6 +283,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
+ ath_start_rx_poll(sc, 3);
if (!common->disable_ani)
ath_start_ani(common);
}
@@ -912,10 +914,19 @@ void ath_hw_check(struct work_struct *work)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
unsigned long flags;
int busy;
+ u8 is_alive, nbeacon = 1;
ath9k_ps_wakeup(sc);
- if (ath9k_hw_check_alive(sc->sc_ah))
+ is_alive = ath9k_hw_check_alive(sc->sc_ah);
+
+ if (is_alive && !AR_SREV_9300(sc->sc_ah))
goto out;
+ else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
+ ath_dbg(common, RESET,
+ "DCU stuck is detected. Schedule chip reset\n");
+ RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG);
+ goto sched_reset;
+ }
spin_lock_irqsave(&common->cc_lock, flags);
busy = ath_update_survey_stats(sc);
@@ -926,12 +937,18 @@ void ath_hw_check(struct work_struct *work)
if (busy >= 99) {
if (++sc->hw_busy_count >= 3) {
RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
- ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ goto sched_reset;
}
-
- } else if (busy >= 0)
+ } else if (busy >= 0) {
sc->hw_busy_count = 0;
+ nbeacon = 3;
+ }
+ ath_start_rx_poll(sc, nbeacon);
+ goto out;
+
+sched_reset:
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
out:
ath9k_ps_restore(sc);
}
@@ -1133,6 +1150,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if (ath_tx_start(hw, skb, &txctl) != 0) {
ath_dbg(common, XMIT, "TX failed\n");
+ TX_STAT_INC(txctl.txq->axq_qnum, txfailed);
goto exit;
}
@@ -1151,6 +1169,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
+ del_timer_sync(&sc->rx_poll_timer);
if (sc->sc_flags & SC_OP_INVALID) {
ath_dbg(common, ANY, "Device not present\n");
@@ -1383,6 +1402,24 @@ static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw,
}
}
+void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
+{
+ if (!AR_SREV_9300(sc->sc_ah))
+ return;
+
+ if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF))
+ return;
+
+ mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
+ (nbeacon * sc->cur_beacon_conf.beacon_interval));
+}
+
+void ath_rx_poll(unsigned long data)
+{
+ struct ath_softc *sc = (struct ath_softc *)data;
+
+ ieee80211_queue_work(sc->hw, &sc->hw_check_work);
+}
static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
@@ -1904,6 +1941,8 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
+ ath_start_rx_poll(sc, 3);
+
if (!common->disable_ani) {
sc->sc_flags |= SC_OP_ANI_RUN;
ath_start_ani(common);
@@ -1943,6 +1982,7 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
/* Stop ANI */
sc->sc_flags &= ~SC_OP_ANI_RUN;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
memset(&sc->caldata, 0, sizeof(sc->caldata));
}
}
@@ -1986,6 +2026,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
} else {
sc->sc_flags &= ~SC_OP_ANI_RUN;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
}
}
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 77dc327def8..a856b51255f 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/nl80211.h>
#include <linux/pci.h>
#include <linux/pci-aspm.h>
@@ -171,14 +173,13 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (ret) {
- printk(KERN_ERR "ath9k: 32-bit DMA not available\n");
+ pr_err("32-bit DMA not available\n");
goto err_dma;
}
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (ret) {
- printk(KERN_ERR "ath9k: 32-bit DMA consistent "
- "DMA enable failed\n");
+ pr_err("32-bit DMA consistent DMA enable failed\n");
goto err_dma;
}
@@ -224,7 +225,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mem = pci_iomap(pdev, 0, 0);
if (!mem) {
- printk(KERN_ERR "PCI memory map error\n") ;
+ pr_err("PCI memory map error\n") ;
ret = -EIO;
goto err_iomap;
}
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index 08bb4553270..92a6c0a87f8 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -1436,7 +1436,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
- u32 changed, enum nl80211_channel_type oper_chan_type)
+ u32 changed)
{
struct ath_softc *sc = priv;
struct ath_rate_priv *ath_rc_priv = priv_sta;
@@ -1447,12 +1447,11 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
/* FIXME: Handle AP mode later when we support CWM */
- if (changed & IEEE80211_RC_HT_CHANGED) {
+ if (changed & IEEE80211_RC_BW_CHANGED) {
if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
return;
- if (oper_chan_type == NL80211_CHAN_HT40MINUS ||
- oper_chan_type == NL80211_CHAN_HT40PLUS)
+ if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
oper_cw40 = true;
if (oper_cw40)
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 1c4583c7ff7..301ef3e5714 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -824,15 +824,20 @@ static bool ath9k_rx_accept(struct ath_common *common,
if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID)
rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS;
- if (!rx_stats->rs_datalen)
+ if (!rx_stats->rs_datalen) {
+ RX_STAT_INC(rx_len_err);
return false;
+ }
+
/*
* rs_status follows rs_datalen so if rs_datalen is too large
* we can take a hint that hardware corrupted it, so ignore
* those frames.
*/
- if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len))
+ if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len)) {
+ RX_STAT_INC(rx_len_err);
return false;
+ }
/* Only use error bits from the last fragment */
if (rx_stats->rs_more)
@@ -902,6 +907,7 @@ static int ath9k_process_rate(struct ath_common *common,
struct ieee80211_supported_band *sband;
enum ieee80211_band band;
unsigned int i = 0;
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
band = hw->conf.channel->band;
sband = hw->wiphy->bands[band];
@@ -936,7 +942,7 @@ static int ath9k_process_rate(struct ath_common *common,
ath_dbg(common, ANY,
"unsupported hw bitrate detected 0x%02x using 1 Mbit\n",
rx_stats->rs_rate);
-
+ RX_STAT_INC(rx_rate_err);
return -EINVAL;
}
@@ -1823,10 +1829,14 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len);
rxs = IEEE80211_SKB_RXCB(hdr_skb);
- if (ieee80211_is_beacon(hdr->frame_control) &&
- !is_zero_ether_addr(common->curbssid) &&
- !compare_ether_addr(hdr->addr3, common->curbssid))
- rs.is_mybeacon = true;
+ if (ieee80211_is_beacon(hdr->frame_control)) {
+ RX_STAT_INC(rx_beacons);
+ if (!is_zero_ether_addr(common->curbssid) &&
+ !compare_ether_addr(hdr->addr3, common->curbssid))
+ rs.is_mybeacon = true;
+ else
+ rs.is_mybeacon = false;
+ }
else
rs.is_mybeacon = false;
@@ -1836,8 +1846,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
* If we're asked to flush receive queue, directly
* chain it back at the queue without processing it.
*/
- if (sc->sc_flags & SC_OP_RXFLUSH)
+ if (sc->sc_flags & SC_OP_RXFLUSH) {
+ RX_STAT_INC(rx_drop_rxflush);
goto requeue_drop_frag;
+ }
memset(rxs, 0, sizeof(struct ieee80211_rx_status));
@@ -1855,6 +1867,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
if (retval)
goto requeue_drop_frag;
+ if (rs.is_mybeacon) {
+ sc->hw_busy_count = 0;
+ ath_start_rx_poll(sc, 3);
+ }
/* Ensure we always have an skb to requeue once we are done
* processing the current buffer's skb */
requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);
@@ -1863,8 +1879,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
* tell hardware it can give us a new frame using the old
* skb and put it at the tail of the sc->rx.rxbuf list for
* processing. */
- if (!requeue_skb)
+ if (!requeue_skb) {
+ RX_STAT_INC(rx_oom_err);
goto requeue_drop_frag;
+ }
/* Unmap the frame */
dma_unmap_single(sc->dev, bf->bf_buf_addr,
@@ -1895,6 +1913,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
}
if (rs.rs_more) {
+ RX_STAT_INC(rx_frags);
/*
* rs_more indicates chained descriptors which can be
* used to link buffers together for a sort of
@@ -1904,6 +1923,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
/* too many fragments - cannot handle frame */
dev_kfree_skb_any(sc->rx.frag);
dev_kfree_skb_any(skb);
+ RX_STAT_INC(rx_too_many_frags_err);
skb = NULL;
}
sc->rx.frag = skb;
@@ -1915,6 +1935,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
if (pskb_expand_head(hdr_skb, 0, space, GFP_ATOMIC) < 0) {
dev_kfree_skb(skb);
+ RX_STAT_INC(rx_oom_err);
goto requeue_drop_frag;
}
diff --git a/drivers/net/wireless/ath/carl9170/cmd.h b/drivers/net/wireless/ath/carl9170/cmd.h
index 885c42778b8..65919c902f5 100644
--- a/drivers/net/wireless/ath/carl9170/cmd.h
+++ b/drivers/net/wireless/ath/carl9170/cmd.h
@@ -114,7 +114,7 @@ __regwrite_out : \
#define carl9170_regwrite_result() \
__err; \
-} while (0);
+} while (0)
#define carl9170_async_regwrite_get_buf() \
@@ -126,7 +126,7 @@ do { \
__err = -ENOMEM; \
goto __async_regwrite_out; \
} \
-} while (0);
+} while (0)
#define carl9170_async_regwrite_begin(carl) \
do { \
@@ -169,6 +169,6 @@ __async_regwrite_out: \
#define carl9170_async_regwrite_result() \
__err; \
-} while (0);
+} while (0)
#endif /* __CMD_H */
diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c
index cffde8d9a52..5c73c03872f 100644
--- a/drivers/net/wireless/ath/carl9170/fw.c
+++ b/drivers/net/wireless/ath/carl9170/fw.c
@@ -355,6 +355,8 @@ 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;
+
#undef SUPPORTED
return carl9170_fw_tx_sequence(ar);
}
diff --git a/drivers/net/wireless/ath/main.c b/drivers/net/wireless/ath/main.c
index ea2c737138d..8e99540cd90 100644
--- a/drivers/net/wireless/ath/main.c
+++ b/drivers/net/wireless/ath/main.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
@@ -49,7 +51,7 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
if (off != 0)
skb_reserve(skb, common->cachelsz - off);
} else {
- printk(KERN_ERR "skbuff alloc of size %u failed\n", len);
+ pr_err("skbuff alloc of size %u failed\n", len);
return NULL;
}
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index 10dea37431b..d81698015bf 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/export.h>
#include <net/cfg80211.h>
@@ -562,7 +564,7 @@ static int __ath_regd_init(struct ath_regulatory *reg)
printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd);
if (!ath_regd_is_eeprom_valid(reg)) {
- printk(KERN_ERR "ath: Invalid EEPROM contents\n");
+ pr_err("Invalid EEPROM contents\n");
return -EINVAL;
}
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index c79e6638c88..05ea95ba6de 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4010,6 +4010,20 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
if (modparam_nohwcrypt)
return -ENOSPC; /* User disabled HW-crypto */
+ if ((vif->type == NL80211_IFTYPE_ADHOC ||
+ vif->type == NL80211_IFTYPE_MESH_POINT) &&
+ (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
+ key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ /*
+ * For now, disable hw crypto for the RSN IBSS group keys. This
+ * could be optimized in the future, but until that gets
+ * implemented, use of software crypto for group addressed
+ * frames is a acceptable to allow RSN IBSS to be used.
+ */
+ return -EOPNOTSUPP;
+ }
+
mutex_lock(&wl->mutex);
dev = wl->current_dev;
@@ -5275,6 +5289,8 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
BIT(NL80211_IFTYPE_WDS) |
BIT(NL80211_IFTYPE_ADHOC);
+ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
hw->queues = modparam_qos ? B43_QOS_QUEUE_NUM : 1;
wl->mac80211_initially_registered_queues = hw->queues;
hw->max_rates = 2;
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index 2c5367884b3..cba41353627 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -378,7 +378,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
phy_ctl |= B43_TXH_PHY_SHORTPRMBL;
- switch (b43_ieee80211_antenna_sanitize(dev, info->antenna_sel_tx)) {
+ switch (b43_ieee80211_antenna_sanitize(dev, 0)) {
case 0: /* Default */
phy_ctl |= B43_TXH_PHY_ANT01AUTO;
break;
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c
index 5188fab0b37..e6c573af494 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/b43legacy/xmit.c
@@ -277,19 +277,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
phy_ctl |= B43legacy_TX4_PHY_ENC_OFDM;
if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
phy_ctl |= B43legacy_TX4_PHY_SHORTPRMBL;
- switch (info->antenna_sel_tx) {
- case 0:
- phy_ctl |= B43legacy_TX4_PHY_ANTLAST;
- break;
- case 1:
- phy_ctl |= B43legacy_TX4_PHY_ANT0;
- break;
- case 2:
- phy_ctl |= B43legacy_TX4_PHY_ANT1;
- break;
- default:
- B43legacy_BUG_ON(1);
- }
+ phy_ctl |= B43legacy_TX4_PHY_ANTLAST;
/* MAC control */
rates = info->control.rates;
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/d11.h b/drivers/net/wireless/brcm80211/brcmsmac/d11.h
index 1948cb2771e..3f659e09f1c 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/d11.h
+++ b/drivers/net/wireless/brcm80211/brcmsmac/d11.h
@@ -733,7 +733,7 @@ struct cck_phy_hdr {
do { \
plcp[1] = len & 0xff; \
plcp[2] = ((len >> 8) & 0xff); \
- } while (0);
+ } while (0)
#define BRCMS_SET_MIMO_PLCP_AMPDU(plcp) (plcp[3] |= MIMO_PLCP_AMPDU)
#define BRCMS_CLR_MIMO_PLCP_AMPDU(plcp) (plcp[3] &= ~MIMO_PLCP_AMPDU)
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.h b/drivers/net/wireless/ipw2x00/ipw2100.h
index e5b1c77ae0e..97312524249 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.h
+++ b/drivers/net/wireless/ipw2x00/ipw2100.h
@@ -135,15 +135,6 @@ enum {
IPW_HW_STATE_ENABLED = 0
};
-struct ssid_context {
- char ssid[IW_ESSID_MAX_SIZE + 1];
- int ssid_len;
- unsigned char bssid[ETH_ALEN];
- int port_type;
- int channel;
-
-};
-
extern const char *port_type_str[];
extern const char *band_str[];
diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h
index 8874588fb92..0b22fb42173 100644
--- a/drivers/net/wireless/ipw2x00/libipw.h
+++ b/drivers/net/wireless/ipw2x00/libipw.h
@@ -584,61 +584,6 @@ struct libipw_tim_parameters {
/*******************************************************/
-enum { /* libipw_basic_report.map */
- LIBIPW_BASIC_MAP_BSS = (1 << 0),
- LIBIPW_BASIC_MAP_OFDM = (1 << 1),
- LIBIPW_BASIC_MAP_UNIDENTIFIED = (1 << 2),
- LIBIPW_BASIC_MAP_RADAR = (1 << 3),
- LIBIPW_BASIC_MAP_UNMEASURED = (1 << 4),
- /* Bits 5-7 are reserved */
-
-};
-struct libipw_basic_report {
- u8 channel;
- __le64 start_time;
- __le16 duration;
- u8 map;
-} __packed;
-
-enum { /* libipw_measurement_request.mode */
- /* Bit 0 is reserved */
- LIBIPW_MEASUREMENT_ENABLE = (1 << 1),
- LIBIPW_MEASUREMENT_REQUEST = (1 << 2),
- LIBIPW_MEASUREMENT_REPORT = (1 << 3),
- /* Bits 4-7 are reserved */
-};
-
-enum {
- LIBIPW_REPORT_BASIC = 0, /* required */
- LIBIPW_REPORT_CCA = 1, /* optional */
- LIBIPW_REPORT_RPI = 2, /* optional */
- /* 3-255 reserved */
-};
-
-struct libipw_measurement_params {
- u8 channel;
- __le64 start_time;
- __le16 duration;
-} __packed;
-
-struct libipw_measurement_request {
- struct libipw_info_element ie;
- u8 token;
- u8 mode;
- u8 type;
- struct libipw_measurement_params params[0];
-} __packed;
-
-struct libipw_measurement_report {
- struct libipw_info_element ie;
- u8 token;
- u8 mode;
- u8 type;
- union {
- struct libipw_basic_report basic[0];
- } u;
-} __packed;
-
struct libipw_tpc_report {
u8 transmit_power;
u8 link_margin;
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index c46275a9256..f2baf94f069 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -2850,9 +2850,9 @@ void
il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags,
struct ieee80211_tx_info *info)
{
- struct ieee80211_tx_rate *r = &info->control.rates[0];
+ struct ieee80211_tx_rate *r = &info->status.rates[0];
- info->antenna_sel_tx =
+ info->status.antenna =
((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
if (rate_n_flags & RATE_MCS_HT_MSK)
r->flags |= IEEE80211_TX_RC_MCS;
diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c
index 11ab1247fae..f3b8e91aa3d 100644
--- a/drivers/net/wireless/iwlegacy/4965-rs.c
+++ b/drivers/net/wireless/iwlegacy/4965-rs.c
@@ -873,7 +873,7 @@ il4965_rs_tx_status(void *il_r, struct ieee80211_supported_band *sband,
tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI) ||
tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ||
tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA) ||
- tbl_type.ant_type != info->antenna_sel_tx ||
+ tbl_type.ant_type != info->status.antenna ||
!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)
|| !!(tx_rate & RATE_MCS_GF_MSK) !=
!!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD) || rs_idx != mac_idx) {
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index 2fe62730ddd..565611eef0d 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -136,3 +136,11 @@ config IWLWIFI_EXPERIMENTAL_MFP
even if the microcode doesn't advertise it.
Say Y only if you want to experiment with MFP.
+
+config IWLWIFI_UCODE16
+ bool "support uCode 16.0"
+ depends on IWLWIFI
+ help
+ This option enables support for uCode version 16.0.
+
+ Say Y if you want to use 16.0 microcode.
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 85d163ed3db..c7c4a995dfe 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -17,6 +17,8 @@ iwlwifi-objs += iwl-drv.o
iwlwifi-objs += iwl-notif-wait.o
iwlwifi-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o
+
+iwlwifi-$(CONFIG_IWLWIFI_UCODE16) += iwl-phy-db.o
iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-testmode.o
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index 5b0d888f746..95c59e39b80 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -157,7 +157,6 @@ static struct iwl_lib_ops iwl1000_lib = {
static const struct iwl_base_params iwl1000_base_params = {
.num_of_queues = IWLAGN_NUM_QUEUES,
- .num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
.max_ll_items = OTP_MAX_LL_ITEMS_1000,
diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c
index 5635b9e2c69..e1329a13f0f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-2000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-2000.c
@@ -86,9 +86,8 @@ static void iwl2000_nic_config(struct iwl_priv *priv)
{
iwl_rf_config(priv);
- if (cfg(priv)->iq_invert)
- iwl_set_bit(trans(priv), CSR_GP_DRIVER_REG,
- CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER);
+ iwl_set_bit(trans(priv), CSR_GP_DRIVER_REG,
+ CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER);
}
static const struct iwl_sensitivity_ranges iwl2000_sensitivity = {
@@ -172,7 +171,6 @@ static struct iwl_lib_ops iwl2030_lib = {
static const struct iwl_base_params iwl2000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
- .num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.pll_cfg_val = 0,
.max_ll_items = OTP_MAX_LL_ITEMS_2x00,
.shadow_ram_support = true,
@@ -191,7 +189,6 @@ static const struct iwl_base_params iwl2000_base_params = {
static const struct iwl_base_params iwl2030_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
- .num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.pll_cfg_val = 0,
.max_ll_items = OTP_MAX_LL_ITEMS_2x00,
.shadow_ram_support = true,
@@ -234,8 +231,7 @@ static const struct iwl_bt_params iwl2030_bt_params = {
.base_params = &iwl2000_base_params, \
.need_temp_offset_calib = true, \
.temp_offset_v2 = true, \
- .led_mode = IWL_LED_RF_STATE, \
- .iq_invert = true \
+ .led_mode = IWL_LED_RF_STATE
const struct iwl_cfg iwl2000_2bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 2200 BGN",
@@ -264,8 +260,7 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = {
.need_temp_offset_calib = true, \
.temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
- .adv_pm = true, \
- .iq_invert = true \
+ .adv_pm = true
const struct iwl_cfg iwl2030_2bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
@@ -288,8 +283,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = {
.temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true, \
- .rx_with_siso_diversity = true, \
- .iq_invert = true \
+ .rx_with_siso_diversity = true
const struct iwl_cfg iwl105_bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 105 BGN",
@@ -319,8 +313,7 @@ const struct iwl_cfg iwl105_bgn_d_cfg = {
.temp_offset_v2 = true, \
.led_mode = IWL_LED_RF_STATE, \
.adv_pm = true, \
- .rx_with_siso_diversity = true, \
- .iq_invert = true \
+ .rx_with_siso_diversity = true
const struct iwl_cfg iwl135_bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 135 BGN",
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index a805e97b89a..34bc8dd0064 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -308,7 +308,6 @@ static struct iwl_lib_ops iwl5150_lib = {
static const struct iwl_base_params iwl5000_base_params = {
.eeprom_size = IWLAGN_EEPROM_IMG_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
- .num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
.led_compensation = 51,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index 64060cd738b..7075570a0f2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -269,7 +269,6 @@ static struct iwl_lib_ops iwl6030_lib = {
static const struct iwl_base_params iwl6000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
- .num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.pll_cfg_val = 0,
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
@@ -286,7 +285,6 @@ static const struct iwl_base_params iwl6000_base_params = {
static const struct iwl_base_params iwl6050_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
- .num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.pll_cfg_val = 0,
.max_ll_items = OTP_MAX_LL_ITEMS_6x50,
.shadow_ram_support = true,
@@ -303,7 +301,6 @@ static const struct iwl_base_params iwl6050_base_params = {
static const struct iwl_base_params iwl6000_g2_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.num_of_queues = IWLAGN_NUM_QUEUES,
- .num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.pll_cfg_val = 0,
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
index d0ec0abd3c8..c797ab19d93 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
@@ -103,9 +103,6 @@
/* EEPROM */
#define IWLAGN_EEPROM_IMG_SIZE 2048
-#define IWLAGN_CMD_FIFO_NUM 7
#define IWLAGN_NUM_QUEUES 20
-#define IWLAGN_NUM_AMPDU_QUEUES 9
-#define IWLAGN_FIRST_AMPDU_QUEUE 11
#endif /* __iwl_agn_hw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index 56f41c9409d..4da4ab23cce 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -228,7 +228,7 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control)
IWL_SCD_BE_MSK | IWL_SCD_BK_MSK |
IWL_SCD_MGMT_MSK;
if ((flush_control & BIT(IWL_RXON_CTX_PAN)) &&
- (priv->shrd->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
+ (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
flush_cmd.fifo_control |= IWL_PAN_SCD_VO_MSK |
IWL_PAN_SCD_VI_MSK | IWL_PAN_SCD_BE_MSK |
IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK |
@@ -615,7 +615,7 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
struct iwl_bt_uart_msg *uart_msg)
{
IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, "
- "Update Req = 0x%X",
+ "Update Req = 0x%X\n",
(BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >>
BT_UART_MSG_FRAME1MSGTYPE_POS,
(BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >>
@@ -624,7 +624,7 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
BT_UART_MSG_FRAME1UPDATEREQ_POS);
IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, "
- "Chl_SeqN = 0x%X, In band = 0x%X",
+ "Chl_SeqN = 0x%X, In band = 0x%X\n",
(BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >>
BT_UART_MSG_FRAME2OPENCONNECTIONS_POS,
(BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >>
@@ -635,7 +635,7 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
BT_UART_MSG_FRAME2INBAND_POS);
IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, "
- "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X",
+ "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n",
(BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
BT_UART_MSG_FRAME3SCOESCO_POS,
(BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >>
@@ -649,12 +649,12 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
(BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >>
BT_UART_MSG_FRAME3OBEX_POS);
- IWL_DEBUG_COEX(priv, "Idle duration = 0x%X",
+ IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n",
(BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >>
BT_UART_MSG_FRAME4IDLEDURATION_POS);
IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, "
- "eSCO Retransmissions = 0x%X",
+ "eSCO Retransmissions = 0x%X\n",
(BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >>
BT_UART_MSG_FRAME5TXACTIVITY_POS,
(BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >>
@@ -662,14 +662,14 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv,
(BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >>
BT_UART_MSG_FRAME5ESCORETRANSMIT_POS);
- IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X",
+ IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n",
(BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >>
BT_UART_MSG_FRAME6SNIFFINTERVAL_POS,
(BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >>
BT_UART_MSG_FRAME6DISCOVERABLE_POS);
IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = "
- "0x%X, Inquiry = 0x%X, Connectable = 0x%X",
+ "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n",
(BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >>
BT_UART_MSG_FRAME7SNIFFACTIVITY_POS,
(BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >>
@@ -856,7 +856,7 @@ static u8 iwl_count_chain_bitmap(u32 chain_bitmap)
void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
{
bool is_single = is_single_rx_stream(priv);
- bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->shrd->status);
+ bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt;
u32 active_chains;
u16 rx_chain;
@@ -1298,6 +1298,12 @@ int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
return -EIO;
}
+ if (test_bit(STATUS_FW_ERROR, &priv->status)) {
+ IWL_ERR(priv, "Command %s failed: FW Error\n",
+ get_cmd_string(cmd->id));
+ return -EIO;
+ }
+
/*
* Synchronous commands from this op-mode must hold
* the mutex, this ensures we don't try to send two
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index 7e590b349dd..b936ae7e00a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -969,7 +969,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
(tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
(tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
(tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) ||
- (tbl_type.ant_type != info->antenna_sel_tx) ||
+ (tbl_type.ant_type != info->status.antenna) ||
(!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
(!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
(rs_index != mac_index)) {
@@ -2166,7 +2166,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
(lq_sta->total_success > lq_sta->max_success_limit) ||
((!lq_sta->search_better_tbl) && (lq_sta->flush_timer)
&& (flush_interval_passed))) {
- IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n:",
+ IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n",
lq_sta->total_failed,
lq_sta->total_success,
flush_interval_passed);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
index f4b84d1596e..e12f11d50b8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
@@ -794,7 +794,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
return;
}
- offset = (void *)hdr - rxb_addr(rxb);
+ offset = (void *)hdr - rxb_addr(rxb) + rxb_offset(rxb);
p = rxb_steal_page(rxb);
skb_add_rx_frag(skb, 0, p, offset, len, len);
@@ -970,7 +970,7 @@ static int iwlagn_rx_reply_rx(struct iwl_priv *priv,
}
if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
- IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d/n",
+ IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n",
phy_res->cfg_phy_cnt);
return 0;
}
@@ -1134,9 +1134,6 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv)
handlers[REPLY_COMPRESSED_BA] =
iwlagn_rx_reply_compressed_ba;
- /* init calibration handlers */
- priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] =
- iwlagn_rx_calib_result;
priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
/* set up notification wait support */
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
index 2e1a31797a9..79d857d81b4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
@@ -24,6 +24,7 @@
*
*****************************************************************************/
+#include <linux/etherdevice.h>
#include "iwl-dev.h"
#include "iwl-agn.h"
#include "iwl-core.h"
@@ -59,9 +60,12 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,
__le32 old_filter = send->filter_flags;
u8 old_dev_type = send->dev_type;
int ret;
+ static const u8 deactivate_cmd[] = {
+ REPLY_WIPAN_DEACTIVATION_COMPLETE
+ };
iwl_init_notification_wait(&priv->notif_wait, &disable_wait,
- REPLY_WIPAN_DEACTIVATION_COMPLETE,
+ deactivate_cmd, ARRAY_SIZE(deactivate_cmd),
NULL, NULL);
send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
@@ -186,6 +190,109 @@ static int iwlagn_send_rxon_assoc(struct iwl_priv *priv,
return ret;
}
+static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
+{
+ u16 new_val;
+ u16 beacon_factor;
+
+ /*
+ * If mac80211 hasn't given us a beacon interval, program
+ * the default into the device (not checking this here
+ * would cause the adjustment below to return the maximum
+ * value, which may break PAN.)
+ */
+ if (!beacon_val)
+ return DEFAULT_BEACON_INTERVAL;
+
+ /*
+ * If the beacon interval we obtained from the peer
+ * is too large, we'll have to wake up more often
+ * (and in IBSS case, we'll beacon too much)
+ *
+ * For example, if max_beacon_val is 4096, and the
+ * requested beacon interval is 7000, we'll have to
+ * use 3500 to be able to wake up on the beacons.
+ *
+ * This could badly influence beacon detection stats.
+ */
+
+ beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
+ new_val = beacon_val / beacon_factor;
+
+ if (!new_val)
+ new_val = max_beacon_val;
+
+ return new_val;
+}
+
+static int iwl_send_rxon_timing(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ u64 tsf;
+ s32 interval_tm, rem;
+ struct ieee80211_conf *conf = NULL;
+ u16 beacon_int;
+ struct ieee80211_vif *vif = ctx->vif;
+
+ conf = &priv->hw->conf;
+
+ lockdep_assert_held(&priv->mutex);
+
+ memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd));
+
+ ctx->timing.timestamp = cpu_to_le64(priv->timestamp);
+ ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval);
+
+ beacon_int = vif ? vif->bss_conf.beacon_int : 0;
+
+ /*
+ * TODO: For IBSS we need to get atim_window from mac80211,
+ * for now just always use 0
+ */
+ ctx->timing.atim_window = 0;
+
+ if (ctx->ctxid == IWL_RXON_CTX_PAN &&
+ (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) &&
+ iwl_is_associated(priv, IWL_RXON_CTX_BSS) &&
+ priv->contexts[IWL_RXON_CTX_BSS].vif &&
+ priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) {
+ ctx->timing.beacon_interval =
+ priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval;
+ beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
+ } else if (ctx->ctxid == IWL_RXON_CTX_BSS &&
+ iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
+ priv->contexts[IWL_RXON_CTX_PAN].vif &&
+ priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int &&
+ (!iwl_is_associated_ctx(ctx) || !ctx->vif ||
+ !ctx->vif->bss_conf.beacon_int)) {
+ ctx->timing.beacon_interval =
+ priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval;
+ beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
+ } else {
+ beacon_int = iwl_adjust_beacon_interval(beacon_int,
+ IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT);
+ ctx->timing.beacon_interval = cpu_to_le16(beacon_int);
+ }
+
+ ctx->beacon_int = beacon_int;
+
+ tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */
+ interval_tm = beacon_int * TIME_UNIT;
+ rem = do_div(tsf, interval_tm);
+ ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
+
+ ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1;
+
+ IWL_DEBUG_ASSOC(priv,
+ "beacon interval %d beacon timer %d beacon tim %d\n",
+ le16_to_cpu(ctx->timing.beacon_interval),
+ le32_to_cpu(ctx->timing.beacon_init_val),
+ le16_to_cpu(ctx->timing.atim_window));
+
+ return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd,
+ CMD_SYNC, sizeof(ctx->timing), &ctx->timing);
+}
+
static int iwlagn_rxon_disconn(struct iwl_priv *priv,
struct iwl_rxon_context *ctx)
{
@@ -309,7 +416,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv)
int slot0 = 300, slot1 = 0;
int ret;
- if (priv->shrd->valid_contexts == BIT(IWL_RXON_CTX_BSS))
+ if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS))
return 0;
BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
@@ -394,6 +501,154 @@ int iwlagn_set_pan_params(struct iwl_priv *priv)
return ret;
}
+static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx, int hw_decrypt)
+{
+ struct iwl_rxon_cmd *rxon = &ctx->staging;
+
+ if (hw_decrypt)
+ rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
+ else
+ rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
+
+}
+
+/* validate RXON structure is valid */
+static int iwl_check_rxon_cmd(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ struct iwl_rxon_cmd *rxon = &ctx->staging;
+ u32 errors = 0;
+
+ if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
+ if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) {
+ IWL_WARN(priv, "check 2.4G: wrong narrow\n");
+ errors |= BIT(0);
+ }
+ if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) {
+ IWL_WARN(priv, "check 2.4G: wrong radar\n");
+ errors |= BIT(1);
+ }
+ } else {
+ if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) {
+ IWL_WARN(priv, "check 5.2G: not short slot!\n");
+ errors |= BIT(2);
+ }
+ if (rxon->flags & RXON_FLG_CCK_MSK) {
+ IWL_WARN(priv, "check 5.2G: CCK!\n");
+ errors |= BIT(3);
+ }
+ }
+ if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) {
+ IWL_WARN(priv, "mac/bssid mcast!\n");
+ errors |= BIT(4);
+ }
+
+ /* make sure basic rates 6Mbps and 1Mbps are supported */
+ if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 &&
+ (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) {
+ IWL_WARN(priv, "neither 1 nor 6 are basic\n");
+ errors |= BIT(5);
+ }
+
+ if (le16_to_cpu(rxon->assoc_id) > 2007) {
+ IWL_WARN(priv, "aid > 2007\n");
+ errors |= BIT(6);
+ }
+
+ if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK))
+ == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) {
+ IWL_WARN(priv, "CCK and short slot\n");
+ errors |= BIT(7);
+ }
+
+ if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
+ == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
+ IWL_WARN(priv, "CCK and auto detect");
+ errors |= BIT(8);
+ }
+
+ if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK |
+ RXON_FLG_TGG_PROTECT_MSK)) ==
+ RXON_FLG_TGG_PROTECT_MSK) {
+ IWL_WARN(priv, "TGg but no auto-detect\n");
+ errors |= BIT(9);
+ }
+
+ if (rxon->channel == 0) {
+ IWL_WARN(priv, "zero channel is invalid\n");
+ errors |= BIT(10);
+ }
+
+ WARN(errors, "Invalid RXON (%#x), channel %d",
+ errors, le16_to_cpu(rxon->channel));
+
+ return errors ? -EINVAL : 0;
+}
+
+/**
+ * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
+ * @priv: staging_rxon is compared to active_rxon
+ *
+ * If the RXON structure is changing enough to require a new tune,
+ * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that
+ * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
+ */
+static int iwl_full_rxon_required(struct iwl_priv *priv,
+ struct iwl_rxon_context *ctx)
+{
+ const struct iwl_rxon_cmd *staging = &ctx->staging;
+ const struct iwl_rxon_cmd *active = &ctx->active;
+
+#define CHK(cond) \
+ if ((cond)) { \
+ IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \
+ return 1; \
+ }
+
+#define CHK_NEQ(c1, c2) \
+ if ((c1) != (c2)) { \
+ IWL_DEBUG_INFO(priv, "need full RXON - " \
+ #c1 " != " #c2 " - %d != %d\n", \
+ (c1), (c2)); \
+ return 1; \
+ }
+
+ /* These items are only settable from the full RXON command */
+ CHK(!iwl_is_associated_ctx(ctx));
+ CHK(compare_ether_addr(staging->bssid_addr, active->bssid_addr));
+ CHK(compare_ether_addr(staging->node_addr, active->node_addr));
+ CHK(compare_ether_addr(staging->wlap_bssid_addr,
+ active->wlap_bssid_addr));
+ CHK_NEQ(staging->dev_type, active->dev_type);
+ CHK_NEQ(staging->channel, active->channel);
+ CHK_NEQ(staging->air_propagation, active->air_propagation);
+ CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
+ active->ofdm_ht_single_stream_basic_rates);
+ CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
+ active->ofdm_ht_dual_stream_basic_rates);
+ CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates,
+ active->ofdm_ht_triple_stream_basic_rates);
+ CHK_NEQ(staging->assoc_id, active->assoc_id);
+
+ /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
+ * be updated with the RXON_ASSOC command -- however only some
+ * flag transitions are allowed using RXON_ASSOC */
+
+ /* Check if we are not switching bands */
+ CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
+ active->flags & RXON_FLG_BAND_24G_MSK);
+
+ /* Check if we are switching association toggle */
+ CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
+ active->filter_flags & RXON_FILTER_ASSOC_MSK);
+
+#undef CHK
+#undef CHK_NEQ
+
+ return 0;
+}
+
/**
* iwlagn_commit_rxon - commit staging_rxon to hardware
*
@@ -547,7 +802,7 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
const struct iwl_channel_info *ch_info;
int ret = 0;
- IWL_DEBUG_MAC80211(priv, "enter: changed %#x", changed);
+ IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed);
mutex_lock(&priv->mutex);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 34adedc74d3..697f2032bfd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -40,6 +40,17 @@
#include "iwl-agn.h"
#include "iwl-trans.h"
+static const u8 tid_to_ac[] = {
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VO,
+};
+
static void iwlagn_tx_cmd_protection(struct iwl_priv *priv,
struct ieee80211_tx_info *info,
__le16 fc, __le32 *tx_flags)
@@ -293,6 +304,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
u16 len, seq_number = 0;
u8 sta_id, tid = IWL_MAX_TID_COUNT;
bool is_agg = false;
+ int txq_id;
if (info->control.vif)
ctx = iwl_rxon_ctx_from_vif(info->control.vif);
@@ -435,7 +447,27 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
/* Copy MAC header from skb into command buffer */
memcpy(tx_cmd->hdr, hdr, hdr_len);
- if (iwl_trans_tx(trans(priv), skb, dev_cmd, ctx->ctxid, sta_id, tid))
+ if (is_agg)
+ txq_id = priv->tid_data[sta_id][tid].agg.txq_id;
+ else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+ /*
+ * Send this frame after DTIM -- there's a special queue
+ * reserved for this for contexts that support AP mode.
+ */
+ txq_id = ctx->mcast_queue;
+
+ /*
+ * The microcode will clear the more data
+ * bit in the last frame it transmits.
+ */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+ txq_id = IWL_AUX_QUEUE;
+ else
+ txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)];
+
+ if (iwl_trans_tx(trans(priv), skb, dev_cmd, txq_id))
goto drop_unlock_sta;
if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc) &&
@@ -464,11 +496,32 @@ drop_unlock_priv:
return -1;
}
+static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int ac)
+{
+ int q;
+
+ for (q = IWLAGN_FIRST_AMPDU_QUEUE;
+ q < cfg(priv)->base_params->num_of_queues; q++) {
+ if (!test_and_set_bit(q, priv->agg_q_alloc)) {
+ priv->queue_to_ac[q] = ac;
+ return q;
+ }
+ }
+
+ return -ENOSPC;
+}
+
+static void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q)
+{
+ clear_bit(q, priv->agg_q_alloc);
+ priv->queue_to_ac[q] = IWL_INVALID_AC;
+}
+
int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid)
{
struct iwl_tid_data *tid_data;
- int sta_id;
+ int sta_id, txq_id;
sta_id = iwl_sta_id(sta);
@@ -480,6 +533,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
spin_lock_bh(&priv->sta_lock);
tid_data = &priv->tid_data[sta_id][tid];
+ txq_id = priv->tid_data[sta_id][tid].agg.txq_id;
switch (priv->tid_data[sta_id][tid].agg.state) {
case IWL_EMPTYING_HW_QUEUE_ADDBA:
@@ -504,9 +558,13 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
/* There are still packets for this RA / TID in the HW */
- if (tid_data->agg.ssn != tid_data->next_reclaimed) {
+ if (!test_bit(txq_id, priv->agg_q_alloc)) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "stopping AGG on STA/TID %d/%d but hwq %d not used\n",
+ sta_id, tid, txq_id);
+ } else if (tid_data->agg.ssn != tid_data->next_reclaimed) {
IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
- "next_recl = %d",
+ "next_recl = %d\n",
tid_data->agg.ssn,
tid_data->next_reclaimed);
priv->tid_data[sta_id][tid].agg.state =
@@ -515,14 +573,17 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
return 0;
}
- IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d",
+ IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n",
tid_data->agg.ssn);
turn_off:
priv->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF;
spin_unlock_bh(&priv->sta_lock);
- iwl_trans_tx_agg_disable(trans(priv), sta_id, tid);
+ if (test_bit(txq_id, priv->agg_q_alloc)) {
+ iwl_trans_tx_agg_disable(trans(priv), txq_id);
+ iwlagn_dealloc_agg_txq(priv, txq_id);
+ }
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
@@ -533,8 +594,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
struct iwl_tid_data *tid_data;
- int sta_id;
- int ret;
+ int sta_id, txq_id, ret;
IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n",
sta->addr, tid);
@@ -552,36 +612,37 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
return -ENXIO;
}
+ txq_id = iwlagn_alloc_agg_txq(priv, tid_to_ac[tid]);
+ if (txq_id < 0) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "No free aggregation queue for %pM/%d\n",
+ sta->addr, tid);
+ return txq_id;
+ }
+
ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
if (ret)
return ret;
spin_lock_bh(&priv->sta_lock);
-
tid_data = &priv->tid_data[sta_id][tid];
tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number);
+ tid_data->agg.txq_id = txq_id;
*ssn = tid_data->agg.ssn;
- ret = iwl_trans_tx_agg_alloc(trans(priv), sta_id, tid);
- if (ret) {
- spin_unlock_bh(&priv->sta_lock);
- return ret;
- }
-
if (*ssn == tid_data->next_reclaimed) {
- IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d",
+ IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n",
tid_data->agg.ssn);
tid_data->agg.state = IWL_AGG_ON;
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
} else {
IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, "
- "next_reclaimed = %d",
+ "next_reclaimed = %d\n",
tid_data->agg.ssn,
tid_data->next_reclaimed);
tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA;
}
-
spin_unlock_bh(&priv->sta_lock);
return ret;
@@ -592,15 +653,20 @@ int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif,
{
struct iwl_station_priv *sta_priv = (void *) sta->drv_priv;
struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif);
+ int q, fifo;
u16 ssn;
buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
spin_lock_bh(&priv->sta_lock);
ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn;
+ q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id;
spin_unlock_bh(&priv->sta_lock);
- iwl_trans_tx_agg_setup(trans(priv), ctx->ctxid, sta_priv->sta_id, tid,
+ fifo = ctx->ac_to_fifo[tid_to_ac[tid]];
+
+ iwl_trans_tx_agg_setup(trans(priv), q, fifo,
+ sta_priv->sta_id, tid,
buf_size, ssn);
/*
@@ -666,7 +732,9 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid)
IWL_DEBUG_TX_QUEUES(priv,
"Can continue DELBA flow ssn = next_recl ="
" %d", tid_data->next_reclaimed);
- iwl_trans_tx_agg_disable(trans(priv), sta_id, tid);
+ iwl_trans_tx_agg_disable(trans(priv),
+ tid_data->agg.txq_id);
+ iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id);
tid_data->agg.state = IWL_AGG_OFF;
ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
}
@@ -711,9 +779,9 @@ static void iwlagn_non_agg_tx_status(struct iwl_priv *priv,
static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
struct ieee80211_tx_info *info)
{
- struct ieee80211_tx_rate *r = &info->control.rates[0];
+ struct ieee80211_tx_rate *r = &info->status.rates[0];
- info->antenna_sel_tx =
+ info->status.antenna =
((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
if (rate_n_flags & RATE_MCS_HT_MSK)
r->flags |= IEEE80211_TX_RC_MCS;
@@ -1005,6 +1073,29 @@ static void iwl_check_abort_status(struct iwl_priv *priv,
}
}
+static int iwl_reclaim(struct iwl_priv *priv, int sta_id, int tid,
+ int txq_id, int ssn, struct sk_buff_head *skbs)
+{
+ if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE &&
+ tid != IWL_TID_NON_QOS &&
+ txq_id != priv->tid_data[sta_id][tid].agg.txq_id)) {
+ /*
+ * FIXME: this is a uCode bug which need to be addressed,
+ * log the information and return for now.
+ * Since it is can possibly happen very often and in order
+ * not to fill the syslog, don't use IWL_ERR or IWL_WARN
+ */
+ IWL_DEBUG_TX_QUEUES(priv,
+ "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n",
+ txq_id, sta_id, tid,
+ priv->tid_data[sta_id][tid].agg.txq_id);
+ return 1;
+ }
+
+ iwl_trans_reclaim(trans(priv), txq_id, ssn, skbs);
+ return 0;
+}
+
int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
@@ -1059,13 +1150,12 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
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",
+ IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
next_reclaimed);
}
/*we can free until ssn % q.n_bd not inclusive */
- WARN_ON(iwl_trans_reclaim(trans(priv), sta_id, tid,
- txq_id, ssn, &skbs));
+ WARN_ON(iwl_reclaim(priv, sta_id, tid, txq_id, ssn, &skbs));
iwlagn_check_ratid_empty(priv, sta_id, tid);
freed = 0;
@@ -1183,8 +1273,8 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
/* Release all TFDs before the SSN, i.e. all TFDs in front of
* block-ack window (we assume that they've been successfully
* transmitted ... if not, it's too late anyway). */
- if (iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow,
- ba_resp_scd_ssn, &reclaimed_skbs)) {
+ if (iwl_reclaim(priv, sta_id, tid, scd_flow,
+ ba_resp_scd_ssn, &reclaimed_skbs)) {
spin_unlock(&priv->sta_lock);
return 0;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index f1226dbf789..22c953d65be 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -26,6 +26,9 @@
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -379,7 +382,7 @@ static void iwl_continuous_event_trace(struct iwl_priv *priv)
u32 num_wraps; /* # times uCode wrapped to top of log */
u32 next_entry; /* index of next entry to be written by uCode */
- base = priv->shrd->device_pointers.log_event_table;
+ base = priv->device_pointers.log_event_table;
if (iwlagn_hw_valid_rtc_data_addr(base)) {
iwl_read_targ_mem_words(trans(priv), base, &read, sizeof(read));
@@ -488,6 +491,93 @@ static void iwl_bg_tx_flush(struct work_struct *work)
iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL);
}
+/*
+ * queue/FIFO/AC mapping definitions
+ */
+
+#define IWL_TX_FIFO_BK 0 /* shared */
+#define IWL_TX_FIFO_BE 1
+#define IWL_TX_FIFO_VI 2 /* shared */
+#define IWL_TX_FIFO_VO 3
+#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK
+#define IWL_TX_FIFO_BE_IPAN 4
+#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI
+#define IWL_TX_FIFO_VO_IPAN 5
+/* re-uses the VO FIFO, uCode will properly flush/schedule */
+#define IWL_TX_FIFO_AUX 5
+#define IWL_TX_FIFO_UNUSED -1
+
+#define IWLAGN_CMD_FIFO_NUM 7
+
+/*
+ * This queue number is required for proper operation
+ * because the ucode will stop/start the scheduler as
+ * required.
+ */
+#define IWL_IPAN_MCAST_QUEUE 8
+
+static const u8 iwlagn_default_queue_to_tx_fifo[] = {
+ IWL_TX_FIFO_VO,
+ IWL_TX_FIFO_VI,
+ IWL_TX_FIFO_BE,
+ IWL_TX_FIFO_BK,
+ IWLAGN_CMD_FIFO_NUM,
+};
+
+static const u8 iwlagn_ipan_queue_to_tx_fifo[] = {
+ IWL_TX_FIFO_VO,
+ IWL_TX_FIFO_VI,
+ IWL_TX_FIFO_BE,
+ IWL_TX_FIFO_BK,
+ IWL_TX_FIFO_BK_IPAN,
+ IWL_TX_FIFO_BE_IPAN,
+ IWL_TX_FIFO_VI_IPAN,
+ IWL_TX_FIFO_VO_IPAN,
+ IWL_TX_FIFO_BE_IPAN,
+ IWLAGN_CMD_FIFO_NUM,
+ IWL_TX_FIFO_AUX,
+};
+
+static const u8 iwlagn_bss_ac_to_fifo[] = {
+ IWL_TX_FIFO_VO,
+ IWL_TX_FIFO_VI,
+ IWL_TX_FIFO_BE,
+ IWL_TX_FIFO_BK,
+};
+
+static const u8 iwlagn_bss_ac_to_queue[] = {
+ 0, 1, 2, 3,
+};
+
+static const u8 iwlagn_pan_ac_to_fifo[] = {
+ IWL_TX_FIFO_VO_IPAN,
+ IWL_TX_FIFO_VI_IPAN,
+ IWL_TX_FIFO_BE_IPAN,
+ IWL_TX_FIFO_BK_IPAN,
+};
+
+static const u8 iwlagn_pan_ac_to_queue[] = {
+ 7, 6, 5, 4,
+};
+
+static const u8 iwlagn_bss_queue_to_ac[] = {
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+};
+
+static const u8 iwlagn_pan_queue_to_ac[] = {
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+};
+
static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
{
int i;
@@ -496,9 +586,9 @@ static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
* The default context is always valid,
* the PAN context depends on uCode.
*/
- priv->shrd->valid_contexts = BIT(IWL_RXON_CTX_BSS);
+ priv->valid_contexts = BIT(IWL_RXON_CTX_BSS);
if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN)
- priv->shrd->valid_contexts |= BIT(IWL_RXON_CTX_PAN);
+ priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN);
for (i = 0; i < NUM_IWL_RXON_CTX; i++)
priv->contexts[i].ctxid = i;
@@ -520,6 +610,10 @@ static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS;
priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS;
priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS;
+ memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue,
+ iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue));
+ memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo,
+ iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo));
priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON;
priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd =
@@ -542,6 +636,11 @@ static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags)
priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
+ memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue,
+ iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue));
+ memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo,
+ iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo));
+ priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE;
BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
}
@@ -824,11 +923,10 @@ void iwl_down(struct iwl_priv *priv)
STATUS_RF_KILL_HW |
test_bit(STATUS_GEO_CONFIGURED, &priv->status) <<
STATUS_GEO_CONFIGURED |
+ test_bit(STATUS_FW_ERROR, &priv->status) <<
+ STATUS_FW_ERROR |
test_bit(STATUS_EXIT_PENDING, &priv->status) <<
STATUS_EXIT_PENDING;
- priv->shrd->status &=
- test_bit(STATUS_FW_ERROR, &priv->shrd->status) <<
- STATUS_FW_ERROR;
dev_kfree_skb(priv->beacon_skb);
priv->beacon_skb = NULL;
@@ -869,6 +967,7 @@ void iwlagn_prepare_restart(struct iwl_priv *priv)
u8 bt_load;
u8 bt_status;
bool bt_is_sco;
+ int i;
lockdep_assert_held(&priv->mutex);
@@ -898,6 +997,15 @@ void iwlagn_prepare_restart(struct iwl_priv *priv)
priv->bt_traffic_load = bt_load;
priv->bt_status = bt_status;
priv->bt_is_sco = bt_is_sco;
+
+ /* reset all queues */
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ atomic_set(&priv->ac_stop_count[i], 0);
+
+ for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++)
+ priv->queue_to_ac[i] = IWL_INVALID_AC;
+
+ memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc));
}
static void iwl_bg_restart(struct work_struct *data)
@@ -907,7 +1015,7 @@ static void iwl_bg_restart(struct work_struct *data)
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
- if (test_and_clear_bit(STATUS_FW_ERROR, &priv->shrd->status)) {
+ if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) {
mutex_lock(&priv->mutex);
iwlagn_prepare_restart(priv);
mutex_unlock(&priv->mutex);
@@ -1028,6 +1136,189 @@ static void iwl_init_hw_rates(struct ieee80211_rate *rates)
}
}
+#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
+#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
+static void iwl_init_ht_hw_capab(const struct iwl_priv *priv,
+ struct ieee80211_sta_ht_cap *ht_info,
+ enum ieee80211_band band)
+{
+ u16 max_bit_rate = 0;
+ u8 rx_chains_num = hw_params(priv).rx_chains_num;
+ u8 tx_chains_num = hw_params(priv).tx_chains_num;
+
+ ht_info->cap = 0;
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+
+ ht_info->ht_supported = true;
+
+ if (cfg(priv)->ht_params &&
+ cfg(priv)->ht_params->ht_greenfield_support)
+ ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
+ max_bit_rate = MAX_BIT_RATE_20_MHZ;
+ if (hw_params(priv).ht40_channel & BIT(band)) {
+ ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
+ ht_info->mcs.rx_mask[4] = 0x01;
+ max_bit_rate = MAX_BIT_RATE_40_MHZ;
+ }
+
+ if (iwlagn_mod_params.amsdu_size_8K)
+ ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+ ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF;
+ ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF;
+
+ ht_info->mcs.rx_mask[0] = 0xFF;
+ if (rx_chains_num >= 2)
+ ht_info->mcs.rx_mask[1] = 0xFF;
+ if (rx_chains_num >= 3)
+ ht_info->mcs.rx_mask[2] = 0xFF;
+
+ /* Highest supported Rx data rate */
+ max_bit_rate *= rx_chains_num;
+ WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK);
+ ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate);
+
+ /* Tx MCS capabilities */
+ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+ if (tx_chains_num != rx_chains_num) {
+ ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
+ ht_info->mcs.tx_params |= ((tx_chains_num - 1) <<
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
+ }
+}
+
+/**
+ * iwl_init_geos - Initialize mac80211's geo/channel info based from eeprom
+ */
+static int iwl_init_geos(struct iwl_priv *priv)
+{
+ struct iwl_channel_info *ch;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *channels;
+ struct ieee80211_channel *geo_ch;
+ struct ieee80211_rate *rates;
+ int i = 0;
+ s8 max_tx_power = IWLAGN_TX_POWER_TARGET_POWER_MIN;
+
+ if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates ||
+ priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) {
+ IWL_DEBUG_INFO(priv, "Geography modes already initialized.\n");
+ set_bit(STATUS_GEO_CONFIGURED, &priv->status);
+ return 0;
+ }
+
+ channels = kcalloc(priv->channel_count,
+ sizeof(struct ieee80211_channel), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ rates = kcalloc(IWL_RATE_COUNT_LEGACY, sizeof(struct ieee80211_rate),
+ GFP_KERNEL);
+ if (!rates) {
+ kfree(channels);
+ return -ENOMEM;
+ }
+
+ /* 5.2GHz channels start after the 2.4GHz channels */
+ sband = &priv->bands[IEEE80211_BAND_5GHZ];
+ sband->channels = &channels[ARRAY_SIZE(iwl_eeprom_band_1)];
+ /* just OFDM */
+ sband->bitrates = &rates[IWL_FIRST_OFDM_RATE];
+ sband->n_bitrates = IWL_RATE_COUNT_LEGACY - IWL_FIRST_OFDM_RATE;
+
+ if (hw_params(priv).sku & EEPROM_SKU_CAP_11N_ENABLE)
+ iwl_init_ht_hw_capab(priv, &sband->ht_cap,
+ IEEE80211_BAND_5GHZ);
+
+ sband = &priv->bands[IEEE80211_BAND_2GHZ];
+ sband->channels = channels;
+ /* OFDM & CCK */
+ sband->bitrates = rates;
+ sband->n_bitrates = IWL_RATE_COUNT_LEGACY;
+
+ if (hw_params(priv).sku & EEPROM_SKU_CAP_11N_ENABLE)
+ iwl_init_ht_hw_capab(priv, &sband->ht_cap,
+ IEEE80211_BAND_2GHZ);
+
+ priv->ieee_channels = channels;
+ priv->ieee_rates = rates;
+
+ for (i = 0; i < priv->channel_count; i++) {
+ ch = &priv->channel_info[i];
+
+ /* FIXME: might be removed if scan is OK */
+ if (!is_channel_valid(ch))
+ continue;
+
+ sband = &priv->bands[ch->band];
+
+ geo_ch = &sband->channels[sband->n_channels++];
+
+ geo_ch->center_freq =
+ ieee80211_channel_to_frequency(ch->channel, ch->band);
+ geo_ch->max_power = ch->max_power_avg;
+ geo_ch->max_antenna_gain = 0xff;
+ geo_ch->hw_value = ch->channel;
+
+ if (is_channel_valid(ch)) {
+ if (!(ch->flags & EEPROM_CHANNEL_IBSS))
+ geo_ch->flags |= IEEE80211_CHAN_NO_IBSS;
+
+ if (!(ch->flags & EEPROM_CHANNEL_ACTIVE))
+ geo_ch->flags |= IEEE80211_CHAN_PASSIVE_SCAN;
+
+ if (ch->flags & EEPROM_CHANNEL_RADAR)
+ geo_ch->flags |= IEEE80211_CHAN_RADAR;
+
+ geo_ch->flags |= ch->ht40_extension_channel;
+
+ if (ch->max_power_avg > max_tx_power)
+ max_tx_power = ch->max_power_avg;
+ } else {
+ geo_ch->flags |= IEEE80211_CHAN_DISABLED;
+ }
+
+ IWL_DEBUG_INFO(priv, "Channel %d Freq=%d[%sGHz] %s flag=0x%X\n",
+ ch->channel, geo_ch->center_freq,
+ is_channel_a_band(ch) ? "5.2" : "2.4",
+ geo_ch->flags & IEEE80211_CHAN_DISABLED ?
+ "restricted" : "valid",
+ geo_ch->flags);
+ }
+
+ priv->tx_power_device_lmt = max_tx_power;
+ priv->tx_power_user_lmt = max_tx_power;
+ priv->tx_power_next = max_tx_power;
+
+ if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) &&
+ hw_params(priv).sku & EEPROM_SKU_CAP_BAND_52GHZ) {
+ IWL_INFO(priv, "Incorrectly detected BG card as ABG. "
+ "Please send your %s to maintainer.\n",
+ trans(priv)->hw_id_str);
+ hw_params(priv).sku &= ~EEPROM_SKU_CAP_BAND_52GHZ;
+ }
+
+ IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n",
+ priv->bands[IEEE80211_BAND_2GHZ].n_channels,
+ priv->bands[IEEE80211_BAND_5GHZ].n_channels);
+
+ set_bit(STATUS_GEO_CONFIGURED, &priv->status);
+
+ return 0;
+}
+
+/*
+ * iwl_free_geos - undo allocations in iwl_init_geos
+ */
+static void iwl_free_geos(struct iwl_priv *priv)
+{
+ kfree(priv->ieee_channels);
+ kfree(priv->ieee_rates);
+ clear_bit(STATUS_GEO_CONFIGURED, &priv->status);
+}
+
static int iwl_init_drv(struct iwl_priv *priv)
{
int ret;
@@ -1130,8 +1421,6 @@ static void iwl_set_hw_params(struct iwl_priv *priv)
if (iwlagn_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
hw_params(priv).sku &= ~EEPROM_SKU_CAP_11N_ENABLE;
- hw_params(priv).num_ampdu_queues =
- cfg(priv)->base_params->num_of_ampdu_queues;
hw_params(priv).wd_timeout = cfg(priv)->base_params->wd_timeout;
/* Device-specific setup */
@@ -1178,7 +1467,6 @@ static void iwl_debug_config(struct iwl_priv *priv)
static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
const struct iwl_fw *fw)
{
- int err = 0;
struct iwl_priv *priv;
struct ieee80211_hw *hw;
struct iwl_op_mode *op_mode;
@@ -1193,6 +1481,9 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
STATISTICS_NOTIFICATION,
REPLY_TX,
};
+ const u8 *q_to_ac;
+ int n_q_to_ac;
+ int i;
/************************
* 1. Allocating HW data
@@ -1201,7 +1492,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
if (!hw) {
pr_err("%s: Cannot allocate network device\n",
cfg(trans)->name);
- err = -ENOMEM;
goto out;
}
@@ -1210,8 +1500,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
priv = IWL_OP_MODE_GET_DVM(op_mode);
priv->shrd = trans->shrd;
priv->fw = fw;
- /* TODO: remove fw from shared data later */
- priv->shrd->fw = fw;
/*
* Populate the state variables that the transport layer needs
@@ -1230,9 +1518,19 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) {
priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
+ trans_cfg.queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo;
+ trans_cfg.n_queue_to_fifo =
+ ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo);
+ q_to_ac = iwlagn_pan_queue_to_ac;
+ n_q_to_ac = ARRAY_SIZE(iwlagn_pan_queue_to_ac);
} else {
priv->sta_key_max_num = STA_KEY_MAX_NUM;
trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
+ trans_cfg.queue_to_fifo = iwlagn_default_queue_to_tx_fifo;
+ trans_cfg.n_queue_to_fifo =
+ ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo);
+ q_to_ac = iwlagn_bss_queue_to_ac;
+ n_q_to_ac = ARRAY_SIZE(iwlagn_bss_queue_to_ac);
}
/* Configure transport layer */
@@ -1273,26 +1571,24 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
IWL_INFO(priv, "Detected %s, REV=0x%X\n",
cfg(priv)->name, trans(priv)->hw_rev);
- err = iwl_trans_start_hw(trans(priv));
- if (err)
+ if (iwl_trans_start_hw(trans(priv)))
goto out_free_traffic_mem;
/*****************
* 3. Read EEPROM
*****************/
- err = iwl_eeprom_init(trans(priv), trans(priv)->hw_rev);
- /* Reset chip to save power until we load uCode during "up". */
- iwl_trans_stop_hw(trans(priv));
- if (err) {
+ /* Read the EEPROM */
+ if (iwl_eeprom_init(trans(priv), trans(priv)->hw_rev)) {
IWL_ERR(priv, "Unable to init EEPROM\n");
goto out_free_traffic_mem;
}
- err = iwl_eeprom_check_version(priv);
- if (err)
+ /* Reset chip to save power until we load uCode during "up". */
+ iwl_trans_stop_hw(trans(priv));
+
+ if (iwl_eeprom_check_version(priv))
goto out_free_eeprom;
- err = iwl_eeprom_init_hw_params(priv);
- if (err)
+ if (iwl_eeprom_init_hw_params(priv))
goto out_free_eeprom;
/* extract MAC Address */
@@ -1323,6 +1619,11 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P;
priv->sta_key_max_num = STA_KEY_MAX_NUM;
trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
+ trans_cfg.queue_to_fifo = iwlagn_default_queue_to_tx_fifo;
+ trans_cfg.n_queue_to_fifo =
+ ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo);
+ q_to_ac = iwlagn_bss_queue_to_ac;
+ n_q_to_ac = ARRAY_SIZE(iwlagn_bss_queue_to_ac);
/* Configure transport layer again*/
iwl_trans_configure(trans(priv), &trans_cfg);
@@ -1331,10 +1632,22 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
/*******************
* 5. Setup priv
*******************/
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ atomic_set(&priv->ac_stop_count[i], 0);
- err = iwl_init_drv(priv);
- if (err)
+ for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
+ if (i < n_q_to_ac)
+ priv->queue_to_ac[i] = q_to_ac[i];
+ else
+ priv->queue_to_ac[i] = IWL_INVALID_AC;
+ }
+
+ WARN_ON(trans_cfg.queue_to_fifo[trans_cfg.cmd_queue] !=
+ IWLAGN_CMD_FIFO_NUM);
+
+ if (iwl_init_drv(priv))
goto out_free_eeprom;
+
/* At this point both hw and priv are initialized. */
/********************
@@ -1367,15 +1680,12 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
*
* 7. Setup and register with mac80211 and debugfs
**************************************************/
- err = iwlagn_mac_setup_register(priv, &fw->ucode_capa);
- if (err)
+ if (iwlagn_mac_setup_register(priv, &fw->ucode_capa))
goto out_destroy_workqueue;
- err = iwl_dbgfs_register(priv, DRV_NAME);
- if (err)
+ if (iwl_dbgfs_register(priv, DRV_NAME))
IWL_ERR(priv,
- "failed to create debugfs files. Ignoring error: %d\n",
- err);
+ "failed to create debugfs files. Ignoring error\n");
return op_mode;
@@ -1429,13 +1739,399 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
ieee80211_free_hw(priv->hw);
}
+static const char * const desc_lookup_text[] = {
+ "OK",
+ "FAIL",
+ "BAD_PARAM",
+ "BAD_CHECKSUM",
+ "NMI_INTERRUPT_WDG",
+ "SYSASSERT",
+ "FATAL_ERROR",
+ "BAD_COMMAND",
+ "HW_ERROR_TUNE_LOCK",
+ "HW_ERROR_TEMPERATURE",
+ "ILLEGAL_CHAN_FREQ",
+ "VCC_NOT_STABLE",
+ "FH_ERROR",
+ "NMI_INTERRUPT_HOST",
+ "NMI_INTERRUPT_ACTION_PT",
+ "NMI_INTERRUPT_UNKNOWN",
+ "UCODE_VERSION_MISMATCH",
+ "HW_ERROR_ABS_LOCK",
+ "HW_ERROR_CAL_LOCK_FAIL",
+ "NMI_INTERRUPT_INST_ACTION_PT",
+ "NMI_INTERRUPT_DATA_ACTION_PT",
+ "NMI_TRM_HW_ER",
+ "NMI_INTERRUPT_TRM",
+ "NMI_INTERRUPT_BREAK_POINT",
+ "DEBUG_0",
+ "DEBUG_1",
+ "DEBUG_2",
+ "DEBUG_3",
+};
+
+static struct { char *name; u8 num; } advanced_lookup[] = {
+ { "NMI_INTERRUPT_WDG", 0x34 },
+ { "SYSASSERT", 0x35 },
+ { "UCODE_VERSION_MISMATCH", 0x37 },
+ { "BAD_COMMAND", 0x38 },
+ { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
+ { "FATAL_ERROR", 0x3D },
+ { "NMI_TRM_HW_ERR", 0x46 },
+ { "NMI_INTERRUPT_TRM", 0x4C },
+ { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
+ { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
+ { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
+ { "NMI_INTERRUPT_HOST", 0x66 },
+ { "NMI_INTERRUPT_ACTION_PT", 0x7C },
+ { "NMI_INTERRUPT_UNKNOWN", 0x84 },
+ { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
+ { "ADVANCED_SYSASSERT", 0 },
+};
+
+static const char *desc_lookup(u32 num)
+{
+ int i;
+ int max = ARRAY_SIZE(desc_lookup_text);
+
+ if (num < max)
+ return desc_lookup_text[num];
+
+ max = ARRAY_SIZE(advanced_lookup) - 1;
+ for (i = 0; i < max; i++) {
+ if (advanced_lookup[i].num == num)
+ break;
+ }
+ return advanced_lookup[i].name;
+}
+
+#define ERROR_START_OFFSET (1 * sizeof(u32))
+#define ERROR_ELEM_SIZE (7 * sizeof(u32))
+
+static void iwl_dump_nic_error_log(struct iwl_priv *priv)
+{
+ struct iwl_trans *trans = trans(priv);
+ u32 base;
+ struct iwl_error_event_table table;
+
+ base = priv->device_pointers.error_event_table;
+ if (priv->cur_ucode == IWL_UCODE_INIT) {
+ if (!base)
+ base = priv->fw->init_errlog_ptr;
+ } else {
+ if (!base)
+ base = priv->fw->inst_errlog_ptr;
+ }
+
+ if (!iwlagn_hw_valid_rtc_data_addr(base)) {
+ IWL_ERR(priv,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base,
+ (priv->cur_ucode == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return;
+ }
+
+ /*TODO: Update dbgfs with ISR error stats obtained below */
+ iwl_read_targ_mem_words(trans, base, &table, sizeof(table));
+
+ if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+ IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+ IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+ priv->shrd->status, table.valid);
+ }
+
+ trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
+ table.data1, table.data2, table.line,
+ table.blink1, table.blink2, table.ilink1,
+ table.ilink2, table.bcon_time, table.gp1,
+ table.gp2, table.gp3, table.ucode_ver,
+ table.hw_ver, table.brd_ver);
+ IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
+ desc_lookup(table.error_id));
+ IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
+ IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1);
+ IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2);
+ IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1);
+ IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2);
+ IWL_ERR(priv, "0x%08X | data1\n", table.data1);
+ IWL_ERR(priv, "0x%08X | data2\n", table.data2);
+ IWL_ERR(priv, "0x%08X | line\n", table.line);
+ IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time);
+ IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low);
+ IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi);
+ IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1);
+ IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2);
+ IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3);
+ IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver);
+ IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver);
+ IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver);
+ IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd);
+ IWL_ERR(priv, "0x%08X | isr0\n", table.isr0);
+ IWL_ERR(priv, "0x%08X | isr1\n", table.isr1);
+ IWL_ERR(priv, "0x%08X | isr2\n", table.isr2);
+ IWL_ERR(priv, "0x%08X | isr3\n", table.isr3);
+ IWL_ERR(priv, "0x%08X | isr4\n", table.isr4);
+ IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref);
+ IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event);
+ IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control);
+ IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration);
+ IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
+ IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
+ IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
+ IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp);
+ IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler);
+}
+
+#define EVENT_START_OFFSET (4 * sizeof(u32))
+
+/**
+ * iwl_print_event_log - Dump error event log to syslog
+ *
+ */
+static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
+ u32 num_events, u32 mode,
+ int pos, char **buf, size_t bufsz)
+{
+ u32 i;
+ u32 base; /* SRAM byte address of event log header */
+ u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
+ u32 ptr; /* SRAM byte address of log data */
+ u32 ev, time, data; /* event log data */
+ unsigned long reg_flags;
+
+ struct iwl_trans *trans = trans(priv);
+
+ if (num_events == 0)
+ return pos;
+
+ base = priv->device_pointers.log_event_table;
+ if (priv->cur_ucode == IWL_UCODE_INIT) {
+ if (!base)
+ base = priv->fw->init_evtlog_ptr;
+ } else {
+ if (!base)
+ base = priv->fw->inst_evtlog_ptr;
+ }
+
+ if (mode == 0)
+ event_size = 2 * sizeof(u32);
+ else
+ event_size = 3 * sizeof(u32);
+
+ ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
+
+ /* Make sure device is powered up for SRAM reads */
+ spin_lock_irqsave(&trans->reg_lock, reg_flags);
+ if (unlikely(!iwl_grab_nic_access(trans)))
+ goto out_unlock;
+
+ /* Set starting address; reads will auto-increment */
+ iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr);
+
+ /* "time" is actually "data" for mode 0 (no timestamp).
+ * place event id # at far right for easier visual parsing. */
+ for (i = 0; i < num_events; i++) {
+ ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
+ time = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
+ if (mode == 0) {
+ /* data, ev */
+ if (bufsz) {
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "EVT_LOG:0x%08x:%04u\n",
+ time, ev);
+ } else {
+ trace_iwlwifi_dev_ucode_event(trans->dev, 0,
+ time, ev);
+ IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
+ time, ev);
+ }
+ } else {
+ data = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
+ if (bufsz) {
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "EVT_LOGT:%010u:0x%08x:%04u\n",
+ time, data, ev);
+ } else {
+ IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
+ time, data, ev);
+ trace_iwlwifi_dev_ucode_event(trans->dev, time,
+ data, ev);
+ }
+ }
+ }
+
+ /* Allow device to power down */
+ iwl_release_nic_access(trans);
+out_unlock:
+ spin_unlock_irqrestore(&trans->reg_lock, reg_flags);
+ return pos;
+}
+
+/**
+ * iwl_print_last_event_logs - Dump the newest # of event log to syslog
+ */
+static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+ u32 num_wraps, u32 next_entry,
+ u32 size, u32 mode,
+ int pos, char **buf, size_t bufsz)
+{
+ /*
+ * display the newest DEFAULT_LOG_ENTRIES entries
+ * i.e the entries just before the next ont that uCode would fill.
+ */
+ if (num_wraps) {
+ if (next_entry < size) {
+ pos = iwl_print_event_log(priv,
+ capacity - (size - next_entry),
+ size - next_entry, mode,
+ pos, buf, bufsz);
+ pos = iwl_print_event_log(priv, 0,
+ next_entry, mode,
+ pos, buf, bufsz);
+ } else
+ pos = iwl_print_event_log(priv, next_entry - size,
+ size, mode, pos, buf, bufsz);
+ } else {
+ if (next_entry < size) {
+ pos = iwl_print_event_log(priv, 0, next_entry,
+ mode, pos, buf, bufsz);
+ } else {
+ pos = iwl_print_event_log(priv, next_entry - size,
+ size, mode, pos, buf, bufsz);
+ }
+ }
+ return pos;
+}
+
+#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
+
+int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+ char **buf, bool display)
+{
+ u32 base; /* SRAM byte address of event log header */
+ u32 capacity; /* event log capacity in # entries */
+ u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
+ u32 num_wraps; /* # times uCode wrapped to top of log */
+ u32 next_entry; /* index of next entry to be written by uCode */
+ u32 size; /* # entries that we'll print */
+ u32 logsize;
+ int pos = 0;
+ size_t bufsz = 0;
+ struct iwl_trans *trans = trans(priv);
+
+ base = priv->device_pointers.log_event_table;
+ if (priv->cur_ucode == IWL_UCODE_INIT) {
+ logsize = priv->fw->init_evtlog_size;
+ if (!base)
+ base = priv->fw->init_evtlog_ptr;
+ } else {
+ logsize = priv->fw->inst_evtlog_size;
+ if (!base)
+ base = priv->fw->inst_evtlog_ptr;
+ }
+
+ if (!iwlagn_hw_valid_rtc_data_addr(base)) {
+ IWL_ERR(priv,
+ "Invalid event log pointer 0x%08X for %s uCode\n",
+ base,
+ (priv->cur_ucode == IWL_UCODE_INIT)
+ ? "Init" : "RT");
+ return -EINVAL;
+ }
+
+ /* event log header */
+ capacity = iwl_read_targ_mem(trans, base);
+ mode = iwl_read_targ_mem(trans, base + (1 * sizeof(u32)));
+ num_wraps = iwl_read_targ_mem(trans, base + (2 * sizeof(u32)));
+ next_entry = iwl_read_targ_mem(trans, base + (3 * sizeof(u32)));
+
+ if (capacity > logsize) {
+ IWL_ERR(priv, "Log capacity %d is bogus, limit to %d "
+ "entries\n", capacity, logsize);
+ capacity = logsize;
+ }
+
+ if (next_entry > logsize) {
+ IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
+ next_entry, logsize);
+ next_entry = logsize;
+ }
+
+ size = num_wraps ? capacity : next_entry;
+
+ /* bail out if nothing in log */
+ if (size == 0) {
+ IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n");
+ return pos;
+ }
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log)
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+#else
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+#endif
+ IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
+ size);
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (display) {
+ if (full_log)
+ bufsz = capacity * 48;
+ else
+ bufsz = size * 48;
+ *buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+ }
+ if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) {
+ /*
+ * if uCode has wrapped back to top of log,
+ * start at the oldest entry,
+ * i.e the next one that uCode would fill.
+ */
+ if (num_wraps)
+ pos = iwl_print_event_log(priv, next_entry,
+ capacity - next_entry, mode,
+ pos, buf, bufsz);
+ /* (then/else) start at top of log */
+ pos = iwl_print_event_log(priv, 0,
+ next_entry, mode, pos, buf, bufsz);
+ } else
+ pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode,
+ pos, buf, bufsz);
+#else
+ pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode,
+ pos, buf, bufsz);
+#endif
+ return pos;
+}
+
+static void iwl_nic_error(struct iwl_op_mode *op_mode)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+ IWL_ERR(priv, "Loaded firmware version: %s\n",
+ priv->fw->fw_version);
+
+ iwl_dump_nic_error_log(priv);
+ iwl_dump_nic_event_log(priv, false, NULL, false);
+
+ iwlagn_fw_error(priv, false);
+}
+
static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode)
{
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
if (!iwl_check_for_ct_kill(priv)) {
IWL_ERR(priv, "Restarting adapter queue is full\n");
- iwl_nic_error(op_mode);
+ iwlagn_fw_error(priv, false);
}
}
@@ -1446,17 +2142,39 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode)
cfg(priv)->lib->nic_config(priv);
}
-static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, u8 ac)
+static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
{
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ int ac = priv->queue_to_ac[queue];
+
+ if (WARN_ON_ONCE(ac == IWL_INVALID_AC))
+ return;
+
+ if (atomic_inc_return(&priv->ac_stop_count[ac]) > 1) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "queue %d (AC %d) already stopped\n",
+ queue, ac);
+ return;
+ }
set_bit(ac, &priv->transport_queue_stop);
ieee80211_stop_queue(priv->hw, ac);
}
-static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, u8 ac)
+static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
{
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ int ac = priv->queue_to_ac[queue];
+
+ if (WARN_ON_ONCE(ac == IWL_INVALID_AC))
+ return;
+
+ if (atomic_dec_return(&priv->ac_stop_count[ac]) > 0) {
+ IWL_DEBUG_TX_QUEUES(priv,
+ "queue %d (AC %d) already awake\n",
+ queue, ac);
+ return;
+ }
clear_bit(ac, &priv->transport_queue_stop);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
index 3780a03f271..51001622430 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
@@ -65,6 +65,13 @@
#include "iwl-dev.h"
+/* The first 11 queues (0-10) are used otherwise */
+#define IWLAGN_FIRST_AMPDU_QUEUE 11
+
+/* AUX (TX during scan dwell) queue */
+#define IWL_AUX_QUEUE 10
+
+
struct iwl_ucode_capabilities;
extern struct ieee80211_ops iwlagn_hw_ops;
@@ -85,7 +92,6 @@ int __must_check iwl_rx_dispatch(struct iwl_op_mode *op_mode,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state);
-void iwl_nic_error(struct iwl_op_mode *op_mode);
bool iwl_check_for_ct_kill(struct iwl_priv *priv);
@@ -115,9 +121,6 @@ void iwlagn_config_ht40(struct ieee80211_conf *conf,
struct iwl_rxon_context *ctx);
/* uCode */
-int iwlagn_rx_calib_result(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb,
- struct iwl_device_cmd *cmd);
int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type);
void iwl_send_prio_tbl(struct iwl_priv *priv);
int iwl_init_alive_start(struct iwl_priv *priv);
@@ -128,6 +131,9 @@ int iwl_send_calib_results(struct iwl_priv *priv);
int iwl_calib_set(struct iwl_priv *priv,
const struct iwl_calib_hdr *cmd, int len);
void iwl_calib_free_results(struct iwl_priv *priv);
+void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand);
+int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+ char **buf, bool display);
/* lib */
int iwlagn_send_tx_power(struct iwl_priv *priv);
@@ -386,6 +392,15 @@ static inline int iwl_is_ready_rf(struct iwl_priv *priv)
return iwl_is_ready(priv);
}
+static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state)
+{
+ if (state)
+ set_bit(STATUS_POWER_PMI, &priv->status);
+ else
+ clear_bit(STATUS_POWER_PMI, &priv->status);
+ iwl_trans_set_pmi(trans(priv), state);
+}
+
#ifdef CONFIG_IWLWIFI_DEBUG
#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \
do { \
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 46490d3b95b..88ea31d9eb7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -28,7 +28,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/etherdevice.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <net/mac80211.h>
@@ -44,189 +43,6 @@
const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
-#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
-static void iwl_init_ht_hw_capab(const struct iwl_priv *priv,
- struct ieee80211_sta_ht_cap *ht_info,
- enum ieee80211_band band)
-{
- u16 max_bit_rate = 0;
- u8 rx_chains_num = hw_params(priv).rx_chains_num;
- u8 tx_chains_num = hw_params(priv).tx_chains_num;
-
- ht_info->cap = 0;
- memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
-
- ht_info->ht_supported = true;
-
- if (cfg(priv)->ht_params &&
- cfg(priv)->ht_params->ht_greenfield_support)
- ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
- ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
- max_bit_rate = MAX_BIT_RATE_20_MHZ;
- if (hw_params(priv).ht40_channel & BIT(band)) {
- ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
- ht_info->mcs.rx_mask[4] = 0x01;
- max_bit_rate = MAX_BIT_RATE_40_MHZ;
- }
-
- if (iwlagn_mod_params.amsdu_size_8K)
- ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
-
- ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF;
- ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF;
-
- ht_info->mcs.rx_mask[0] = 0xFF;
- if (rx_chains_num >= 2)
- ht_info->mcs.rx_mask[1] = 0xFF;
- if (rx_chains_num >= 3)
- ht_info->mcs.rx_mask[2] = 0xFF;
-
- /* Highest supported Rx data rate */
- max_bit_rate *= rx_chains_num;
- WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK);
- ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate);
-
- /* Tx MCS capabilities */
- ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
- if (tx_chains_num != rx_chains_num) {
- ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
- ht_info->mcs.tx_params |= ((tx_chains_num - 1) <<
- IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
- }
-}
-
-/**
- * iwl_init_geos - Initialize mac80211's geo/channel info based from eeprom
- */
-int iwl_init_geos(struct iwl_priv *priv)
-{
- struct iwl_channel_info *ch;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *channels;
- struct ieee80211_channel *geo_ch;
- struct ieee80211_rate *rates;
- int i = 0;
- s8 max_tx_power = IWLAGN_TX_POWER_TARGET_POWER_MIN;
-
- if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates ||
- priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) {
- IWL_DEBUG_INFO(priv, "Geography modes already initialized.\n");
- set_bit(STATUS_GEO_CONFIGURED, &priv->status);
- return 0;
- }
-
- channels = kcalloc(priv->channel_count,
- sizeof(struct ieee80211_channel), GFP_KERNEL);
- if (!channels)
- return -ENOMEM;
-
- rates = kcalloc(IWL_RATE_COUNT_LEGACY, sizeof(struct ieee80211_rate),
- GFP_KERNEL);
- if (!rates) {
- kfree(channels);
- return -ENOMEM;
- }
-
- /* 5.2GHz channels start after the 2.4GHz channels */
- sband = &priv->bands[IEEE80211_BAND_5GHZ];
- sband->channels = &channels[ARRAY_SIZE(iwl_eeprom_band_1)];
- /* just OFDM */
- sband->bitrates = &rates[IWL_FIRST_OFDM_RATE];
- sband->n_bitrates = IWL_RATE_COUNT_LEGACY - IWL_FIRST_OFDM_RATE;
-
- if (hw_params(priv).sku & EEPROM_SKU_CAP_11N_ENABLE)
- iwl_init_ht_hw_capab(priv, &sband->ht_cap,
- IEEE80211_BAND_5GHZ);
-
- sband = &priv->bands[IEEE80211_BAND_2GHZ];
- sband->channels = channels;
- /* OFDM & CCK */
- sband->bitrates = rates;
- sband->n_bitrates = IWL_RATE_COUNT_LEGACY;
-
- if (hw_params(priv).sku & EEPROM_SKU_CAP_11N_ENABLE)
- iwl_init_ht_hw_capab(priv, &sband->ht_cap,
- IEEE80211_BAND_2GHZ);
-
- priv->ieee_channels = channels;
- priv->ieee_rates = rates;
-
- for (i = 0; i < priv->channel_count; i++) {
- ch = &priv->channel_info[i];
-
- /* FIXME: might be removed if scan is OK */
- if (!is_channel_valid(ch))
- continue;
-
- sband = &priv->bands[ch->band];
-
- geo_ch = &sband->channels[sband->n_channels++];
-
- geo_ch->center_freq =
- ieee80211_channel_to_frequency(ch->channel, ch->band);
- geo_ch->max_power = ch->max_power_avg;
- geo_ch->max_antenna_gain = 0xff;
- geo_ch->hw_value = ch->channel;
-
- if (is_channel_valid(ch)) {
- if (!(ch->flags & EEPROM_CHANNEL_IBSS))
- geo_ch->flags |= IEEE80211_CHAN_NO_IBSS;
-
- if (!(ch->flags & EEPROM_CHANNEL_ACTIVE))
- geo_ch->flags |= IEEE80211_CHAN_PASSIVE_SCAN;
-
- if (ch->flags & EEPROM_CHANNEL_RADAR)
- geo_ch->flags |= IEEE80211_CHAN_RADAR;
-
- geo_ch->flags |= ch->ht40_extension_channel;
-
- if (ch->max_power_avg > max_tx_power)
- max_tx_power = ch->max_power_avg;
- } else {
- geo_ch->flags |= IEEE80211_CHAN_DISABLED;
- }
-
- IWL_DEBUG_INFO(priv, "Channel %d Freq=%d[%sGHz] %s flag=0x%X\n",
- ch->channel, geo_ch->center_freq,
- is_channel_a_band(ch) ? "5.2" : "2.4",
- geo_ch->flags & IEEE80211_CHAN_DISABLED ?
- "restricted" : "valid",
- geo_ch->flags);
- }
-
- priv->tx_power_device_lmt = max_tx_power;
- priv->tx_power_user_lmt = max_tx_power;
- priv->tx_power_next = max_tx_power;
-
- if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) &&
- hw_params(priv).sku & EEPROM_SKU_CAP_BAND_52GHZ) {
- IWL_INFO(priv, "Incorrectly detected BG card as ABG. "
- "Please send your %s to maintainer.\n",
- trans(priv)->hw_id_str);
- hw_params(priv).sku &= ~EEPROM_SKU_CAP_BAND_52GHZ;
- }
-
- IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n",
- priv->bands[IEEE80211_BAND_2GHZ].n_channels,
- priv->bands[IEEE80211_BAND_5GHZ].n_channels);
-
- set_bit(STATUS_GEO_CONFIGURED, &priv->status);
-
- return 0;
-}
-
-/*
- * iwl_free_geos - undo allocations in iwl_init_geos
- */
-void iwl_free_geos(struct iwl_priv *priv)
-{
- kfree(priv->ieee_channels);
- kfree(priv->ieee_rates);
- clear_bit(STATUS_GEO_CONFIGURED, &priv->status);
-}
-
static bool iwl_is_channel_extension(struct iwl_priv *priv,
enum ieee80211_band band,
u16 channel, u8 extension_chan_offset)
@@ -271,255 +87,6 @@ bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
ctx->ht.extension_chan_offset);
}
-static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
-{
- u16 new_val;
- u16 beacon_factor;
-
- /*
- * If mac80211 hasn't given us a beacon interval, program
- * the default into the device (not checking this here
- * would cause the adjustment below to return the maximum
- * value, which may break PAN.)
- */
- if (!beacon_val)
- return DEFAULT_BEACON_INTERVAL;
-
- /*
- * If the beacon interval we obtained from the peer
- * is too large, we'll have to wake up more often
- * (and in IBSS case, we'll beacon too much)
- *
- * For example, if max_beacon_val is 4096, and the
- * requested beacon interval is 7000, we'll have to
- * use 3500 to be able to wake up on the beacons.
- *
- * This could badly influence beacon detection stats.
- */
-
- beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val;
- new_val = beacon_val / beacon_factor;
-
- if (!new_val)
- new_val = max_beacon_val;
-
- return new_val;
-}
-
-int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
- u64 tsf;
- s32 interval_tm, rem;
- struct ieee80211_conf *conf = NULL;
- u16 beacon_int;
- struct ieee80211_vif *vif = ctx->vif;
-
- conf = &priv->hw->conf;
-
- lockdep_assert_held(&priv->mutex);
-
- memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd));
-
- ctx->timing.timestamp = cpu_to_le64(priv->timestamp);
- ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval);
-
- beacon_int = vif ? vif->bss_conf.beacon_int : 0;
-
- /*
- * TODO: For IBSS we need to get atim_window from mac80211,
- * for now just always use 0
- */
- ctx->timing.atim_window = 0;
-
- if (ctx->ctxid == IWL_RXON_CTX_PAN &&
- (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) &&
- iwl_is_associated(priv, IWL_RXON_CTX_BSS) &&
- priv->contexts[IWL_RXON_CTX_BSS].vif &&
- priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) {
- ctx->timing.beacon_interval =
- priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval;
- beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
- } else if (ctx->ctxid == IWL_RXON_CTX_BSS &&
- iwl_is_associated(priv, IWL_RXON_CTX_PAN) &&
- priv->contexts[IWL_RXON_CTX_PAN].vif &&
- priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int &&
- (!iwl_is_associated_ctx(ctx) || !ctx->vif ||
- !ctx->vif->bss_conf.beacon_int)) {
- ctx->timing.beacon_interval =
- priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval;
- beacon_int = le16_to_cpu(ctx->timing.beacon_interval);
- } else {
- beacon_int = iwl_adjust_beacon_interval(beacon_int,
- IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT);
- ctx->timing.beacon_interval = cpu_to_le16(beacon_int);
- }
-
- ctx->beacon_int = beacon_int;
-
- tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */
- interval_tm = beacon_int * TIME_UNIT;
- rem = do_div(tsf, interval_tm);
- ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
-
- ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1;
-
- IWL_DEBUG_ASSOC(priv,
- "beacon interval %d beacon timer %d beacon tim %d\n",
- le16_to_cpu(ctx->timing.beacon_interval),
- le32_to_cpu(ctx->timing.beacon_init_val),
- le16_to_cpu(ctx->timing.atim_window));
-
- return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd,
- CMD_SYNC, sizeof(ctx->timing), &ctx->timing);
-}
-
-void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- int hw_decrypt)
-{
- struct iwl_rxon_cmd *rxon = &ctx->staging;
-
- if (hw_decrypt)
- rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK;
- else
- rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK;
-
-}
-
-/* validate RXON structure is valid */
-int iwl_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
-{
- struct iwl_rxon_cmd *rxon = &ctx->staging;
- u32 errors = 0;
-
- if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
- if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) {
- IWL_WARN(priv, "check 2.4G: wrong narrow\n");
- errors |= BIT(0);
- }
- if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) {
- IWL_WARN(priv, "check 2.4G: wrong radar\n");
- errors |= BIT(1);
- }
- } else {
- if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) {
- IWL_WARN(priv, "check 5.2G: not short slot!\n");
- errors |= BIT(2);
- }
- if (rxon->flags & RXON_FLG_CCK_MSK) {
- IWL_WARN(priv, "check 5.2G: CCK!\n");
- errors |= BIT(3);
- }
- }
- if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) {
- IWL_WARN(priv, "mac/bssid mcast!\n");
- errors |= BIT(4);
- }
-
- /* make sure basic rates 6Mbps and 1Mbps are supported */
- if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 &&
- (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) {
- IWL_WARN(priv, "neither 1 nor 6 are basic\n");
- errors |= BIT(5);
- }
-
- if (le16_to_cpu(rxon->assoc_id) > 2007) {
- IWL_WARN(priv, "aid > 2007\n");
- errors |= BIT(6);
- }
-
- if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK))
- == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) {
- IWL_WARN(priv, "CCK and short slot\n");
- errors |= BIT(7);
- }
-
- if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
- == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
- IWL_WARN(priv, "CCK and auto detect");
- errors |= BIT(8);
- }
-
- if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK |
- RXON_FLG_TGG_PROTECT_MSK)) ==
- RXON_FLG_TGG_PROTECT_MSK) {
- IWL_WARN(priv, "TGg but no auto-detect\n");
- errors |= BIT(9);
- }
-
- if (rxon->channel == 0) {
- IWL_WARN(priv, "zero channel is invalid\n");
- errors |= BIT(10);
- }
-
- WARN(errors, "Invalid RXON (%#x), channel %d",
- errors, le16_to_cpu(rxon->channel));
-
- return errors ? -EINVAL : 0;
-}
-
-/**
- * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
- * @priv: staging_rxon is compared to active_rxon
- *
- * If the RXON structure is changing enough to require a new tune,
- * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that
- * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required.
- */
-int iwl_full_rxon_required(struct iwl_priv *priv,
- struct iwl_rxon_context *ctx)
-{
- const struct iwl_rxon_cmd *staging = &ctx->staging;
- const struct iwl_rxon_cmd *active = &ctx->active;
-
-#define CHK(cond) \
- if ((cond)) { \
- IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \
- return 1; \
- }
-
-#define CHK_NEQ(c1, c2) \
- if ((c1) != (c2)) { \
- IWL_DEBUG_INFO(priv, "need full RXON - " \
- #c1 " != " #c2 " - %d != %d\n", \
- (c1), (c2)); \
- return 1; \
- }
-
- /* These items are only settable from the full RXON command */
- CHK(!iwl_is_associated_ctx(ctx));
- CHK(compare_ether_addr(staging->bssid_addr, active->bssid_addr));
- CHK(compare_ether_addr(staging->node_addr, active->node_addr));
- CHK(compare_ether_addr(staging->wlap_bssid_addr,
- active->wlap_bssid_addr));
- CHK_NEQ(staging->dev_type, active->dev_type);
- CHK_NEQ(staging->channel, active->channel);
- CHK_NEQ(staging->air_propagation, active->air_propagation);
- CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates,
- active->ofdm_ht_single_stream_basic_rates);
- CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates,
- active->ofdm_ht_dual_stream_basic_rates);
- CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates,
- active->ofdm_ht_triple_stream_basic_rates);
- CHK_NEQ(staging->assoc_id, active->assoc_id);
-
- /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can
- * be updated with the RXON_ASSOC command -- however only some
- * flag transitions are allowed using RXON_ASSOC */
-
- /* Check if we are not switching bands */
- CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK,
- active->flags & RXON_FLG_BAND_24G_MSK);
-
- /* Check if we are switching association toggle */
- CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK,
- active->filter_flags & RXON_FILTER_ASSOC_MSK);
-
-#undef CHK
-#undef CHK_NEQ
-
- return 0;
-}
-
static void _iwl_set_rxon_ht(struct iwl_priv *priv,
struct iwl_ht_config *ht_conf,
struct iwl_rxon_context *ctx)
@@ -595,46 +162,6 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf)
_iwl_set_rxon_ht(priv, ht_conf, ctx);
}
-/* Return valid, unused, channel for a passive scan to reset the RF */
-u8 iwl_get_single_channel_number(struct iwl_priv *priv,
- enum ieee80211_band band)
-{
- const struct iwl_channel_info *ch_info;
- int i;
- u8 channel = 0;
- u8 min, max;
- struct iwl_rxon_context *ctx;
-
- if (band == IEEE80211_BAND_5GHZ) {
- min = 14;
- max = priv->channel_count;
- } else {
- min = 0;
- max = 14;
- }
-
- for (i = min; i < max; i++) {
- bool busy = false;
-
- for_each_context(priv, ctx) {
- busy = priv->channel_info[i].channel ==
- le16_to_cpu(ctx->staging.channel);
- if (busy)
- break;
- }
-
- if (busy)
- continue;
-
- channel = priv->channel_info[i].channel;
- ch_info = iwl_get_channel_info(priv, band, channel);
- if (is_channel_valid(ch_info))
- break;
- }
-
- return channel;
-}
-
/**
* iwl_set_rxon_channel - Set the band and channel values in staging RXON
* @ch: requested channel as a pointer to struct ieee80211_channel
@@ -828,7 +355,7 @@ void iwl_print_rx_config_cmd(struct iwl_priv *priv,
}
#endif
-static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
+void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
{
unsigned int reload_msec;
unsigned long reload_jiffies;
@@ -842,7 +369,7 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
priv->ucode_loaded = false;
/* Set the FW error flag -- cleared on iwl_down */
- set_bit(STATUS_FW_ERROR, &priv->shrd->status);
+ set_bit(STATUS_FW_ERROR, &priv->status);
/* Cancel currently queued command. */
clear_bit(STATUS_HCMD_ACTIVE, &priv->shrd->status);
@@ -1451,13 +978,6 @@ __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base,
return cpu_to_le32(res);
}
-void iwl_nic_error(struct iwl_op_mode *op_mode)
-{
- struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
-
- iwlagn_fw_error(priv, false);
-}
-
void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
{
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 635eb685ede..7aa3060fc6b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -93,18 +93,12 @@ struct iwl_lib_ops {
* L i b *
***************************/
-void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
- int hw_decrypt);
-int iwl_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
-int iwl_full_rxon_required(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch,
struct iwl_rxon_context *ctx);
void iwl_set_flags_for_band(struct iwl_priv *priv,
struct iwl_rxon_context *ctx,
enum ieee80211_band band,
struct ieee80211_vif *vif);
-u8 iwl_get_single_channel_number(struct iwl_priv *priv,
- enum ieee80211_band band);
void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf);
bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
struct iwl_rxon_context *ctx,
@@ -204,19 +198,10 @@ u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval);
__le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base,
u32 addon, u32 beacon_interval);
-
-/*****************************************************
-* GEOS
-******************************************************/
-int iwl_init_geos(struct iwl_priv *priv);
-void iwl_free_geos(struct iwl_priv *priv);
-
extern void iwl_send_bt_config(struct iwl_priv *priv);
extern int iwl_send_statistics_request(struct iwl_priv *priv,
u8 flags, bool clear);
-int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx);
-
static inline const struct ieee80211_supported_band *iwl_get_hw_mode(
struct iwl_priv *priv, enum ieee80211_band band)
{
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 2bbaebd99ad..d109d1bbb3a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -234,7 +234,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
IWL_ERR(priv, "No uCode has been loadded.\n");
return -EINVAL;
}
- img = &priv->fw->img[priv->shrd->ucode_type];
+ img = &priv->fw->img[priv->cur_ucode];
priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
}
len = priv->dbgfs_sram_len;
@@ -369,14 +369,19 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
i, station->sta.sta.addr,
station->sta.station_flags_msk);
pos += scnprintf(buf + pos, bufsz - pos,
- "TID\tseq_num\trate_n_flags\n");
+ "TID seqno next_rclmd "
+ "rate_n_flags state txq\n");
for (j = 0; j < IWL_MAX_TID_COUNT; j++) {
tid_data = &priv->tid_data[i][j];
pos += scnprintf(buf + pos, bufsz - pos,
- "%d:\t%#x\t%#x",
+ "%d: 0x%.4x 0x%.4x 0x%.8x "
+ "%d %.2d",
j, tid_data->seq_number,
- tid_data->agg.rate_n_flags);
+ tid_data->next_reclaimed,
+ tid_data->agg.rate_n_flags,
+ tid_data->agg.state,
+ tid_data->agg.txq_id);
if (tid_data->agg.wait_for_ba)
pos += scnprintf(buf + pos, bufsz - pos,
@@ -544,9 +549,9 @@ static ssize_t iwl_dbgfs_status_read(struct file *file,
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n",
test_bit(STATUS_SCAN_HW, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n",
- test_bit(STATUS_POWER_PMI, &priv->shrd->status));
+ test_bit(STATUS_POWER_PMI, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n",
- test_bit(STATUS_FW_ERROR, &priv->shrd->status));
+ test_bit(STATUS_FW_ERROR, &priv->status));
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
@@ -2473,6 +2478,44 @@ static ssize_t iwl_dbgfs_echo_test_write(struct file *file,
return count;
}
+static ssize_t iwl_dbgfs_log_event_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char *buf;
+ int pos = 0;
+ ssize_t ret = -ENOMEM;
+
+ ret = pos = iwl_dump_nic_event_log(priv, true, &buf, true);
+ if (buf) {
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ }
+ return ret;
+}
+
+static ssize_t iwl_dbgfs_log_event_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ u32 event_log_flag;
+ char buf[8];
+ int buf_size;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &event_log_flag) != 1)
+ return -EFAULT;
+ if (event_log_flag == 1)
+ iwl_dump_nic_event_log(priv, true, NULL, false);
+
+ return count;
+}
+
DEBUGFS_READ_FILE_OPS(rx_statistics);
DEBUGFS_READ_FILE_OPS(tx_statistics);
DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
@@ -2497,6 +2540,7 @@ DEBUGFS_READ_FILE_OPS(bt_traffic);
DEBUGFS_READ_WRITE_FILE_OPS(protection_mode);
DEBUGFS_READ_FILE_OPS(reply_tx_error);
DEBUGFS_WRITE_FILE_OPS(echo_test);
+DEBUGFS_READ_WRITE_FILE_OPS(log_event);
/*
* Create the debugfs files and directories
@@ -2560,6 +2604,8 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR);
DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR);
+
if (iwl_advanced_bt_coexist(priv))
DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 16956b777f9..99be58940e2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -220,8 +220,7 @@ enum iwl_agg_state {
* Tx response (REPLY_TX), and the block ack notification
* (REPLY_COMPRESSED_BA).
* @state: state of the BA agreement establishment / tear down.
- * @txq_id: Tx queue used by the BA session - used by the transport layer.
- * Needed by the upper layer for debugfs only.
+ * @txq_id: Tx queue used by the BA session
* @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
* the first packet to be sent in legacy HW queue in Tx AGG stop flow.
* Basically when next_reclaimed reaches ssn, we can tell mac80211 that
@@ -623,6 +622,10 @@ struct iwl_force_reset {
struct iwl_rxon_context {
struct ieee80211_vif *vif;
+ u8 mcast_queue;
+ u8 ac_to_queue[IEEE80211_NUM_ACS];
+ u8 ac_to_fifo[IEEE80211_NUM_ACS];
+
/*
* We could use the vif to indicate active, but we
* also need it to be active during disabling when
@@ -720,6 +723,11 @@ struct iwl_priv {
unsigned long transport_queue_stop;
bool passive_no_rx;
+#define IWL_INVALID_AC 0xff
+ u8 queue_to_ac[IWL_MAX_HW_QUEUES];
+ atomic_t ac_stop_count[IEEE80211_NUM_ACS];
+
+ unsigned long agg_q_alloc[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
/* ieee device used by generic ieee processing code */
struct ieee80211_hw *hw;
@@ -731,6 +739,7 @@ struct iwl_priv {
struct workqueue_struct *workqueue;
enum ieee80211_band band;
+ u8 valid_contexts;
void (*pre_rx_handler)(struct iwl_priv *priv,
struct iwl_rx_cmd_buffer *rxb);
@@ -982,6 +991,15 @@ struct iwl_priv {
__le64 replay_ctr;
__le16 last_seq_ctl;
bool have_rekey_data;
+
+ /* device_pointers: pointers to ucode event tables */
+ struct {
+ u32 error_event_table;
+ u32 log_event_table;
+ } device_pointers;
+
+ /* indicator of loaded ucode image */
+ enum iwl_ucode_type cur_ucode;
}; /*iwl_priv */
extern struct kmem_cache *iwl_tx_cmd_pool;
@@ -998,7 +1016,7 @@ iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif)
#define for_each_context(priv, ctx) \
for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \
ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \
- if (priv->shrd->valid_contexts & BIT(ctx->ctxid))
+ if (priv->valid_contexts & BIT(ctx->ctxid))
static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx)
{
diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c
index b6805f8e9a0..f1c675a2a6f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c
+++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c
@@ -157,7 +157,8 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
*/
hw->flags |= IEEE80211_HW_SUPPORTS_PS |
- IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+ IEEE80211_HW_SCAN_WHILE_IDLE;
if (hw_params(priv).sku & EEPROM_SKU_CAP_11N_ENABLE)
hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
@@ -437,7 +438,6 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
unsigned long flags;
u32 base, status = 0xffffffff;
int ret = -EIO;
- const struct fw_img *img;
IWL_DEBUG_MAC80211(priv, "enter\n");
mutex_lock(&priv->mutex);
@@ -445,7 +445,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
iwl_write32(trans(priv), CSR_UCODE_DRV_GP1_CLR,
CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
- base = priv->shrd->device_pointers.error_event_table;
+ base = priv->device_pointers.error_event_table;
if (iwlagn_hw_valid_rtc_data_addr(base)) {
spin_lock_irqsave(&trans(priv)->reg_lock, flags);
ret = iwl_grab_nic_access_silent(trans(priv));
@@ -458,6 +458,8 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (ret == 0) {
+ const struct fw_img *img;
+
img = &(priv->fw->img[IWL_UCODE_WOWLAN]);
if (!priv->wowlan_sram) {
priv->wowlan_sram =
@@ -653,6 +655,8 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
ret = iwl_sta_rx_agg_stop(priv, sta, tid);
break;
case IEEE80211_AMPDU_TX_START:
+ if (!trans(priv)->ops->tx_agg_setup)
+ break;
if (iwlagn_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG)
break;
IWL_DEBUG_HT(priv, "start Tx\n");
@@ -1003,7 +1007,7 @@ static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw,
struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
int err = 0;
- if (!(priv->shrd->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
+ if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
return -EOPNOTSUPP;
if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)))
@@ -1091,7 +1095,7 @@ static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- if (!(priv->shrd->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
+ if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
return -EOPNOTSUPP;
IWL_DEBUG_MAC80211(priv, "enter\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
index 88dc4a0f96b..0066b899fe5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
+++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
@@ -75,21 +75,45 @@ void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait)
void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait,
struct iwl_rx_packet *pkt)
{
+ bool triggered = false;
+
if (!list_empty(&notif_wait->notif_waits)) {
struct iwl_notification_wait *w;
spin_lock(&notif_wait->notif_wait_lock);
list_for_each_entry(w, &notif_wait->notif_waits, list) {
- if (w->cmd != pkt->hdr.cmd)
+ int i;
+ bool found = false;
+
+ /*
+ * If it already finished (triggered) or has been
+ * aborted then don't evaluate it again to avoid races,
+ * Otherwise the function could be called again even
+ * though it returned true before
+ */
+ if (w->triggered || w->aborted)
+ continue;
+
+ for (i = 0; i < w->n_cmds; i++) {
+ if (w->cmds[i] == pkt->hdr.cmd) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
continue;
- w->triggered = true;
- if (w->fn)
- w->fn(notif_wait, pkt, w->fn_data);
+
+ if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) {
+ w->triggered = true;
+ triggered = true;
+ }
}
spin_unlock(&notif_wait->notif_wait_lock);
- wake_up_all(&notif_wait->notif_waitq);
}
+
+ if (triggered)
+ wake_up_all(&notif_wait->notif_waitq);
}
void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait)
@@ -109,14 +133,18 @@ void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait)
void
iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait,
struct iwl_notification_wait *wait_entry,
- u8 cmd,
- void (*fn)(struct iwl_notif_wait_data *notif_wait,
+ const u8 *cmds, int n_cmds,
+ bool (*fn)(struct iwl_notif_wait_data *notif_wait,
struct iwl_rx_packet *pkt, void *data),
void *fn_data)
{
+ if (WARN_ON(n_cmds > MAX_NOTIF_CMDS))
+ n_cmds = MAX_NOTIF_CMDS;
+
wait_entry->fn = fn;
wait_entry->fn_data = fn_data;
- wait_entry->cmd = cmd;
+ wait_entry->n_cmds = n_cmds;
+ memcpy(wait_entry->cmds, cmds, n_cmds);
wait_entry->triggered = false;
wait_entry->aborted = false;
diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
index 5e8af957aa7..821523100cf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
+++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
@@ -72,11 +72,19 @@ struct iwl_notif_wait_data {
wait_queue_head_t notif_waitq;
};
+#define MAX_NOTIF_CMDS 5
+
/**
* struct iwl_notification_wait - notification wait entry
* @list: list head for global list
- * @fn: function called with the notification
- * @cmd: command ID
+ * @fn: Function called with the notification. If the function
+ * returns true, the wait is over, if it returns false then
+ * the waiter stays blocked. If no function is given, any
+ * of the listed commands will unblock the waiter.
+ * @cmds: command IDs
+ * @n_cmds: number of command IDs
+ * @triggered: waiter should be woken up
+ * @aborted: wait was aborted
*
* This structure is not used directly, to wait for a
* notification declare it on the stack, and call
@@ -93,11 +101,12 @@ struct iwl_notif_wait_data {
struct iwl_notification_wait {
struct list_head list;
- void (*fn)(struct iwl_notif_wait_data *notif_data,
+ bool (*fn)(struct iwl_notif_wait_data *notif_data,
struct iwl_rx_packet *pkt, void *data);
void *fn_data;
- u8 cmd;
+ u8 cmds[MAX_NOTIF_CMDS];
+ u8 n_cmds;
bool triggered, aborted;
};
@@ -112,8 +121,8 @@ void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data);
void __acquires(wait_entry)
iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data,
struct iwl_notification_wait *wait_entry,
- u8 cmd,
- void (*fn)(struct iwl_notif_wait_data *notif_data,
+ const u8 *cmds, int n_cmds,
+ bool (*fn)(struct iwl_notif_wait_data *notif_data,
struct iwl_rx_packet *pkt, void *data),
void *fn_data);
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
index 6ea4163ff56..b1fd251e88d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
@@ -111,10 +111,10 @@ struct iwl_fw;
* @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
* HCMD the this Rx responds to.
* Must be atomic.
- * @queue_full: notifies that a HW queue is full. Ac is the ac of the queue
+ * @queue_full: notifies that a HW queue is full.
* Must be atomic
* @queue_not_full: notifies that a HW queue is not full any more.
- * Ac is the ac of the queue. Must be atomic
+ * Must be atomic
* @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that
* the radio is killed. Must be atomic.
* @free_skb: allows the transport layer to free skbs that haven't been
@@ -132,8 +132,8 @@ struct iwl_op_mode_ops {
void (*stop)(struct iwl_op_mode *op_mode);
int (*rx)(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
- void (*queue_full)(struct iwl_op_mode *op_mode, u8 ac);
- void (*queue_not_full)(struct iwl_op_mode *op_mode, u8 ac);
+ void (*queue_full)(struct iwl_op_mode *op_mode, int queue);
+ void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue);
void (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb);
void (*nic_error)(struct iwl_op_mode *op_mode);
@@ -169,15 +169,16 @@ static inline int iwl_op_mode_rx(struct iwl_op_mode *op_mode,
return op_mode->ops->rx(op_mode, rxb, cmd);
}
-static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode, u8 ac)
+static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode,
+ int queue)
{
- op_mode->ops->queue_full(op_mode, ac);
+ op_mode->ops->queue_full(op_mode, queue);
}
static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode,
- u8 ac)
+ int queue)
{
- op_mode->ops->queue_not_full(op_mode, ac);
+ op_mode->ops->queue_not_full(op_mode, queue);
}
static inline void iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode,
diff --git a/drivers/net/wireless/iwlwifi/iwl-pci.c b/drivers/net/wireless/iwlwifi/iwl-pci.c
index c5e339ee918..f3e56b04d77 100644
--- a/drivers/net/wireless/iwlwifi/iwl-pci.c
+++ b/drivers/net/wireless/iwlwifi/iwl-pci.c
@@ -60,6 +60,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci-aspm.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
new file mode 100644
index 00000000000..d65305d08eb
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
@@ -0,0 +1,273 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License 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 Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "iwl-debug.h"
+#include "iwl-shared.h"
+#include "iwl-dev.h"
+
+#include "iwl-phy-db.h"
+
+#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */
+
+struct iwl_phy_db *iwl_phy_db_init(struct iwl_shared *shrd)
+{
+ struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
+ GFP_KERNEL);
+
+ if (!phy_db)
+ return phy_db;
+
+ phy_db->shrd = shrd;
+
+ /* TODO: add default values of the phy db. */
+ return phy_db;
+}
+
+/*
+ * get phy db section: returns a pointer to a phy db section specified by
+ * type and channel group id.
+ */
+static struct iwl_phy_db_entry *
+iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type,
+ u16 chg_id)
+{
+ if (!phy_db || type < 0 || type >= IWL_PHY_DB_MAX)
+ return NULL;
+
+ switch (type) {
+ case IWL_PHY_DB_CFG:
+ return &phy_db->cfg;
+ case IWL_PHY_DB_CALIB_NCH:
+ return &phy_db->calib_nch;
+ case IWL_PHY_DB_CALIB_CH:
+ return &phy_db->calib_ch;
+ case IWL_PHY_DB_CALIB_CHG_PAPD:
+ if (chg_id < 0 || chg_id >= IWL_NUM_PAPD_CH_GROUPS)
+ return NULL;
+ return &phy_db->calib_ch_group_papd[chg_id];
+ case IWL_PHY_DB_CALIB_CHG_TXP:
+ if (chg_id < 0 || chg_id >= IWL_NUM_TXP_CH_GROUPS)
+ return NULL;
+ return &phy_db->calib_ch_group_txp[chg_id];
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type,
+ u16 chg_id)
+{
+ struct iwl_phy_db_entry *entry =
+ iwl_phy_db_get_section(phy_db, type, chg_id);
+ if (!entry)
+ return;
+
+ kfree(entry->data);
+ entry->data = NULL;
+ entry->size = 0;
+}
+
+void iwl_phy_db_free(struct iwl_phy_db *phy_db)
+{
+ int i;
+
+ if (!phy_db)
+ return;
+
+ iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
+ iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
+ iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0);
+ for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++)
+ iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
+ for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++)
+ iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
+
+ kfree(phy_db);
+}
+
+int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type, u8 *data,
+ u16 size, gfp_t alloc_ctx)
+{
+ struct iwl_phy_db_entry *entry;
+ u16 chg_id = 0;
+
+ if (!phy_db)
+ return -EINVAL;
+
+ if (type == IWL_PHY_DB_CALIB_CHG_PAPD ||
+ type == IWL_PHY_DB_CALIB_CHG_TXP)
+ chg_id = le16_to_cpup((__le16 *)data);
+
+ entry = iwl_phy_db_get_section(phy_db, type, chg_id);
+ if (!entry)
+ return -EINVAL;
+
+ kfree(entry->data);
+ entry->data = kmemdup(data, size, alloc_ctx);
+ if (!entry->data) {
+ entry->size = 0;
+ return -ENOMEM;
+ }
+
+ entry->size = size;
+
+ if (type == IWL_PHY_DB_CALIB_CH) {
+ phy_db->channel_num = le32_to_cpup((__le32 *)data);
+ phy_db->channel_size =
+ (size - CHANNEL_NUM_SIZE) / phy_db->channel_num;
+ }
+
+ return 0;
+}
+
+static int is_valid_channel(u16 ch_id)
+{
+ if (ch_id <= 14 ||
+ (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
+ (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
+ (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
+ return 1;
+ return 0;
+}
+
+static u8 ch_id_to_ch_index(u16 ch_id)
+{
+ if (WARN_ON(!is_valid_channel(ch_id)))
+ return 0xff;
+
+ if (ch_id <= 14)
+ return ch_id - 1;
+ if (ch_id <= 64)
+ return (ch_id + 20) / 4;
+ if (ch_id <= 140)
+ return (ch_id - 12) / 4;
+ return (ch_id - 13) / 4;
+}
+
+
+static u16 channel_id_to_papd(u16 ch_id)
+{
+ if (WARN_ON(!is_valid_channel(ch_id)))
+ return 0xff;
+
+ if (1 <= ch_id && ch_id <= 14)
+ return 0;
+ if (36 <= ch_id && ch_id <= 64)
+ return 1;
+ if (100 <= ch_id && ch_id <= 140)
+ return 2;
+ return 3;
+}
+
+static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
+{
+ /* TODO David*/
+ return 0;
+}
+
+int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type, u8 **data,
+ u16 *size, u16 ch_id)
+{
+ struct iwl_phy_db_entry *entry;
+ u32 channel_num;
+ u32 channel_size;
+ u16 ch_group_id = 0;
+ u16 index;
+
+ if (!phy_db)
+ return -EINVAL;
+
+ /* find wanted channel group */
+ if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
+ ch_group_id = channel_id_to_papd(ch_id);
+ else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
+ ch_group_id = channel_id_to_txp(phy_db, ch_id);
+
+ entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
+ if (!entry)
+ return -EINVAL;
+
+ if (type == IWL_PHY_DB_CALIB_CH) {
+ index = ch_id_to_ch_index(ch_id);
+ channel_num = phy_db->channel_num;
+ channel_size = phy_db->channel_size;
+ if (index >= channel_num) {
+ IWL_ERR(phy_db, "Wrong channel number %d", ch_id);
+ return -EINVAL;
+ }
+ *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size;
+ *size = channel_size;
+ } else {
+ *data = entry->data;
+ *size = entry->size;
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h
new file mode 100644
index 00000000000..ba91a8b2839
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.h
@@ -0,0 +1,123 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License 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 Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_PHYDB_H__
+#define __IWL_PHYDB_H__
+
+#include <linux/types.h>
+
+#define IWL_NUM_PAPD_CH_GROUPS 4
+#define IWL_NUM_TXP_CH_GROUPS 8
+
+struct iwl_phy_db_entry {
+ u16 size;
+ u8 *data;
+};
+
+struct iwl_shared;
+
+/**
+ * struct iwl_phy_db - stores phy configuration and calibration data.
+ *
+ * @cfg: phy configuration.
+ * @calib_nch: non channel specific calibration data.
+ * @calib_ch: channel specific calibration data.
+ * @calib_ch_group_papd: calibration data related to papd channel group.
+ * @calib_ch_group_txp: calibration data related to tx power chanel group.
+ */
+struct iwl_phy_db {
+ struct iwl_phy_db_entry cfg;
+ struct iwl_phy_db_entry calib_nch;
+ struct iwl_phy_db_entry calib_ch;
+ struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS];
+ struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS];
+
+ u32 channel_num;
+ u32 channel_size;
+
+ /* for an access to the logger */
+ const struct iwl_shared *shrd;
+};
+
+enum iwl_phy_db_section_type {
+ IWL_PHY_DB_CFG = 1,
+ IWL_PHY_DB_CALIB_NCH,
+ IWL_PHY_DB_CALIB_CH,
+ IWL_PHY_DB_CALIB_CHG_PAPD,
+ IWL_PHY_DB_CALIB_CHG_TXP,
+ IWL_PHY_DB_MAX
+};
+
+struct iwl_phy_db *iwl_phy_db_init(struct iwl_shared *shrd);
+
+void iwl_phy_db_free(struct iwl_phy_db *phy_db);
+
+int iwl_phy_db_set_section(struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type, u8 *data,
+ u16 size, gfp_t alloc_ctx);
+
+int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
+ enum iwl_phy_db_section_type type, u8 **data,
+ u16 *size, u16 ch_id);
+
+#endif /* __IWL_PHYDB_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c
index 958d9d09aee..7bc7a82aba4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.c
+++ b/drivers/net/wireless/iwlwifi/iwl-power.c
@@ -403,12 +403,12 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
}
if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)
- set_bit(STATUS_POWER_PMI, &priv->shrd->status);
+ iwl_dvm_set_pmi(priv, true);
ret = iwl_set_power(priv, cmd);
if (!ret) {
if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK))
- clear_bit(STATUS_POWER_PMI, &priv->shrd->status);
+ iwl_dvm_set_pmi(priv, false);
if (update_chains)
iwl_update_chain_flags(priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index 902efe4bc89..dcf5b12071b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -69,7 +69,7 @@ static int iwl_send_scan_abort(struct iwl_priv *priv)
if (!test_bit(STATUS_READY, &priv->status) ||
!test_bit(STATUS_GEO_CONFIGURED, &priv->status) ||
!test_bit(STATUS_SCAN_HW, &priv->status) ||
- test_bit(STATUS_FW_ERROR, &priv->shrd->status))
+ test_bit(STATUS_FW_ERROR, &priv->status))
return -EIO;
ret = iwl_dvm_send_cmd(priv, &cmd);
@@ -451,6 +451,46 @@ static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv,
return iwl_limit_dwell(priv, passive);
}
+/* Return valid, unused, channel for a passive scan to reset the RF */
+static u8 iwl_get_single_channel_number(struct iwl_priv *priv,
+ enum ieee80211_band band)
+{
+ const struct iwl_channel_info *ch_info;
+ int i;
+ u8 channel = 0;
+ u8 min, max;
+ struct iwl_rxon_context *ctx;
+
+ if (band == IEEE80211_BAND_5GHZ) {
+ min = 14;
+ max = priv->channel_count;
+ } else {
+ min = 0;
+ max = 14;
+ }
+
+ for (i = min; i < max; i++) {
+ bool busy = false;
+
+ for_each_context(priv, ctx) {
+ busy = priv->channel_info[i].channel ==
+ le16_to_cpu(ctx->staging.channel);
+ if (busy)
+ break;
+ }
+
+ if (busy)
+ continue;
+
+ channel = priv->channel_info[i].channel;
+ ch_info = iwl_get_channel_info(priv, band, channel);
+ if (is_channel_valid(ch_info))
+ break;
+ }
+
+ return channel;
+}
+
static int iwl_get_single_channel_for_scan(struct iwl_priv *priv,
struct ieee80211_vif *vif,
enum ieee80211_band band,
@@ -793,9 +833,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
band = priv->scan_band;
- if (cfg(priv)->scan_rx_antennas[band])
- rx_ant = cfg(priv)->scan_rx_antennas[band];
-
if (band == IEEE80211_BAND_2GHZ &&
cfg(priv)->bt_params &&
cfg(priv)->bt_params->advanced_bt_coexist) {
@@ -809,8 +846,12 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
- /* In power save mode use one chain, otherwise use all chains */
- if (test_bit(STATUS_POWER_PMI, &priv->shrd->status)) {
+ /*
+ * In power save mode while associated use one chain,
+ * otherwise use all chains
+ */
+ if (test_bit(STATUS_POWER_PMI, &priv->status) &&
+ !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) {
/* rx_ant has been set to all valid chains previously */
active_chains = rx_ant &
((u8)(priv->chain_noise_data.active_chains));
diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h
index b515d657a0a..983b41e43d4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-shared.h
+++ b/drivers/net/wireless/iwlwifi/iwl-shared.h
@@ -160,7 +160,6 @@ struct iwl_mod_params {
*
* Holds the module parameters
*
- * @num_ampdu_queues: num of ampdu queues
* @tx_chains_num: Number of TX chains
* @rx_chains_num: Number of RX chains
* @valid_tx_ant: usable antennas for TX
@@ -176,7 +175,6 @@ struct iwl_mod_params {
* @use_rts_for_aggregation: use rts/cts protection for HT traffic
*/
struct iwl_hw_params {
- u8 num_ampdu_queues;
u8 tx_chains_num;
u8 rx_chains_num;
u8 valid_tx_ant;
@@ -217,7 +215,6 @@ enum iwl_led_mode {
* @chain_noise_num_beacons: number of beacons used to compute chain noise
* @adv_thermal_throttle: support advance thermal throttle
* @support_ct_kill_exit: support ct kill exit condition
- * @support_wimax_coexist: support wimax/wifi co-exist
* @plcp_delta_threshold: plcp error rate threshold used to trigger
* radio tuning when there is a high receiving plcp error rate
* @chain_noise_scale: default chain noise scale used for gain computation
@@ -231,7 +228,6 @@ enum iwl_led_mode {
struct iwl_base_params {
int eeprom_size;
int num_of_queues; /* def: HW dependent */
- int num_of_ampdu_queues;/* def: HW dependent */
/* for iwl_apm_init() */
u32 pll_cfg_val;
@@ -240,7 +236,6 @@ struct iwl_base_params {
u16 led_compensation;
bool adv_thermal_throttle;
bool support_ct_kill_exit;
- const bool support_wimax_coexist;
u8 plcp_delta_threshold;
s32 chain_noise_scale;
unsigned int wd_timeout;
@@ -299,21 +294,15 @@ struct iwl_ht_params {
* @need_temp_offset_calib: need to perform temperature offset calibration
* @no_xtal_calib: some devices do not need crystal calibration data,
* don't send it to those
- * @scan_rx_antennas: available antenna for scan operation
* @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
* @adv_pm: advance power management
* @rx_with_siso_diversity: 1x1 device with rx antenna diversity
* @internal_wimax_coex: internal wifi/wimax combo device
- * @iq_invert: I/Q inversion
* @temp_offset_v2: support v2 of temperature offset calibration
*
- * We enable the driver to be backward compatible wrt API version. The
- * driver specifies which APIs it supports (with @ucode_api_max being the
- * highest and @ucode_api_min the lowest). Firmware will only be loaded if
- * it has a supported API version.
- *
- * The ideal usage of this infrastructure is to treat a new ucode API
- * release as a new hardware revision.
+ * We enable the driver to be backward compatible wrt. hardware features.
+ * API differences in uCode shouldn't be handled here but through TLVs
+ * and/or the uCode API version instead.
*/
struct iwl_cfg {
/* params specific to an individual device within a device family */
@@ -337,12 +326,10 @@ struct iwl_cfg {
const struct iwl_bt_params *bt_params;
const bool need_temp_offset_calib; /* if used set to true */
const bool no_xtal_calib;
- u8 scan_rx_antennas[IEEE80211_NUM_BANDS];
enum iwl_led_mode led_mode;
const bool adv_pm;
const bool rx_with_siso_diversity;
const bool internal_wimax_coex;
- const bool iq_invert;
const bool temp_offset_v2;
};
@@ -351,7 +338,6 @@ struct iwl_cfg {
*
* @status: STATUS_*
* @wowlan: are we running wowlan uCode
- * @valid_contexts: microcode/device supports multiple contexts
* @bus: pointer to the bus layer data
* @cfg: see struct iwl_cfg
* @priv: pointer to the upper layer data
@@ -360,30 +346,18 @@ struct iwl_cfg {
* @hw_params: see struct iwl_hw_params
* @lock: protect general shared data
* @eeprom: pointer to the eeprom/OTP image
- * @ucode_type: indicator of loaded ucode image
- * @device_pointers: pointers to ucode event tables
*/
struct iwl_shared {
unsigned long status;
- u8 valid_contexts;
const struct iwl_cfg *cfg;
struct iwl_trans *trans;
void *drv;
struct iwl_hw_params hw_params;
- const struct iwl_fw *fw;
/* eeprom -- this is in the card's little endian byte order */
u8 *eeprom;
- /* ucode related variables */
- enum iwl_ucode_type ucode_type;
-
- struct {
- u32 error_event_table;
- u32 log_event_table;
- } device_pointers;
-
};
/*Whatever _m is (iwl_trans, iwl_priv, these macros will work */
diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.c b/drivers/net/wireless/iwlwifi/iwl-testmode.c
index a54e20e7b17..f31a0629c6c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-testmode.c
+++ b/drivers/net/wireless/iwlwifi/iwl-testmode.c
@@ -423,10 +423,13 @@ nla_put_failure:
static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv)
{
struct iwl_notification_wait calib_wait;
+ static const u8 calib_complete[] = {
+ CALIBRATION_COMPLETE_NOTIFICATION
+ };
int ret;
iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
- CALIBRATION_COMPLETE_NOTIFICATION,
+ calib_complete, ARRAY_SIZE(calib_complete),
NULL, NULL);
ret = iwl_init_alive_start(priv);
if (ret) {
@@ -605,11 +608,11 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
IWL_ERR(priv, "No uCode has not been loaded\n");
return -EINVAL;
} else {
- img = &priv->fw->img[priv->shrd->ucode_type];
+ img = &priv->fw->img[priv->cur_ucode];
inst_size = img->sec[IWL_UCODE_SECTION_INST].len;
data_size = img->sec[IWL_UCODE_SECTION_DATA].len;
}
- if (nla_put_u32(skb, IWL_TM_ATTR_FW_TYPE, priv->shrd->ucode_type) ||
+ if (nla_put_u32(skb, IWL_TM_ATTR_FW_TYPE, priv->cur_ucode) ||
nla_put_u32(skb, IWL_TM_ATTR_FW_INST_SIZE, inst_size) ||
nla_put_u32(skb, IWL_TM_ATTR_FW_DATA_SIZE, data_size))
goto nla_put_failure;
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
index 1c2fe87bd7e..32adee3b54e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
@@ -136,13 +136,6 @@ static inline int iwl_queue_dec_wrap(int index, int n_bd)
return --index & (n_bd - 1);
}
-/*
- * This queue number is required for proper operation
- * because the ucode will stop/start the scheduler as
- * required.
- */
-#define IWL_IPAN_MCAST_QUEUE 8
-
struct iwl_cmd_meta {
/* only for SYNC commands, iff the reply skb is wanted */
struct iwl_host_cmd *source;
@@ -199,9 +192,6 @@ struct iwl_queue {
* lock: queue lock
* @time_stamp: time (in jiffies) of last read_ptr change
* @need_update: indicates need to update read/write index
- * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled
- * @sta_id: valid if sched_retry is set
- * @tid: valid if sched_retry is set
*
* A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
* descriptors) and required locking structures.
@@ -218,12 +208,7 @@ struct iwl_tx_queue {
spinlock_t lock;
unsigned long time_stamp;
u8 need_update;
- u8 sched_retry;
u8 active;
- u8 swq_id;
-
- u16 sta_id;
- u16 tid;
};
/**
@@ -236,13 +221,6 @@ struct iwl_tx_queue {
* @scd_base_addr: scheduler sram base address in SRAM
* @scd_bc_tbls: pointer to the byte count table of the scheduler
* @kw: keep warm address
- * @ac_to_fifo: to what fifo is a specifc AC mapped ?
- * @ac_to_queue: to what tx queue is a specifc AC mapped ?
- * @mcast_queue:
- * @txq: Tx DMA processing queues
- * @txq_ctx_active_msk: what queue is active
- * queue_stopped: tracks what queue is stopped
- * queue_stop_count: tracks what SW queue is stopped
* @pci_dev: basic pci-network driver stuff
* @hw_base: pci hardware address support
* @ucode_write_complete: indicates that the ucode has been copied.
@@ -272,16 +250,9 @@ struct iwl_trans_pcie {
struct iwl_dma_ptr scd_bc_tbls;
struct iwl_dma_ptr kw;
- const u8 *ac_to_fifo[NUM_IWL_RXON_CTX];
- const u8 *ac_to_queue[NUM_IWL_RXON_CTX];
- u8 mcast_queue[NUM_IWL_RXON_CTX];
- u8 agg_txq[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT];
-
struct iwl_tx_queue *txq;
- unsigned long txq_ctx_active_msk;
-#define IWL_MAX_HW_QUEUES 32
+ unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)];
- atomic_t queue_stop_count[4];
/* PCI bus related data */
struct pci_dev *pci_dev;
@@ -293,6 +264,8 @@ struct iwl_trans_pcie {
u8 cmd_queue;
u8 n_no_reclaim_cmds;
u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
+ u8 setup_q_to_fifo[IWL_MAX_HW_QUEUES];
+ u8 n_q_to_fifo;
};
#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
@@ -331,15 +304,12 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans,
void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
struct iwl_tx_queue *txq,
u16 byte_cnt);
-int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans,
- int sta_id, int tid);
+void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int queue);
void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index);
void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
- struct iwl_tx_queue *txq,
- int tx_fifo_id, int scd_retry);
-int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans, int sta_id, int tid);
-void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans,
- enum iwl_rxon_context_id ctx,
+ struct iwl_tx_queue *txq,
+ int tx_fifo_id, bool active);
+void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo,
int sta_id, int tid, int frame_limit, u16 ssn);
void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
int index, enum dma_data_direction dma_dir);
@@ -350,8 +320,6 @@ int iwl_queue_space(const struct iwl_queue *q);
/*****************************************************
* Error handling
******************************************************/
-int iwl_dump_nic_event_log(struct iwl_trans *trans, bool full_log,
- char **buf, bool display);
int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display);
void iwl_dump_csr(struct iwl_trans *trans);
@@ -388,91 +356,28 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
iwl_write32(trans, CSR_INT_MASK, CSR_INT_BIT_RF_KILL);
}
-/*
- * we have 8 bits used like this:
- *
- * 7 6 5 4 3 2 1 0
- * | | | | | | | |
- * | | | | | | +-+-------- AC queue (0-3)
- * | | | | | |
- * | +-+-+-+-+------------ HW queue ID
- * |
- * +---------------------- unused
- */
-static inline void iwl_set_swq_id(struct iwl_tx_queue *txq, u8 ac, u8 hwq)
-{
- BUG_ON(ac > 3); /* only have 2 bits */
- BUG_ON(hwq > 31); /* only use 5 bits */
-
- txq->swq_id = (hwq << 2) | ac;
-}
-
-static inline u8 iwl_get_queue_ac(struct iwl_tx_queue *txq)
-{
- return txq->swq_id & 0x3;
-}
-
static inline void iwl_wake_queue(struct iwl_trans *trans,
struct iwl_tx_queue *txq)
{
- u8 queue = txq->swq_id;
- u8 ac = queue & 3;
- u8 hwq = (queue >> 2) & 0x1f;
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
-
- if (test_and_clear_bit(hwq, trans_pcie->queue_stopped)) {
- if (atomic_dec_return(&trans_pcie->queue_stop_count[ac]) <= 0) {
- iwl_op_mode_queue_not_full(trans->op_mode, ac);
- IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d ac %d",
- hwq, ac);
- } else {
- IWL_DEBUG_TX_QUEUES(trans,
- "Don't wake hwq %d ac %d stop count %d",
- hwq, ac,
- atomic_read(&trans_pcie->queue_stop_count[ac]));
- }
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (test_and_clear_bit(txq->q.id, trans_pcie->queue_stopped)) {
+ IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->q.id);
+ iwl_op_mode_queue_not_full(trans->op_mode, txq->q.id);
}
}
static inline void iwl_stop_queue(struct iwl_trans *trans,
struct iwl_tx_queue *txq)
{
- u8 queue = txq->swq_id;
- u8 ac = queue & 3;
- u8 hwq = (queue >> 2) & 0x1f;
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
-
- if (!test_and_set_bit(hwq, trans_pcie->queue_stopped)) {
- if (atomic_inc_return(&trans_pcie->queue_stop_count[ac]) > 0) {
- iwl_op_mode_queue_full(trans->op_mode, ac);
- IWL_DEBUG_TX_QUEUES(trans,
- "Stop hwq %d ac %d stop count %d",
- hwq, ac,
- atomic_read(&trans_pcie->queue_stop_count[ac]));
- } else {
- IWL_DEBUG_TX_QUEUES(trans,
- "Don't stop hwq %d ac %d stop count %d",
- hwq, ac,
- atomic_read(&trans_pcie->queue_stop_count[ac]));
- }
- } else {
- IWL_DEBUG_TX_QUEUES(trans, "stop hwq %d, but it is stopped",
- hwq);
- }
-}
-
-static inline void iwl_txq_ctx_activate(struct iwl_trans_pcie *trans_pcie,
- int txq_id)
-{
- set_bit(txq_id, &trans_pcie->txq_ctx_active_msk);
-}
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-static inline void iwl_txq_ctx_deactivate(struct iwl_trans_pcie *trans_pcie,
- int txq_id)
-{
- clear_bit(txq_id, &trans_pcie->txq_ctx_active_msk);
+ if (!test_and_set_bit(txq->q.id, trans_pcie->queue_stopped)) {
+ iwl_op_mode_queue_full(trans->op_mode, txq->q.id);
+ IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->q.id);
+ } else
+ IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n",
+ txq->q.id);
}
static inline int iwl_queue_used(const struct iwl_queue *q, int i)
@@ -487,19 +392,4 @@ static inline u8 get_cmd_index(struct iwl_queue *q, u32 index)
return index & (q->n_window - 1);
}
-#define IWL_TX_FIFO_BK 0 /* shared */
-#define IWL_TX_FIFO_BE 1
-#define IWL_TX_FIFO_VI 2 /* shared */
-#define IWL_TX_FIFO_VO 3
-#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK
-#define IWL_TX_FIFO_BE_IPAN 4
-#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI
-#define IWL_TX_FIFO_VO_IPAN 5
-/* re-uses the VO FIFO, uCode will properly flush/schedule */
-#define IWL_TX_FIFO_AUX 5
-#define IWL_TX_FIFO_UNUSED -1
-
-/* AUX (TX during scan dwell) queue */
-#define IWL_AUX_QUEUE 10
-
#endif /* __iwl_trans_int_pcie_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
index 8b1a7988e17..ab0f3fc22b8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
@@ -146,8 +146,11 @@ void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans,
q->write_actual = (q->write & ~0x7);
iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, q->write_actual);
} else {
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
+
/* If power-saving is in use, make sure device is awake */
- if (test_bit(STATUS_POWER_PMI, &trans->shrd->status)) {
+ if (test_bit(STATUS_POWER_PMI, &trans_pcie->status)) {
reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
@@ -362,83 +365,96 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans,
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_rx_queue *rxq = &trans_pcie->rxq;
struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
- struct iwl_device_cmd *cmd;
unsigned long flags;
- int len, err;
- u16 sequence;
- struct iwl_rx_cmd_buffer rxcb;
- struct iwl_rx_packet *pkt;
- bool reclaim;
- int index, cmd_index;
+ bool page_stolen = false;
+ int max_len = PAGE_SIZE << hw_params(trans).rx_page_order;
+ u32 offset = 0;
if (WARN_ON(!rxb))
return;
- dma_unmap_page(trans->dev, rxb->page_dma,
- PAGE_SIZE << hw_params(trans).rx_page_order,
- DMA_FROM_DEVICE);
-
- rxcb._page = rxb->page;
- pkt = rxb_addr(&rxcb);
+ dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE);
- IWL_DEBUG_RX(trans, "%s, 0x%02x\n",
- get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
+ while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) {
+ struct iwl_rx_packet *pkt;
+ struct iwl_device_cmd *cmd;
+ u16 sequence;
+ bool reclaim;
+ int index, cmd_index, err, len;
+ struct iwl_rx_cmd_buffer rxcb = {
+ ._offset = offset,
+ ._page = rxb->page,
+ ._page_stolen = false,
+ };
+ pkt = rxb_addr(&rxcb);
- len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
- len += sizeof(u32); /* account for status word */
- trace_iwlwifi_dev_rx(trans->dev, pkt, len);
-
- /* Reclaim a command buffer only if this packet is a response
- * to a (driver-originated) command.
- * If the packet (e.g. Rx frame) originated from uCode,
- * there is no command buffer to reclaim.
- * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
- * but apparently a few don't get set; catch them here. */
- reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME);
- if (reclaim) {
- int i;
+ if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID))
+ break;
- for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
- if (trans_pcie->no_reclaim_cmds[i] == pkt->hdr.cmd) {
- reclaim = false;
- break;
+ IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n",
+ rxcb._offset, get_cmd_string(pkt->hdr.cmd),
+ pkt->hdr.cmd);
+
+ len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+ len += sizeof(u32); /* account for status word */
+ trace_iwlwifi_dev_rx(trans->dev, pkt, len);
+
+ /* Reclaim a command buffer only if this packet is a response
+ * to a (driver-originated) command.
+ * If the packet (e.g. Rx frame) originated from uCode,
+ * there is no command buffer to reclaim.
+ * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
+ * but apparently a few don't get set; catch them here. */
+ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME);
+ if (reclaim) {
+ int i;
+
+ for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
+ if (trans_pcie->no_reclaim_cmds[i] ==
+ pkt->hdr.cmd) {
+ reclaim = false;
+ break;
+ }
}
}
- }
- sequence = le16_to_cpu(pkt->hdr.sequence);
- index = SEQ_TO_INDEX(sequence);
- cmd_index = get_cmd_index(&txq->q, index);
+ sequence = le16_to_cpu(pkt->hdr.sequence);
+ index = SEQ_TO_INDEX(sequence);
+ cmd_index = get_cmd_index(&txq->q, index);
- if (reclaim)
- cmd = txq->cmd[cmd_index];
- else
- cmd = NULL;
+ if (reclaim)
+ cmd = txq->cmd[cmd_index];
+ else
+ cmd = NULL;
- err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd);
+ err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd);
- /*
- * XXX: After here, we should always check rxcb._page
- * against NULL before touching it or its virtual
- * memory (pkt). Because some rx_handler might have
- * already taken or freed the pages.
- */
+ /*
+ * After here, we should always check rxcb._page_stolen,
+ * if it is true then one of the handlers took the page.
+ */
- if (reclaim) {
- /* Invoke any callbacks, transfer the buffer to caller,
- * and fire off the (possibly) blocking
- * iwl_trans_send_cmd()
- * as we reclaim the driver command queue */
- if (rxcb._page)
- iwl_tx_cmd_complete(trans, &rxcb, err);
- else
- IWL_WARN(trans, "Claim null rxb?\n");
+ if (reclaim) {
+ /* Invoke any callbacks, transfer the buffer to caller,
+ * and fire off the (possibly) blocking
+ * iwl_trans_send_cmd()
+ * as we reclaim the driver command queue */
+ if (!rxcb._page_stolen)
+ iwl_tx_cmd_complete(trans, &rxcb, err);
+ else
+ IWL_WARN(trans, "Claim null rxb?\n");
+ }
+
+ page_stolen |= rxcb._page_stolen;
+ offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN);
}
- /* page was stolen from us */
- if (rxcb._page == NULL)
+ /* page was stolen from us -- free our reference */
+ if (page_stolen) {
+ __free_pages(rxb->page, hw_params(trans).rx_page_order);
rxb->page = NULL;
+ }
/* Reuse the page if possible. For notification packets and
* SKBs that fail to Rx correctly, add them back into the
@@ -520,153 +536,6 @@ static void iwl_rx_handle(struct iwl_trans *trans)
iwlagn_rx_queue_restock(trans);
}
-static const char * const desc_lookup_text[] = {
- "OK",
- "FAIL",
- "BAD_PARAM",
- "BAD_CHECKSUM",
- "NMI_INTERRUPT_WDG",
- "SYSASSERT",
- "FATAL_ERROR",
- "BAD_COMMAND",
- "HW_ERROR_TUNE_LOCK",
- "HW_ERROR_TEMPERATURE",
- "ILLEGAL_CHAN_FREQ",
- "VCC_NOT_STABLE",
- "FH_ERROR",
- "NMI_INTERRUPT_HOST",
- "NMI_INTERRUPT_ACTION_PT",
- "NMI_INTERRUPT_UNKNOWN",
- "UCODE_VERSION_MISMATCH",
- "HW_ERROR_ABS_LOCK",
- "HW_ERROR_CAL_LOCK_FAIL",
- "NMI_INTERRUPT_INST_ACTION_PT",
- "NMI_INTERRUPT_DATA_ACTION_PT",
- "NMI_TRM_HW_ER",
- "NMI_INTERRUPT_TRM",
- "NMI_INTERRUPT_BREAK_POINT",
- "DEBUG_0",
- "DEBUG_1",
- "DEBUG_2",
- "DEBUG_3",
-};
-
-static struct { char *name; u8 num; } advanced_lookup[] = {
- { "NMI_INTERRUPT_WDG", 0x34 },
- { "SYSASSERT", 0x35 },
- { "UCODE_VERSION_MISMATCH", 0x37 },
- { "BAD_COMMAND", 0x38 },
- { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
- { "FATAL_ERROR", 0x3D },
- { "NMI_TRM_HW_ERR", 0x46 },
- { "NMI_INTERRUPT_TRM", 0x4C },
- { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
- { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
- { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
- { "NMI_INTERRUPT_HOST", 0x66 },
- { "NMI_INTERRUPT_ACTION_PT", 0x7C },
- { "NMI_INTERRUPT_UNKNOWN", 0x84 },
- { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
- { "ADVANCED_SYSASSERT", 0 },
-};
-
-static const char *desc_lookup(u32 num)
-{
- int i;
- int max = ARRAY_SIZE(desc_lookup_text);
-
- if (num < max)
- return desc_lookup_text[num];
-
- max = ARRAY_SIZE(advanced_lookup) - 1;
- for (i = 0; i < max; i++) {
- if (advanced_lookup[i].num == num)
- break;
- }
- return advanced_lookup[i].name;
-}
-
-#define ERROR_START_OFFSET (1 * sizeof(u32))
-#define ERROR_ELEM_SIZE (7 * sizeof(u32))
-
-static void iwl_dump_nic_error_log(struct iwl_trans *trans)
-{
- u32 base;
- struct iwl_error_event_table table;
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
-
- base = trans->shrd->device_pointers.error_event_table;
- if (trans->shrd->ucode_type == IWL_UCODE_INIT) {
- if (!base)
- base = trans->shrd->fw->init_errlog_ptr;
- } else {
- if (!base)
- base = trans->shrd->fw->inst_errlog_ptr;
- }
-
- if (!iwlagn_hw_valid_rtc_data_addr(base)) {
- IWL_ERR(trans,
- "Not valid error log pointer 0x%08X for %s uCode\n",
- base,
- (trans->shrd->ucode_type == IWL_UCODE_INIT)
- ? "Init" : "RT");
- return;
- }
-
- iwl_read_targ_mem_words(trans, base, &table, sizeof(table));
-
- if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
- IWL_ERR(trans, "Start IWL Error Log Dump:\n");
- IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
- trans->shrd->status, table.valid);
- }
-
- trans_pcie->isr_stats.err_code = table.error_id;
-
- trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
- table.data1, table.data2, table.line,
- table.blink1, table.blink2, table.ilink1,
- table.ilink2, table.bcon_time, table.gp1,
- table.gp2, table.gp3, table.ucode_ver,
- table.hw_ver, table.brd_ver);
- IWL_ERR(trans, "0x%08X | %-28s\n", table.error_id,
- desc_lookup(table.error_id));
- IWL_ERR(trans, "0x%08X | uPc\n", table.pc);
- IWL_ERR(trans, "0x%08X | branchlink1\n", table.blink1);
- IWL_ERR(trans, "0x%08X | branchlink2\n", table.blink2);
- IWL_ERR(trans, "0x%08X | interruptlink1\n", table.ilink1);
- IWL_ERR(trans, "0x%08X | interruptlink2\n", table.ilink2);
- IWL_ERR(trans, "0x%08X | data1\n", table.data1);
- IWL_ERR(trans, "0x%08X | data2\n", table.data2);
- IWL_ERR(trans, "0x%08X | line\n", table.line);
- IWL_ERR(trans, "0x%08X | beacon time\n", table.bcon_time);
- IWL_ERR(trans, "0x%08X | tsf low\n", table.tsf_low);
- IWL_ERR(trans, "0x%08X | tsf hi\n", table.tsf_hi);
- IWL_ERR(trans, "0x%08X | time gp1\n", table.gp1);
- IWL_ERR(trans, "0x%08X | time gp2\n", table.gp2);
- IWL_ERR(trans, "0x%08X | time gp3\n", table.gp3);
- IWL_ERR(trans, "0x%08X | uCode version\n", table.ucode_ver);
- IWL_ERR(trans, "0x%08X | hw version\n", table.hw_ver);
- IWL_ERR(trans, "0x%08X | board version\n", table.brd_ver);
- IWL_ERR(trans, "0x%08X | hcmd\n", table.hcmd);
-
- IWL_ERR(trans, "0x%08X | isr0\n", table.isr0);
- IWL_ERR(trans, "0x%08X | isr1\n", table.isr1);
- IWL_ERR(trans, "0x%08X | isr2\n", table.isr2);
- IWL_ERR(trans, "0x%08X | isr3\n", table.isr3);
- IWL_ERR(trans, "0x%08X | isr4\n", table.isr4);
- IWL_ERR(trans, "0x%08X | isr_pref\n", table.isr_pref);
- IWL_ERR(trans, "0x%08X | wait_event\n", table.wait_event);
- IWL_ERR(trans, "0x%08X | l2p_control\n", table.l2p_control);
- IWL_ERR(trans, "0x%08X | l2p_duration\n", table.l2p_duration);
- IWL_ERR(trans, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
- IWL_ERR(trans, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
- IWL_ERR(trans, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
- IWL_ERR(trans, "0x%08X | timestamp\n", table.u_timestamp);
- IWL_ERR(trans, "0x%08X | flow_handler\n", table.flow_handler);
-}
-
/**
* iwl_irq_handle_error - called for HW or SW error interrupt from card
*/
@@ -689,243 +558,12 @@ static void iwl_irq_handle_error(struct iwl_trans *trans)
return;
}
- IWL_ERR(trans, "Loaded firmware version: %s\n",
- trans->shrd->fw->fw_version);
-
- iwl_dump_nic_error_log(trans);
iwl_dump_csr(trans);
iwl_dump_fh(trans, NULL, false);
- iwl_dump_nic_event_log(trans, false, NULL, false);
iwl_op_mode_nic_error(trans->op_mode);
}
-#define EVENT_START_OFFSET (4 * sizeof(u32))
-
-/**
- * iwl_print_event_log - Dump error event log to syslog
- *
- */
-static int iwl_print_event_log(struct iwl_trans *trans, u32 start_idx,
- u32 num_events, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- u32 i;
- u32 base; /* SRAM byte address of event log header */
- u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
- u32 ptr; /* SRAM byte address of log data */
- u32 ev, time, data; /* event log data */
- unsigned long reg_flags;
-
- if (num_events == 0)
- return pos;
-
- base = trans->shrd->device_pointers.log_event_table;
- if (trans->shrd->ucode_type == IWL_UCODE_INIT) {
- if (!base)
- base = trans->shrd->fw->init_evtlog_ptr;
- } else {
- if (!base)
- base = trans->shrd->fw->inst_evtlog_ptr;
- }
-
- if (mode == 0)
- event_size = 2 * sizeof(u32);
- else
- event_size = 3 * sizeof(u32);
-
- ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
-
- /* Make sure device is powered up for SRAM reads */
- spin_lock_irqsave(&trans->reg_lock, reg_flags);
- if (unlikely(!iwl_grab_nic_access(trans)))
- goto out_unlock;
-
- /* Set starting address; reads will auto-increment */
- iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr);
-
- /* "time" is actually "data" for mode 0 (no timestamp).
- * place event id # at far right for easier visual parsing. */
- for (i = 0; i < num_events; i++) {
- ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
- time = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
- if (mode == 0) {
- /* data, ev */
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "EVT_LOG:0x%08x:%04u\n",
- time, ev);
- } else {
- trace_iwlwifi_dev_ucode_event(trans->dev, 0,
- time, ev);
- IWL_ERR(trans, "EVT_LOG:0x%08x:%04u\n",
- time, ev);
- }
- } else {
- data = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
- if (bufsz) {
- pos += scnprintf(*buf + pos, bufsz - pos,
- "EVT_LOGT:%010u:0x%08x:%04u\n",
- time, data, ev);
- } else {
- IWL_ERR(trans, "EVT_LOGT:%010u:0x%08x:%04u\n",
- time, data, ev);
- trace_iwlwifi_dev_ucode_event(trans->dev, time,
- data, ev);
- }
- }
- }
-
- /* Allow device to power down */
- iwl_release_nic_access(trans);
-out_unlock:
- spin_unlock_irqrestore(&trans->reg_lock, reg_flags);
- return pos;
-}
-
-/**
- * iwl_print_last_event_logs - Dump the newest # of event log to syslog
- */
-static int iwl_print_last_event_logs(struct iwl_trans *trans, u32 capacity,
- u32 num_wraps, u32 next_entry,
- u32 size, u32 mode,
- int pos, char **buf, size_t bufsz)
-{
- /*
- * display the newest DEFAULT_LOG_ENTRIES entries
- * i.e the entries just before the next ont that uCode would fill.
- */
- if (num_wraps) {
- if (next_entry < size) {
- pos = iwl_print_event_log(trans,
- capacity - (size - next_entry),
- size - next_entry, mode,
- pos, buf, bufsz);
- pos = iwl_print_event_log(trans, 0,
- next_entry, mode,
- pos, buf, bufsz);
- } else
- pos = iwl_print_event_log(trans, next_entry - size,
- size, mode, pos, buf, bufsz);
- } else {
- if (next_entry < size) {
- pos = iwl_print_event_log(trans, 0, next_entry,
- mode, pos, buf, bufsz);
- } else {
- pos = iwl_print_event_log(trans, next_entry - size,
- size, mode, pos, buf, bufsz);
- }
- }
- return pos;
-}
-
-#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
-
-int iwl_dump_nic_event_log(struct iwl_trans *trans, bool full_log,
- char **buf, bool display)
-{
- u32 base; /* SRAM byte address of event log header */
- u32 capacity; /* event log capacity in # entries */
- u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
- u32 num_wraps; /* # times uCode wrapped to top of log */
- u32 next_entry; /* index of next entry to be written by uCode */
- u32 size; /* # entries that we'll print */
- u32 logsize;
- int pos = 0;
- size_t bufsz = 0;
-
- base = trans->shrd->device_pointers.log_event_table;
- if (trans->shrd->ucode_type == IWL_UCODE_INIT) {
- logsize = trans->shrd->fw->init_evtlog_size;
- if (!base)
- base = trans->shrd->fw->init_evtlog_ptr;
- } else {
- logsize = trans->shrd->fw->inst_evtlog_size;
- if (!base)
- base = trans->shrd->fw->inst_evtlog_ptr;
- }
-
- if (!iwlagn_hw_valid_rtc_data_addr(base)) {
- IWL_ERR(trans,
- "Invalid event log pointer 0x%08X for %s uCode\n",
- base,
- (trans->shrd->ucode_type == IWL_UCODE_INIT)
- ? "Init" : "RT");
- return -EINVAL;
- }
-
- /* event log header */
- capacity = iwl_read_targ_mem(trans, base);
- mode = iwl_read_targ_mem(trans, base + (1 * sizeof(u32)));
- num_wraps = iwl_read_targ_mem(trans, base + (2 * sizeof(u32)));
- next_entry = iwl_read_targ_mem(trans, base + (3 * sizeof(u32)));
-
- if (capacity > logsize) {
- IWL_ERR(trans, "Log capacity %d is bogus, limit to %d "
- "entries\n", capacity, logsize);
- capacity = logsize;
- }
-
- if (next_entry > logsize) {
- IWL_ERR(trans, "Log write index %d is bogus, limit to %d\n",
- next_entry, logsize);
- next_entry = logsize;
- }
-
- size = num_wraps ? capacity : next_entry;
-
- /* bail out if nothing in log */
- if (size == 0) {
- IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n");
- return pos;
- }
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log)
- size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#else
- size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
- ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#endif
- IWL_ERR(trans, "Start IWL Event Log Dump: display last %u entries\n",
- size);
-
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (display) {
- if (full_log)
- bufsz = capacity * 48;
- else
- bufsz = size * 48;
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
- }
- if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) {
- /*
- * if uCode has wrapped back to top of log,
- * start at the oldest entry,
- * i.e the next one that uCode would fill.
- */
- if (num_wraps)
- pos = iwl_print_event_log(trans, next_entry,
- capacity - next_entry, mode,
- pos, buf, bufsz);
- /* (then/else) start at top of log */
- pos = iwl_print_event_log(trans, 0,
- next_entry, mode, pos, buf, bufsz);
- } else
- pos = iwl_print_last_event_logs(trans, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#else
- pos = iwl_print_last_event_logs(trans, capacity, num_wraps,
- next_entry, size, mode,
- pos, buf, bufsz);
-#endif
- return pos;
-}
-
/* tasklet for iwlagn interrupt */
void iwl_irq_tasklet(struct iwl_trans *trans)
{
@@ -963,7 +601,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans)
if (iwl_have_debug_level(IWL_DL_ISR)) {
/* just for debug */
inta_mask = iwl_read32(trans, CSR_INT_MASK);
- IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n ",
+ IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n",
inta, inta_mask);
}
#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
index e92972fd6ec..4684e2310cd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
@@ -41,43 +41,6 @@
#define IWL_TX_CRC_SIZE 4
#define IWL_TX_DELIMITER_SIZE 4
-/*
- * mac80211 queues, ACs, hardware queues, FIFOs.
- *
- * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues
- *
- * Mac80211 uses the following numbers, which we get as from it
- * by way of skb_get_queue_mapping(skb):
- *
- * VO 0
- * VI 1
- * BE 2
- * BK 3
- *
- *
- * Regular (not A-MPDU) frames are put into hardware queues corresponding
- * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their
- * own queue per aggregation session (RA/TID combination), such queues are
- * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In
- * order to map frames to the right queue, we also need an AC->hw queue
- * mapping. This is implemented here.
- *
- * Due to the way hw queues are set up (by the hw specific code), the AC->hw
- * queue mapping is the identity mapping.
- */
-
-static const u8 tid_to_ac[] = {
- IEEE80211_AC_BE,
- IEEE80211_AC_BK,
- IEEE80211_AC_BK,
- IEEE80211_AC_BE,
- IEEE80211_AC_VI,
- IEEE80211_AC_VI,
- IEEE80211_AC_VO,
- IEEE80211_AC_VO
-};
-
-
/**
* iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
*/
@@ -141,8 +104,10 @@ void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq)
iwl_write32(trans, HBUS_TARG_WRPTR,
txq->q.write_ptr | (txq_id << 8));
} else {
+ struct iwl_trans_pcie *trans_pcie =
+ IWL_TRANS_GET_PCIE_TRANS(trans);
/* if we're trying to save power */
- if (test_bit(STATUS_POWER_PMI, &trans->shrd->status)) {
+ if (test_bit(STATUS_POWER_PMI, &trans_pcie->status)) {
/* wake up nic if it's powered down ...
* uCode will wake up, and interrupt us again, so next
* time we'll skip this part. */
@@ -448,20 +413,17 @@ static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id)
void iwl_trans_set_wr_ptrs(struct iwl_trans *trans,
int txq_id, u32 index)
{
- IWL_DEBUG_TX_QUEUES(trans, "Q %d WrPtr: %d", txq_id, index & 0xff);
+ IWL_DEBUG_TX_QUEUES(trans, "Q %d WrPtr: %d\n", txq_id, index & 0xff);
iwl_write_direct32(trans, HBUS_TARG_WRPTR,
(index & 0xff) | (txq_id << 8));
iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), index);
}
void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
- struct iwl_tx_queue *txq,
- int tx_fifo_id, int scd_retry)
+ struct iwl_tx_queue *txq,
+ int tx_fifo_id, bool active)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int txq_id = txq->q.id;
- int active =
- test_bit(txq_id, &trans_pcie->txq_ctx_active_msk) ? 1 : 0;
iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
(active << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
@@ -469,77 +431,22 @@ void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
(1 << SCD_QUEUE_STTS_REG_POS_WSL) |
SCD_QUEUE_STTS_REG_MSK);
- txq->sched_retry = scd_retry;
-
if (active)
- IWL_DEBUG_TX_QUEUES(trans, "Activate %s Queue %d on FIFO %d\n",
- scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
+ IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d\n",
+ txq_id, tx_fifo_id);
else
- IWL_DEBUG_TX_QUEUES(trans, "Deactivate %s Queue %d\n",
- scd_retry ? "BA" : "AC/CMD", txq_id);
-}
-
-static inline int get_ac_from_tid(u16 tid)
-{
- if (likely(tid < ARRAY_SIZE(tid_to_ac)))
- return tid_to_ac[tid];
-
- /* no support for TIDs 8-15 yet */
- return -EINVAL;
+ IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id);
}
-static inline int get_fifo_from_tid(struct iwl_trans_pcie *trans_pcie,
- u8 ctx, u16 tid)
+void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int txq_id, int fifo,
+ int sta_id, int tid, int frame_limit, u16 ssn)
{
- const u8 *ac_to_fifo = trans_pcie->ac_to_fifo[ctx];
- if (likely(tid < ARRAY_SIZE(tid_to_ac)))
- return ac_to_fifo[tid_to_ac[tid]];
-
- /* no support for TIDs 8-15 yet */
- return -EINVAL;
-}
-
-static inline bool is_agg_txqid_valid(struct iwl_trans *trans, int txq_id)
-{
- if (txq_id < IWLAGN_FIRST_AMPDU_QUEUE)
- return false;
- return txq_id < (IWLAGN_FIRST_AMPDU_QUEUE +
- hw_params(trans).num_ampdu_queues);
-}
-
-void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans,
- enum iwl_rxon_context_id ctx, int sta_id,
- int tid, int frame_limit, u16 ssn)
-{
- int tx_fifo, txq_id;
- u16 ra_tid;
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
unsigned long flags;
+ u16 ra_tid = BUILD_RAxTID(sta_id, tid);
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
-
- if (WARN_ON(sta_id == IWL_INVALID_STATION))
- return;
- if (WARN_ON(tid >= IWL_MAX_TID_COUNT))
- return;
-
- tx_fifo = get_fifo_from_tid(trans_pcie, ctx, tid);
- if (WARN_ON(tx_fifo < 0)) {
- IWL_ERR(trans, "txq_agg_setup, bad fifo: %d\n", tx_fifo);
- return;
- }
-
- txq_id = trans_pcie->agg_txq[sta_id][tid];
- if (WARN_ON_ONCE(!is_agg_txqid_valid(trans, txq_id))) {
- IWL_ERR(trans,
- "queue number out of range: %d, must be %d to %d\n",
- txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
- IWLAGN_FIRST_AMPDU_QUEUE +
- hw_params(trans).num_ampdu_queues - 1);
- return;
- }
-
- ra_tid = BUILD_RAxTID(sta_id, tid);
+ if (test_and_set_bit(txq_id, trans_pcie->queue_used))
+ WARN_ONCE(1, "queue %d already used - expect issues", txq_id);
spin_lock_irqsave(&trans_pcie->irq_lock, flags);
@@ -550,10 +457,10 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans,
iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id);
/* Set this queue as a chain-building queue */
- iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, (1<<txq_id));
+ iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id));
/* enable aggregations for the queue */
- iwl_set_bits_prph(trans, SCD_AGGR_SEL, (1<<txq_id));
+ iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
/* Place first TFD at index corresponding to start sequence number.
* Assumes that ssn_idx is valid (!= 0xFFF) */
@@ -563,92 +470,42 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans,
/* Set up Tx window size and frame limit for this queue */
iwl_write_targ_mem(trans, trans_pcie->scd_base_addr +
- SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
- sizeof(u32),
- ((frame_limit <<
- SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
- SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
- ((frame_limit <<
- SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
- SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
+ SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32),
+ ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
+ SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
+ ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
+ SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
iwl_set_bits_prph(trans, SCD_INTERRUPT_MASK, (1 << txq_id));
/* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id],
- tx_fifo, 1);
-
- trans_pcie->txq[txq_id].sta_id = sta_id;
- trans_pcie->txq[txq_id].tid = tid;
+ fifo, true);
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
}
-/*
- * Find first available (lowest unused) Tx Queue, mark it "active".
- * Called only when finding queue for aggregation.
- * Should never return anything < 7, because they should already
- * be in use as EDCA AC (0-3), Command (4), reserved (5, 6)
- */
-static int iwlagn_txq_ctx_activate_free(struct iwl_trans *trans)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int txq_id;
-
- for (txq_id = 0; txq_id < cfg(trans)->base_params->num_of_queues;
- txq_id++)
- if (!test_and_set_bit(txq_id,
- &trans_pcie->txq_ctx_active_msk))
- return txq_id;
- return -1;
-}
-
-int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans,
- int sta_id, int tid)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int txq_id;
-
- txq_id = iwlagn_txq_ctx_activate_free(trans);
- if (txq_id == -1) {
- IWL_ERR(trans, "No free aggregation queue available\n");
- return -ENXIO;
- }
-
- trans_pcie->agg_txq[sta_id][tid] = txq_id;
- iwl_set_swq_id(&trans_pcie->txq[txq_id], get_ac_from_tid(tid), txq_id);
-
- return 0;
-}
-
-int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int sta_id, int tid)
+void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int txq_id)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- u8 txq_id = trans_pcie->agg_txq[sta_id][tid];
- if (WARN_ON_ONCE(!is_agg_txqid_valid(trans, txq_id))) {
- IWL_ERR(trans,
- "queue number out of range: %d, must be %d to %d\n",
- txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
- IWLAGN_FIRST_AMPDU_QUEUE +
- hw_params(trans).num_ampdu_queues - 1);
- return -EINVAL;
+ if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) {
+ WARN_ONCE(1, "queue %d not used", txq_id);
+ return;
}
iwlagn_tx_queue_stop_scheduler(trans, txq_id);
- iwl_clear_bits_prph(trans, SCD_AGGR_SEL, (1 << txq_id));
+ iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
- trans_pcie->agg_txq[sta_id][tid] = 0;
trans_pcie->txq[txq_id].q.read_ptr = 0;
trans_pcie->txq[txq_id].q.write_ptr = 0;
- /* supposes that ssn_idx is valid (!= 0xFFF) */
iwl_trans_set_wr_ptrs(trans, txq_id, 0);
- iwl_clear_bits_prph(trans, SCD_INTERRUPT_MASK, (1 << txq_id));
- iwl_txq_ctx_deactivate(trans_pcie, txq_id);
- iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], 0, 0);
- return 0;
+ iwl_clear_bits_prph(trans, SCD_INTERRUPT_MASK, BIT(txq_id));
+
+ iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id],
+ 0, false);
}
/*************** HOST COMMAND QUEUE FUNCTIONS *****/
@@ -681,11 +538,6 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
int trace_idx;
#endif
- if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) {
- IWL_WARN(trans, "fw recovery, no hcmd send\n");
- return -EIO;
- }
-
copy_size = sizeof(out_cmd->hdr);
cmd_size = sizeof(out_cmd->hdr);
@@ -966,12 +818,6 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
get_cmd_string(cmd->id));
- if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) {
- IWL_ERR(trans, "Command %s failed: FW Error\n",
- get_cmd_string(cmd->id));
- return -EIO;
- }
-
if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE,
&trans->shrd->status))) {
IWL_ERR(trans, "Command %s: a command is already active!\n",
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
index 4d7b30d3e64..9f62283504e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
@@ -180,7 +180,6 @@ static void iwl_trans_rx_hw_init(struct iwl_trans *trans,
FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |
FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
- FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
rb_size|
(rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
(rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
@@ -370,21 +369,13 @@ error:
}
static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq,
- int slots_num, u32 txq_id)
+ int slots_num, u32 txq_id)
{
int ret;
txq->need_update = 0;
memset(txq->meta, 0, sizeof(txq->meta[0]) * slots_num);
- /*
- * For the default queues 0-3, set up the swq_id
- * already -- all others need to get one later
- * (if they need one at all).
- */
- if (txq_id < 4)
- iwl_set_swq_id(txq, txq_id, txq_id);
-
/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
* iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
@@ -895,59 +886,6 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans)
return ret;
}
-#define IWL_AC_UNSET -1
-
-struct queue_to_fifo_ac {
- s8 fifo, ac;
-};
-
-static const struct queue_to_fifo_ac iwlagn_default_queue_to_tx_fifo[] = {
- { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
- { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
-};
-
-static const struct queue_to_fifo_ac iwlagn_ipan_queue_to_tx_fifo[] = {
- { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
- { IWL_TX_FIFO_BK_IPAN, IEEE80211_AC_BK, },
- { IWL_TX_FIFO_BE_IPAN, IEEE80211_AC_BE, },
- { IWL_TX_FIFO_VI_IPAN, IEEE80211_AC_VI, },
- { IWL_TX_FIFO_VO_IPAN, IEEE80211_AC_VO, },
- { IWL_TX_FIFO_BE_IPAN, 2, },
- { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
- { IWL_TX_FIFO_AUX, IWL_AC_UNSET, },
-};
-
-static const u8 iwlagn_bss_ac_to_fifo[] = {
- IWL_TX_FIFO_VO,
- IWL_TX_FIFO_VI,
- IWL_TX_FIFO_BE,
- IWL_TX_FIFO_BK,
-};
-static const u8 iwlagn_bss_ac_to_queue[] = {
- 0, 1, 2, 3,
-};
-static const u8 iwlagn_pan_ac_to_fifo[] = {
- IWL_TX_FIFO_VO_IPAN,
- IWL_TX_FIFO_VI_IPAN,
- IWL_TX_FIFO_BE_IPAN,
- IWL_TX_FIFO_BK_IPAN,
-};
-static const u8 iwlagn_pan_ac_to_queue[] = {
- 7, 6, 5, 4,
-};
-
/*
* ucode
*/
@@ -1028,19 +966,8 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
const struct fw_img *fw)
{
int ret;
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill;
- trans_pcie->ac_to_queue[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_queue;
- trans_pcie->ac_to_queue[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_queue;
-
- trans_pcie->ac_to_fifo[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_fifo;
- trans_pcie->ac_to_fifo[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_fifo;
-
- trans_pcie->mcast_queue[IWL_RXON_CTX_BSS] = 0;
- trans_pcie->mcast_queue[IWL_RXON_CTX_PAN] = IWL_IPAN_MCAST_QUEUE;
-
/* This may fail if AMT took ownership of the device */
if (iwl_prepare_card_hw(trans)) {
IWL_WARN(trans, "Exit HW not ready\n");
@@ -1098,9 +1025,7 @@ static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask)
static void iwl_tx_start(struct iwl_trans *trans)
{
- const struct queue_to_fifo_ac *queue_to_fifo;
- struct iwl_trans_pcie *trans_pcie =
- IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 a;
unsigned long flags;
int i, chan;
@@ -1166,41 +1091,19 @@ static void iwl_tx_start(struct iwl_trans *trans)
/* Activate all Tx DMA/FIFO channels */
iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7));
- /* map queues to FIFOs */
- if (trans->shrd->valid_contexts != BIT(IWL_RXON_CTX_BSS))
- queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo;
- else
- queue_to_fifo = iwlagn_default_queue_to_tx_fifo;
-
iwl_trans_set_wr_ptrs(trans, trans_pcie->cmd_queue, 0);
- /* make sure all queue are not stopped */
- memset(&trans_pcie->queue_stopped[0], 0,
- sizeof(trans_pcie->queue_stopped));
- for (i = 0; i < 4; i++)
- atomic_set(&trans_pcie->queue_stop_count[i], 0);
+ /* make sure all queue are not stopped/used */
+ memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
+ memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used));
- /* reset to 0 to enable all the queue first */
- trans_pcie->txq_ctx_active_msk = 0;
+ for (i = 0; i < trans_pcie->n_q_to_fifo; i++) {
+ int fifo = trans_pcie->setup_q_to_fifo[i];
- BUILD_BUG_ON(ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo) <
- IWLAGN_FIRST_AMPDU_QUEUE);
- BUILD_BUG_ON(ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo) <
- IWLAGN_FIRST_AMPDU_QUEUE);
+ set_bit(i, trans_pcie->queue_used);
- for (i = 0; i < IWLAGN_FIRST_AMPDU_QUEUE; i++) {
- int fifo = queue_to_fifo[i].fifo;
- int ac = queue_to_fifo[i].ac;
-
- iwl_txq_ctx_activate(trans_pcie, i);
-
- if (fifo == IWL_TX_FIFO_UNUSED)
- continue;
-
- if (ac != IWL_AC_UNSET)
- iwl_set_swq_id(&trans_pcie->txq[i], ac, i);
iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i],
- fifo, 0);
+ fifo, true);
}
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
@@ -1325,70 +1228,32 @@ static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans)
}
static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
- struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
- u8 sta_id, u8 tid)
+ struct iwl_device_cmd *dev_cmd, int txq_id)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload;
struct iwl_cmd_meta *out_meta;
struct iwl_tx_queue *txq;
struct iwl_queue *q;
-
dma_addr_t phys_addr = 0;
dma_addr_t txcmd_phys;
dma_addr_t scratch_phys;
u16 len, firstlen, secondlen;
u8 wait_write_ptr = 0;
- u8 txq_id;
- bool is_agg = false;
__le16 fc = hdr->frame_control;
u8 hdr_len = ieee80211_hdrlen(fc);
u16 __maybe_unused wifi_seq;
- /*
- * Send this frame after DTIM -- there's a special queue
- * reserved for this for contexts that support AP mode.
- */
- if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
- txq_id = trans_pcie->mcast_queue[ctx];
-
- /*
- * The microcode will clear the more data
- * bit in the last frame it transmits.
- */
- hdr->frame_control |=
- cpu_to_le16(IEEE80211_FCTL_MOREDATA);
- } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
- txq_id = IWL_AUX_QUEUE;
- else
- txq_id =
- trans_pcie->ac_to_queue[ctx][skb_get_queue_mapping(skb)];
-
- /* aggregation is on for this <sta,tid> */
- if (info->flags & IEEE80211_TX_CTL_AMPDU) {
- WARN_ON(tid >= IWL_MAX_TID_COUNT);
- txq_id = trans_pcie->agg_txq[sta_id][tid];
- is_agg = true;
- }
-
txq = &trans_pcie->txq[txq_id];
q = &txq->q;
- spin_lock(&txq->lock);
+ if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
- /* In AGG mode, the index in the ring must correspond to the WiFi
- * sequence number. This is a HW requirements to help the SCD to parse
- * the BA.
- * Check here that the packets are in the right place on the ring.
- */
-#ifdef CONFIG_IWLWIFI_DEBUG
- wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
- WARN_ONCE(is_agg && ((wifi_seq & 0xff) != q->write_ptr),
- "Q: %d WiFi Seq %d tfdNum %d",
- txq_id, wifi_seq, q->write_ptr);
-#endif
+ spin_lock(&txq->lock);
/* Set up driver data for this TFD */
txq->skbs[q->write_ptr] = skb;
@@ -1565,8 +1430,8 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans)
iwl_enable_rfkill_int(trans);
}
-static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
- int txq_id, int ssn, struct sk_buff_head *skbs)
+static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
+ struct sk_buff_head *skbs)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
@@ -1578,33 +1443,15 @@ static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
txq->time_stamp = jiffies;
- if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE &&
- tid != IWL_TID_NON_QOS &&
- txq_id != trans_pcie->agg_txq[sta_id][tid])) {
- /*
- * FIXME: this is a uCode bug which need to be addressed,
- * log the information and return for now.
- * Since it is can possibly happen very often and in order
- * not to fill the syslog, don't use IWL_ERR or IWL_WARN
- */
- IWL_DEBUG_TX_QUEUES(trans, "Bad queue mapping txq_id %d, "
- "agg_txq[sta_id[tid] %d", txq_id,
- trans_pcie->agg_txq[sta_id][tid]);
- spin_unlock(&txq->lock);
- return 1;
- }
-
if (txq->q.read_ptr != tfd_num) {
- IWL_DEBUG_TX_REPLY(trans, "[Q %d | AC %d] %d -> %d (%d)\n",
- txq_id, iwl_get_queue_ac(txq), txq->q.read_ptr,
- tfd_num, ssn);
+ IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n",
+ txq_id, txq->q.read_ptr, tfd_num, ssn);
freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs);
if (iwl_queue_space(&txq->q) > txq->q.low_mark)
iwl_wake_queue(trans, txq);
}
spin_unlock(&txq->lock);
- return 0;
}
static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
@@ -1623,7 +1470,7 @@ static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)
}
static void iwl_trans_pcie_configure(struct iwl_trans *trans,
- const struct iwl_trans_config *trans_cfg)
+ const struct iwl_trans_config *trans_cfg)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -1635,6 +1482,17 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
if (trans_pcie->n_no_reclaim_cmds)
memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
trans_pcie->n_no_reclaim_cmds * sizeof(u8));
+
+ trans_pcie->n_q_to_fifo = trans_cfg->n_queue_to_fifo;
+
+ if (WARN_ON(trans_pcie->n_q_to_fifo > IWL_MAX_HW_QUEUES))
+ trans_pcie->n_q_to_fifo = IWL_MAX_HW_QUEUES;
+
+ /* at least the command queue must be mapped */
+ WARN_ON(!trans_pcie->n_q_to_fifo);
+
+ memcpy(trans_pcie->setup_q_to_fifo, trans_cfg->queue_to_fifo,
+ trans_pcie->n_q_to_fifo * sizeof(u8));
}
static void iwl_trans_pcie_free(struct iwl_trans *trans)
@@ -1660,6 +1518,16 @@ static void iwl_trans_pcie_free(struct iwl_trans *trans)
kfree(trans);
}
+static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (state)
+ set_bit(STATUS_POWER_PMI, &trans_pcie->status);
+ else
+ clear_bit(STATUS_POWER_PMI, &trans_pcie->status);
+}
+
#ifdef CONFIG_PM_SLEEP
static int iwl_trans_pcie_suspend(struct iwl_trans *trans)
{
@@ -1952,18 +1820,10 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
txq = &trans_pcie->txq[cnt];
q = &txq->q;
pos += scnprintf(buf + pos, bufsz - pos,
- "hwq %.2d: read=%u write=%u stop=%d"
- " swq_id=%#.2x (ac %d/hwq %d)\n",
+ "hwq %.2d: read=%u write=%u use=%d stop=%d\n",
cnt, q->read_ptr, q->write_ptr,
- !!test_bit(cnt, trans_pcie->queue_stopped),
- txq->swq_id, txq->swq_id & 3,
- (txq->swq_id >> 2) & 0x1f);
- if (cnt >= 4)
- continue;
- /* for the ACs, display the stop count too */
- pos += scnprintf(buf + pos, bufsz - pos,
- " stop-count: %d\n",
- atomic_read(&trans_pcie->queue_stop_count[cnt]));
+ !!test_bit(cnt, trans_pcie->queue_used),
+ !!test_bit(cnt, trans_pcie->queue_stopped));
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
@@ -1997,44 +1857,6 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
-static ssize_t iwl_dbgfs_log_event_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- char *buf;
- int pos = 0;
- ssize_t ret = -ENOMEM;
-
- ret = pos = iwl_dump_nic_event_log(trans, true, &buf, true);
- if (buf) {
- ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
- kfree(buf);
- }
- return ret;
-}
-
-static ssize_t iwl_dbgfs_log_event_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_trans *trans = file->private_data;
- u32 event_log_flag;
- char buf[8];
- int buf_size;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%d", &event_log_flag) != 1)
- return -EFAULT;
- if (event_log_flag == 1)
- iwl_dump_nic_event_log(trans, true, NULL, false);
-
- return count;
-}
-
static ssize_t iwl_dbgfs_interrupt_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos) {
@@ -2161,7 +1983,6 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
return ret;
}
-DEBUGFS_READ_WRITE_FILE_OPS(log_event);
DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
DEBUGFS_READ_FILE_OPS(fh_reg);
DEBUGFS_READ_FILE_OPS(rx_queue);
@@ -2177,7 +1998,6 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
{
DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR);
DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR);
- DEBUGFS_ADD_FILE(log_event, dir, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(csr, dir, S_IWUSR);
DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR);
@@ -2205,7 +2025,6 @@ const struct iwl_trans_ops trans_ops_pcie = {
.reclaim = iwl_trans_pcie_reclaim,
.tx_agg_disable = iwl_trans_pcie_tx_agg_disable,
- .tx_agg_alloc = iwl_trans_pcie_tx_agg_alloc,
.tx_agg_setup = iwl_trans_pcie_tx_agg_setup,
.free = iwl_trans_pcie_free,
@@ -2223,6 +2042,7 @@ const struct iwl_trans_ops trans_ops_pcie = {
.write32 = iwl_trans_pcie_write32,
.read32 = iwl_trans_pcie_read32,
.configure = iwl_trans_pcie_configure,
+ .set_pmi = iwl_trans_pcie_set_pmi,
};
struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd,
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 0c81cbaa808..66c54c1b404 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -162,6 +162,8 @@ struct iwl_cmd_header {
#define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */
+#define FH_RSCSR_FRAME_INVALID 0x55550000
+#define FH_RSCSR_FRAME_ALIGN 0x40
struct iwl_rx_packet {
/*
@@ -260,27 +262,42 @@ static inline void iwl_free_resp(struct iwl_host_cmd *cmd)
struct iwl_rx_cmd_buffer {
struct page *_page;
+ int _offset;
+ bool _page_stolen;
};
static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r)
{
- return page_address(r->_page);
+ return (void *)((unsigned long)page_address(r->_page) + r->_offset);
+}
+
+static inline int rxb_offset(struct iwl_rx_cmd_buffer *r)
+{
+ return r->_offset;
}
static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
{
- struct page *p = r->_page;
- r->_page = NULL;
- return p;
+ r->_page_stolen = true;
+ get_page(r->_page);
+ return r->_page;
}
#define MAX_NO_RECLAIM_CMDS 6
+/*
+ * Maximum number of HW queues the transport layer
+ * currently supports
+ */
+#define IWL_MAX_HW_QUEUES 32
+
/**
* struct iwl_trans_config - transport configuration
*
* @op_mode: pointer to the upper layer.
- * Must be set before any other call.
+ * @queue_to_fifo: queue to FIFO mapping to set up by
+ * default
+ * @n_queue_to_fifo: number of queues to set up
* @cmd_queue: the index of the command queue.
* Must be set before start_fw.
* @no_reclaim_cmds: Some devices erroneously don't set the
@@ -291,6 +308,9 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
*/
struct iwl_trans_config {
struct iwl_op_mode *op_mode;
+ const u8 *queue_to_fifo;
+ u8 n_queue_to_fifo;
+
u8 cmd_queue;
const u8 *no_reclaim_cmds;
int n_no_reclaim_cmds;
@@ -322,8 +342,6 @@ struct iwl_trans_config {
* Must be atomic
* @reclaim: free packet until ssn. Returns a list of freed packets.
* Must be atomic
- * @tx_agg_alloc: allocate resources for a TX BA session
- * Must be atomic
* @tx_agg_setup: setup a tx queue for AMPDU - will be called once the HW is
* ready and a successful ADDBA response has been received.
* May sleep
@@ -346,6 +364,7 @@ struct iwl_trans_config {
* @configure: configure parameters required by the transport layer from
* the op_mode. May be called several times before start_fw, can't be
* called after that.
+ * @set_pmi: set the power pmi state
*/
struct iwl_trans_ops {
@@ -360,18 +379,13 @@ struct iwl_trans_ops {
int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
int (*tx)(struct iwl_trans *trans, struct sk_buff *skb,
- struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
- u8 sta_id, u8 tid);
- int (*reclaim)(struct iwl_trans *trans, int sta_id, int tid,
- int txq_id, int ssn, struct sk_buff_head *skbs);
-
- int (*tx_agg_disable)(struct iwl_trans *trans,
- int sta_id, int tid);
- int (*tx_agg_alloc)(struct iwl_trans *trans,
- int sta_id, int tid);
- void (*tx_agg_setup)(struct iwl_trans *trans,
- enum iwl_rxon_context_id ctx, int sta_id, int tid,
- int frame_limit, u16 ssn);
+ struct iwl_device_cmd *dev_cmd, int queue);
+ void (*reclaim)(struct iwl_trans *trans, int queue, int ssn,
+ struct sk_buff_head *skbs);
+
+ void (*tx_agg_setup)(struct iwl_trans *trans, int queue, int fifo,
+ int sta_id, int tid, int frame_limit, u16 ssn);
+ void (*tx_agg_disable)(struct iwl_trans *trans, int queue);
void (*free)(struct iwl_trans *trans);
@@ -387,6 +401,7 @@ struct iwl_trans_ops {
u32 (*read32)(struct iwl_trans *trans, u32 ofs);
void (*configure)(struct iwl_trans *trans,
const struct iwl_trans_config *trans_cfg);
+ void (*set_pmi)(struct iwl_trans *trans, bool state);
};
/**
@@ -507,55 +522,42 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
}
static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
- struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
- u8 sta_id, u8 tid)
-{
- if (trans->state != IWL_TRANS_FW_ALIVE)
- IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
-
- return trans->ops->tx(trans, skb, dev_cmd, ctx, sta_id, tid);
-}
-
-static inline int iwl_trans_reclaim(struct iwl_trans *trans, int sta_id,
- int tid, int txq_id, int ssn,
- struct sk_buff_head *skbs)
+ struct iwl_device_cmd *dev_cmd, int queue)
{
WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
"%s bad state = %d", __func__, trans->state);
- return trans->ops->reclaim(trans, sta_id, tid, txq_id, ssn, skbs);
+ return trans->ops->tx(trans, skb, dev_cmd, queue);
}
-static inline int iwl_trans_tx_agg_disable(struct iwl_trans *trans,
- int sta_id, int tid)
+static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue,
+ int ssn, struct sk_buff_head *skbs)
{
WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
"%s bad state = %d", __func__, trans->state);
- return trans->ops->tx_agg_disable(trans, sta_id, tid);
+ trans->ops->reclaim(trans, queue, ssn, skbs);
}
-static inline int iwl_trans_tx_agg_alloc(struct iwl_trans *trans,
- int sta_id, int tid)
+static inline void iwl_trans_tx_agg_disable(struct iwl_trans *trans, int queue)
{
WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
"%s bad state = %d", __func__, trans->state);
- return trans->ops->tx_agg_alloc(trans, sta_id, tid);
+ trans->ops->tx_agg_disable(trans, queue);
}
-
-static inline void iwl_trans_tx_agg_setup(struct iwl_trans *trans,
- enum iwl_rxon_context_id ctx,
- int sta_id, int tid,
- int frame_limit, u16 ssn)
+static inline void iwl_trans_tx_agg_setup(struct iwl_trans *trans, int queue,
+ int fifo, int sta_id, int tid,
+ int frame_limit, u16 ssn)
{
might_sleep();
WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
"%s bad state = %d", __func__, trans->state);
- trans->ops->tx_agg_setup(trans, ctx, sta_id, tid, frame_limit, ssn);
+ trans->ops->tx_agg_setup(trans, queue, fifo, sta_id, tid,
+ frame_limit, ssn);
}
static inline void iwl_trans_free(struct iwl_trans *trans)
@@ -611,6 +613,11 @@ static inline u32 iwl_trans_read32(struct iwl_trans *trans, u32 ofs)
return trans->ops->read32(trans, ofs);
}
+static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state)
+{
+ trans->ops->set_pmi(trans, state);
+}
+
/*****************************************************
* Transport layers implementations + their allocation function
******************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-ucode.c b/drivers/net/wireless/iwlwifi/iwl-ucode.c
index 25282872883..ba7c9f883cb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-ucode.c
+++ b/drivers/net/wireless/iwlwifi/iwl-ucode.c
@@ -40,37 +40,6 @@
#include "iwl-fh.h"
#include "iwl-op-mode.h"
-static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = {
- {COEX_CU_UNASSOC_IDLE_RP, COEX_CU_UNASSOC_IDLE_WP,
- 0, COEX_UNASSOC_IDLE_FLAGS},
- {COEX_CU_UNASSOC_MANUAL_SCAN_RP, COEX_CU_UNASSOC_MANUAL_SCAN_WP,
- 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
- {COEX_CU_UNASSOC_AUTO_SCAN_RP, COEX_CU_UNASSOC_AUTO_SCAN_WP,
- 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
- {COEX_CU_CALIBRATION_RP, COEX_CU_CALIBRATION_WP,
- 0, COEX_CALIBRATION_FLAGS},
- {COEX_CU_PERIODIC_CALIBRATION_RP, COEX_CU_PERIODIC_CALIBRATION_WP,
- 0, COEX_PERIODIC_CALIBRATION_FLAGS},
- {COEX_CU_CONNECTION_ESTAB_RP, COEX_CU_CONNECTION_ESTAB_WP,
- 0, COEX_CONNECTION_ESTAB_FLAGS},
- {COEX_CU_ASSOCIATED_IDLE_RP, COEX_CU_ASSOCIATED_IDLE_WP,
- 0, COEX_ASSOCIATED_IDLE_FLAGS},
- {COEX_CU_ASSOC_MANUAL_SCAN_RP, COEX_CU_ASSOC_MANUAL_SCAN_WP,
- 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
- {COEX_CU_ASSOC_AUTO_SCAN_RP, COEX_CU_ASSOC_AUTO_SCAN_WP,
- 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
- {COEX_CU_ASSOC_ACTIVE_LEVEL_RP, COEX_CU_ASSOC_ACTIVE_LEVEL_WP,
- 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
- {COEX_CU_RF_ON_RP, COEX_CU_RF_ON_WP, 0, COEX_CU_RF_ON_FLAGS},
- {COEX_CU_RF_OFF_RP, COEX_CU_RF_OFF_WP, 0, COEX_RF_OFF_FLAGS},
- {COEX_CU_STAND_ALONE_DEBUG_RP, COEX_CU_STAND_ALONE_DEBUG_WP,
- 0, COEX_STAND_ALONE_DEBUG_FLAGS},
- {COEX_CU_IPAN_ASSOC_LEVEL_RP, COEX_CU_IPAN_ASSOC_LEVEL_WP,
- 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
- {COEX_CU_RSRVD1_RP, COEX_CU_RSRVD1_WP, 0, COEX_RSRVD1_FLAGS},
- {COEX_CU_RSRVD2_RP, COEX_CU_RSRVD2_WP, 0, COEX_RSRVD2_FLAGS}
-};
-
/******************************************************************************
*
* uCode download functions
@@ -174,24 +143,6 @@ static int iwl_send_calib_cfg(struct iwl_priv *priv)
return iwl_dvm_send_cmd(priv, &cmd);
}
-int iwlagn_rx_calib_result(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb,
- struct iwl_device_cmd *cmd)
-{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->data;
- int len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
-
- /* reduce the size of the length field itself */
- len -= 4;
-
- if (iwl_calib_set(priv, hdr, len))
- IWL_ERR(priv, "Failed to record calibration data %d\n",
- hdr->op_code);
-
- return 0;
-}
-
int iwl_init_alive_start(struct iwl_priv *priv)
{
int ret;
@@ -233,25 +184,9 @@ static int iwl_send_wimax_coex(struct iwl_priv *priv)
{
struct iwl_wimax_coex_cmd coex_cmd;
- if (cfg(priv)->base_params->support_wimax_coexist) {
- /* UnMask wake up src at associated sleep */
- coex_cmd.flags = COEX_FLAGS_ASSOC_WA_UNMASK_MSK;
+ /* coexistence is disabled */
+ memset(&coex_cmd, 0, sizeof(coex_cmd));
- /* UnMask wake up src at unassociated sleep */
- coex_cmd.flags |= COEX_FLAGS_UNASSOC_WA_UNMASK_MSK;
- memcpy(coex_cmd.sta_prio, cu_priorities,
- sizeof(struct iwl_wimax_coex_event_entry) *
- COEX_NUM_OF_EVENTS);
-
- /* enabling the coexistence feature */
- coex_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK;
-
- /* enabling the priorities tables */
- coex_cmd.flags |= COEX_FLAGS_STA_TABLE_VALID_MSK;
- } else {
- /* coexistence is disabled */
- memset(&coex_cmd, 0, sizeof(coex_cmd));
- }
return iwl_dvm_send_cmd_pdu(priv,
COEX_PRIORITY_TABLE_CMD, CMD_SYNC,
sizeof(coex_cmd), &coex_cmd);
@@ -417,9 +352,8 @@ struct iwl_alive_data {
u8 subtype;
};
-static void iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt,
- void *data)
+static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
{
struct iwl_priv *priv =
container_of(notif_wait, struct iwl_priv, notif_wait);
@@ -433,13 +367,15 @@ static void iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
palive->is_valid, palive->ver_type,
palive->ver_subtype);
- priv->shrd->device_pointers.error_event_table =
+ priv->device_pointers.error_event_table =
le32_to_cpu(palive->error_event_table_ptr);
- priv->shrd->device_pointers.log_event_table =
+ priv->device_pointers.log_event_table =
le32_to_cpu(palive->log_event_table_ptr);
alive_data->subtype = palive->ver_subtype;
alive_data->valid = palive->is_valid == UCODE_VALID_OK;
+
+ return true;
}
#define UCODE_ALIVE_TIMEOUT HZ
@@ -453,9 +389,10 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
const struct fw_img *fw;
int ret;
enum iwl_ucode_type old_type;
+ static const u8 alive_cmd[] = { REPLY_ALIVE };
- old_type = priv->shrd->ucode_type;
- priv->shrd->ucode_type = ucode_type;
+ old_type = priv->cur_ucode;
+ priv->cur_ucode = ucode_type;
fw = iwl_get_ucode_image(priv, ucode_type);
priv->ucode_loaded = false;
@@ -463,12 +400,13 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
if (!fw)
return -EINVAL;
- iwl_init_notification_wait(&priv->notif_wait, &alive_wait, REPLY_ALIVE,
- iwl_alive_fn, &alive_data);
+ iwl_init_notification_wait(&priv->notif_wait, &alive_wait,
+ alive_cmd, ARRAY_SIZE(alive_cmd),
+ iwl_alive_fn, &alive_data);
ret = iwl_trans_start_fw(trans(priv), fw);
if (ret) {
- priv->shrd->ucode_type = old_type;
+ priv->cur_ucode = old_type;
iwl_remove_notification(&priv->notif_wait, &alive_wait);
return ret;
}
@@ -480,13 +418,13 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
ret = iwl_wait_notification(&priv->notif_wait, &alive_wait,
UCODE_ALIVE_TIMEOUT);
if (ret) {
- priv->shrd->ucode_type = old_type;
+ priv->cur_ucode = old_type;
return ret;
}
if (!alive_data.valid) {
IWL_ERR(priv, "Loaded ucode is not valid!\n");
- priv->shrd->ucode_type = old_type;
+ priv->cur_ucode = old_type;
return -EIO;
}
@@ -498,7 +436,7 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
if (ucode_type != IWL_UCODE_WOWLAN) {
ret = iwl_verify_ucode(priv, ucode_type);
if (ret) {
- priv->shrd->ucode_type = old_type;
+ priv->cur_ucode = old_type;
return ret;
}
@@ -510,7 +448,7 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
if (ret) {
IWL_WARN(priv,
"Could not complete ALIVE transition: %d\n", ret);
- priv->shrd->ucode_type = old_type;
+ priv->cur_ucode = old_type;
return ret;
}
@@ -519,9 +457,38 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
return 0;
}
+static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_priv *priv = data;
+ struct iwl_calib_hdr *hdr;
+ int len;
+
+ if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) {
+ WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION);
+ return true;
+ }
+
+ hdr = (struct iwl_calib_hdr *)pkt->data;
+ len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+
+ /* reduce the size by the length field itself */
+ len -= sizeof(__le32);
+
+ if (iwl_calib_set(priv, hdr, len))
+ IWL_ERR(priv, "Failed to record calibration data %d\n",
+ hdr->op_code);
+
+ return false;
+}
+
int iwl_run_init_ucode(struct iwl_priv *priv)
{
struct iwl_notification_wait calib_wait;
+ static const u8 calib_complete[] = {
+ CALIBRATION_RES_NOTIFICATION,
+ CALIBRATION_COMPLETE_NOTIFICATION
+ };
int ret;
lockdep_assert_held(&priv->mutex);
@@ -534,8 +501,8 @@ int iwl_run_init_ucode(struct iwl_priv *priv)
return 0;
iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
- CALIBRATION_COMPLETE_NOTIFICATION,
- NULL, NULL);
+ calib_complete, ARRAY_SIZE(calib_complete),
+ iwlagn_wait_calib, priv);
/* Will also start the device */
ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT);
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 538783f5198..2d913644d92 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -637,6 +637,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);
if (data->idle) {
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
@@ -671,6 +672,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
spin_lock(&hwsim_radio_lock);
list_for_each_entry(data2, &hwsim_radios, list) {
struct sk_buff *nskb;
+ struct ieee80211_mgmt *mgmt;
if (data == data2)
continue;
@@ -688,8 +690,17 @@ 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));
+ 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 +
+ 24 * 8 * 10 / txrate->bitrate);
+
memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
ieee80211_rx_irqsafe(data2->hw, nskb);
}
@@ -703,12 +714,6 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
bool ack;
struct ieee80211_tx_info *txi;
u32 _pid;
- struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
- struct mac80211_hwsim_data *data = hw->priv;
-
- if (ieee80211_is_beacon(mgmt->frame_control) ||
- ieee80211_is_probe_resp(mgmt->frame_control))
- mgmt->u.beacon.timestamp = __mac80211_hwsim_get_tsf(data);
mac80211_hwsim_monitor_rx(hw, skb);
@@ -805,11 +810,9 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
struct ieee80211_vif *vif)
{
struct ieee80211_hw *hw = arg;
- struct mac80211_hwsim_data *data = hw->priv;
struct sk_buff *skb;
struct ieee80211_tx_info *info;
u32 _pid;
- struct ieee80211_mgmt *mgmt;
hwsim_check_magic(vif);
@@ -823,9 +826,6 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
return;
info = IEEE80211_SKB_CB(skb);
- mgmt = (struct ieee80211_mgmt *) skb->data;
- mgmt->u.beacon.timestamp = __mac80211_hwsim_get_tsf(data);
-
mac80211_hwsim_monitor_rx(hw, skb);
/* wmediumd mode check */
@@ -1450,7 +1450,7 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");
-struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
struct mac_address *addr)
{
struct mac80211_hwsim_data *data;
@@ -1795,9 +1795,11 @@ static int __init init_mac80211_hwsim(void)
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_STATIC_SMPS |
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
- IEEE80211_HW_AMPDU_AGGREGATION;
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_WANT_MONITOR_VIF;
- hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index a5e182b5e94..fe8ebfebcc0 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -350,25 +350,26 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
ret_len += sizeof(struct mwifiex_ie_types_htcap);
}
- if (bss_desc->bcn_ht_info) {
+ if (bss_desc->bcn_ht_oper) {
if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
ht_info = (struct mwifiex_ie_types_htinfo *) *buffer;
memset(ht_info, 0,
sizeof(struct mwifiex_ie_types_htinfo));
ht_info->header.type =
- cpu_to_le16(WLAN_EID_HT_INFORMATION);
+ cpu_to_le16(WLAN_EID_HT_OPERATION);
ht_info->header.len =
- cpu_to_le16(sizeof(struct ieee80211_ht_info));
+ cpu_to_le16(
+ sizeof(struct ieee80211_ht_operation));
memcpy((u8 *) ht_info +
sizeof(struct mwifiex_ie_types_header),
- (u8 *) bss_desc->bcn_ht_info +
+ (u8 *) bss_desc->bcn_ht_oper +
sizeof(struct ieee_types_header),
le16_to_cpu(ht_info->header.len));
if (!(sband->ht_cap.cap &
IEEE80211_HT_CAP_SUP_WIDTH_20_40))
- ht_info->ht_info.ht_param &=
+ ht_info->ht_oper.ht_param &=
~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY |
IEEE80211_HT_PARAM_CHA_SEC_OFFSET);
@@ -385,16 +386,16 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
sizeof(struct mwifiex_ie_types_chan_list_param_set) -
sizeof(struct mwifiex_ie_types_header));
chan_list->chan_scan_param[0].chan_number =
- bss_desc->bcn_ht_info->control_chan;
+ bss_desc->bcn_ht_oper->primary_chan;
chan_list->chan_scan_param[0].radio_type =
mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
- bss_desc->bcn_ht_info->ht_param &
+ bss_desc->bcn_ht_oper->ht_param &
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)
SET_SECONDARYCHAN(chan_list->chan_scan_param[0].
radio_type,
- (bss_desc->bcn_ht_info->ht_param &
+ (bss_desc->bcn_ht_oper->ht_param &
IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
*buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set);
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 65050384c42..c7e89188c35 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -516,25 +516,23 @@ static int
mwifiex_dump_station_info(struct mwifiex_private *priv,
struct station_info *sinfo)
{
- struct mwifiex_ds_get_signal signal;
struct mwifiex_rate_cfg rate;
- int ret = 0;
sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES |
- STATION_INFO_RX_PACKETS |
- STATION_INFO_TX_PACKETS
- | STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE;
+ STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS |
+ STATION_INFO_TX_BITRATE |
+ STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
/* Get signal information from the firmware */
- memset(&signal, 0, sizeof(struct mwifiex_ds_get_signal));
- if (mwifiex_get_signal_info(priv, &signal)) {
- dev_err(priv->adapter->dev, "getting signal information\n");
- ret = -EFAULT;
+ if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_RSSI_INFO,
+ HostCmd_ACT_GEN_GET, 0, NULL)) {
+ dev_err(priv->adapter->dev, "failed to get signal information\n");
+ return -EFAULT;
}
if (mwifiex_drv_get_data_rate(priv, &rate)) {
dev_err(priv->adapter->dev, "getting data rate\n");
- ret = -EFAULT;
+ return -EFAULT;
}
/* Get DTIM period information from firmware */
@@ -557,11 +555,12 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
}
+ sinfo->signal_avg = priv->bcn_rssi_avg;
sinfo->rx_bytes = priv->stats.rx_bytes;
sinfo->tx_bytes = priv->stats.tx_bytes;
sinfo->rx_packets = priv->stats.rx_packets;
sinfo->tx_packets = priv->stats.tx_packets;
- sinfo->signal = priv->qual_level;
+ sinfo->signal = priv->bcn_rssi_avg;
/* bit rate is in 500 kb/s units. Convert it to 100kb/s units */
sinfo->txrate.legacy = rate.rate * 5;
@@ -581,7 +580,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
priv->curr_bss_params.bss_descriptor.beacon_period;
}
- return ret;
+ return 0;
}
/*
@@ -604,6 +603,23 @@ mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
return mwifiex_dump_station_info(priv, sinfo);
}
+/*
+ * CFG802.11 operation handler to dump station information.
+ */
+static int
+mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ if (!priv->media_connected || idx)
+ return -ENOENT;
+
+ memcpy(mac, priv->cfg_bssid, ETH_ALEN);
+
+ return mwifiex_dump_station_info(priv, sinfo);
+}
+
/* Supported rates to be advertised to the cfg80211 */
static struct ieee80211_rate mwifiex_rates[] = {
@@ -750,6 +766,45 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
}
/*
+ * CFG802.11 operation handler for connection quality monitoring.
+ *
+ * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI
+ * events to FW.
+ */
+static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_ds_misc_subsc_evt subsc_evt;
+
+ priv->cqm_rssi_thold = rssi_thold;
+ priv->cqm_rssi_hyst = rssi_hyst;
+
+ memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt));
+ subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
+
+ /* Subscribe/unsubscribe low and high rssi events */
+ if (rssi_thold && rssi_hyst) {
+ subsc_evt.action = HostCmd_ACT_BITWISE_SET;
+ subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold);
+ subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold);
+ subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
+ subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
+ return mwifiex_send_cmd_sync(priv,
+ HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt);
+ } else {
+ subsc_evt.action = HostCmd_ACT_BITWISE_CLR;
+ return mwifiex_send_cmd_sync(priv,
+ HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt);
+ }
+
+ return 0;
+}
+
+/*
* CFG802.11 operation handler for disconnection request.
*
* This function does not work when there is already a disconnection
@@ -1340,6 +1395,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.connect = mwifiex_cfg80211_connect,
.disconnect = mwifiex_cfg80211_disconnect,
.get_station = mwifiex_cfg80211_get_station,
+ .dump_station = mwifiex_cfg80211_dump_station,
.set_wiphy_params = mwifiex_cfg80211_set_wiphy_params,
.set_channel = mwifiex_cfg80211_set_channel,
.join_ibss = mwifiex_cfg80211_join_ibss,
@@ -1350,6 +1406,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.set_power_mgmt = mwifiex_cfg80211_set_power_mgmt,
.set_tx_power = mwifiex_cfg80211_set_tx_power,
.set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask,
+ .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
};
/*
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index e98fc5af73d..bb26114bdb9 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -92,10 +92,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0)
#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1)
#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2)
+#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4)
#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10)
#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16)
#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18)
#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19)
+#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22)
#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31)
#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42)
#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82)
@@ -194,6 +196,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e
#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c
#define HostCmd_CMD_WMM_GET_STATUS 0x0071
+#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075
#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f
#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083
#define HostCmd_CMD_VERSION_EXT 0x0097
@@ -228,6 +231,8 @@ enum ENH_PS_MODES {
#define HostCmd_RET_BIT 0x8000
#define HostCmd_ACT_GEN_GET 0x0000
#define HostCmd_ACT_GEN_SET 0x0001
+#define HostCmd_ACT_BITWISE_SET 0x0002
+#define HostCmd_ACT_BITWISE_CLR 0x0003
#define HostCmd_RESULT_OK 0x0000
#define HostCmd_ACT_MAC_RX_ON 0x0001
@@ -1007,7 +1012,7 @@ struct ieee_types_wmm_parameter {
struct ieee_types_vendor_header vend_hdr;
u8 qos_info_bitmap;
u8 reserved;
- struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_MAX_QUEUES];
+ struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
} __packed;
struct ieee_types_wmm_info {
@@ -1028,7 +1033,7 @@ struct ieee_types_wmm_info {
struct host_cmd_ds_wmm_get_status {
u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) *
- IEEE80211_MAX_QUEUES];
+ IEEE80211_NUM_ACS];
u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2];
} __packed;
@@ -1045,7 +1050,7 @@ struct mwifiex_ie_types_htcap {
struct mwifiex_ie_types_htinfo {
struct mwifiex_ie_types_header header;
- struct ieee80211_ht_info ht_info;
+ struct ieee80211_ht_operation ht_oper;
} __packed;
struct mwifiex_ie_types_2040bssco {
@@ -1146,6 +1151,17 @@ struct host_cmd_ds_pcie_details {
u32 sleep_cookie_addr_hi;
} __packed;
+struct mwifiex_ie_types_rssi_threshold {
+ struct mwifiex_ie_types_header header;
+ u8 abs_value;
+ u8 evt_freq;
+} __packed;
+
+struct host_cmd_ds_802_11_subsc_evt {
+ __le16 action;
+ __le16 events;
+} __packed;
+
struct host_cmd_ds_command {
__le16 command;
__le16 size;
@@ -1195,6 +1211,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_set_bss_mode bss_mode;
struct host_cmd_ds_pcie_details pcie_host_spec;
struct host_cmd_ds_802_11_eeprom_access eeprom;
+ struct host_cmd_ds_802_11_subsc_evt subsc_evt;
} params;
} __packed;
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index 7ca4e8234f3..99c06649f94 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -85,34 +85,6 @@ struct mwifiex_ds_get_stats {
u32 wep_icv_error[4];
};
-#define BCN_RSSI_AVG_MASK 0x00000002
-#define BCN_NF_AVG_MASK 0x00000200
-#define ALL_RSSI_INFO_MASK 0x00000fff
-
-struct mwifiex_ds_get_signal {
- /*
- * Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI,
- * Bit2: Last Data RSSI, Bit3: Average Data RSSI,
- * Bit4: Last Beacon SNR, Bit5: Average Beacon SNR,
- * Bit6: Last Data SNR, Bit7: Average Data SNR,
- * Bit8: Last Beacon NF, Bit9: Average Beacon NF,
- * Bit10: Last Data NF, Bit11: Average Data NF
- */
- u16 selector;
- s16 bcn_rssi_last;
- s16 bcn_rssi_avg;
- s16 data_rssi_last;
- s16 data_rssi_avg;
- s16 bcn_snr_last;
- s16 bcn_snr_avg;
- s16 data_snr_last;
- s16 data_snr_avg;
- s16 bcn_nf_last;
- s16 bcn_nf_avg;
- s16 data_nf_last;
- s16 data_nf_avg;
-};
-
#define MWIFIEX_MAX_VER_STR_LEN 128
struct mwifiex_ver_ext {
@@ -308,6 +280,27 @@ struct mwifiex_ds_misc_cmd {
u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER];
};
+#define BITMASK_BCN_RSSI_LOW BIT(0)
+#define BITMASK_BCN_RSSI_HIGH BIT(4)
+
+enum subsc_evt_rssi_state {
+ EVENT_HANDLED,
+ RSSI_LOW_RECVD,
+ RSSI_HIGH_RECVD
+};
+
+struct subsc_evt_cfg {
+ u8 abs_value;
+ u8 evt_freq;
+};
+
+struct mwifiex_ds_misc_subsc_evt {
+ u16 action;
+ u16 events;
+ struct subsc_evt_cfg bcn_l_rssi_cfg;
+ struct subsc_evt_cfg bcn_h_rssi_cfg;
+};
+
#define MWIFIEX_MAX_VSIE_LEN (256)
#define MWIFIEX_MAX_VSIE_NUM (8)
#define MWIFIEX_VSIE_MASK_SCAN 0x01
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 8f9382b9c3c..bca8b6d5227 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -932,20 +932,20 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
/* Fill HT INFORMATION */
ht_info = (struct mwifiex_ie_types_htinfo *) pos;
memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo));
- ht_info->header.type = cpu_to_le16(WLAN_EID_HT_INFORMATION);
+ ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION);
ht_info->header.len =
- cpu_to_le16(sizeof(struct ieee80211_ht_info));
+ cpu_to_le16(sizeof(struct ieee80211_ht_operation));
- ht_info->ht_info.control_chan =
+ ht_info->ht_oper.primary_chan =
(u8) priv->curr_bss_params.bss_descriptor.channel;
if (adapter->sec_chan_offset) {
- ht_info->ht_info.ht_param = adapter->sec_chan_offset;
- ht_info->ht_info.ht_param |=
+ ht_info->ht_oper.ht_param = adapter->sec_chan_offset;
+ ht_info->ht_oper.ht_param |=
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
}
- ht_info->ht_info.operation_mode =
+ ht_info->ht_oper.operation_mode =
cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
- ht_info->ht_info.basic_set[0] = 0xff;
+ ht_info->ht_oper.basic_set[0] = 0xff;
pos += sizeof(struct mwifiex_ie_types_htinfo);
cmd_append_size +=
sizeof(struct mwifiex_ie_types_htinfo);
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 35225e9b108..fcccf6b1373 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -201,10 +201,10 @@ struct mwifiex_wmm_desc {
u32 packets_out[MAX_NUM_TID];
/* spin lock to protect ra_list */
spinlock_t ra_list_spinlock;
- struct mwifiex_wmm_ac_status ac_status[IEEE80211_MAX_QUEUES];
- enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_MAX_QUEUES];
+ struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS];
+ enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS];
u32 drv_pkt_delay_max;
- u8 queue_priority[IEEE80211_MAX_QUEUES];
+ u8 queue_priority[IEEE80211_NUM_ACS];
u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */
/* Number of transmit packets queued */
atomic_t tx_pkts_queued;
@@ -269,7 +269,7 @@ struct mwifiex_bssdescriptor {
u8 disable_11n;
struct ieee80211_ht_cap *bcn_ht_cap;
u16 ht_cap_offset;
- struct ieee80211_ht_info *bcn_ht_info;
+ struct ieee80211_ht_operation *bcn_ht_oper;
u16 ht_info_offset;
u8 *bcn_bss_co_2040;
u16 bss_co_2040_offset;
@@ -448,7 +448,6 @@ struct mwifiex_private {
struct dentry *dfs_dev_dir;
#endif
u8 nick_name[16];
- u8 qual_level, qual_noise;
u16 current_key_index;
struct semaphore async_sem;
u8 scan_pending_on_block;
@@ -459,6 +458,9 @@ struct mwifiex_private {
u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
struct wps wps;
u8 scan_block;
+ s32 cqm_rssi_thold;
+ u32 cqm_rssi_hyst;
+ u8 subsc_evt_rssi_state;
};
enum mwifiex_ba_status {
@@ -896,8 +898,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type);
int mwifiex_enable_hs(struct mwifiex_adapter *adapter);
int mwifiex_disable_auto_ds(struct mwifiex_private *priv);
-int mwifiex_get_signal_info(struct mwifiex_private *priv,
- struct mwifiex_ds_get_signal *signal);
int mwifiex_drv_get_data_rate(struct mwifiex_private *priv,
struct mwifiex_rate_cfg *rate);
int mwifiex_request_scan(struct mwifiex_private *priv,
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index aff9cd763f2..ef84a1a6742 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -1221,9 +1221,9 @@ mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
sizeof(struct ieee_types_header) -
bss_entry->beacon_buf);
break;
- case WLAN_EID_HT_INFORMATION:
- bss_entry->bcn_ht_info = (struct ieee80211_ht_info *)
- (current_ptr +
+ case WLAN_EID_HT_OPERATION:
+ bss_entry->bcn_ht_oper =
+ (struct ieee80211_ht_operation *)(current_ptr +
sizeof(struct ieee_types_header));
bss_entry->ht_info_offset = (u16) (current_ptr +
sizeof(struct ieee_types_header) -
@@ -1493,7 +1493,7 @@ mwifiex_update_curr_bss_params(struct mwifiex_private *priv, u8 *bssid,
priv->curr_bss_params.bss_descriptor.bcn_ht_cap = NULL;
priv->curr_bss_params.bss_descriptor.ht_cap_offset =
0;
- priv->curr_bss_params.bss_descriptor.bcn_ht_info = NULL;
+ priv->curr_bss_params.bss_descriptor.bcn_ht_oper = NULL;
priv->curr_bss_params.bss_descriptor.ht_info_offset =
0;
priv->curr_bss_params.bss_descriptor.bcn_bss_co_2040 =
@@ -1667,8 +1667,9 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
memcpy(bssid, bcn_param->bssid, ETH_ALEN);
- rssi = (s32) (bcn_param->rssi);
- dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%02X\n", rssi);
+ rssi = (s32) bcn_param->rssi;
+ rssi = (-rssi) * 100; /* Convert dBm to mBm */
+ dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi);
beacon_period = le16_to_cpu(bcn_param->beacon_period);
@@ -2019,8 +2020,8 @@ mwifiex_save_curr_bcn(struct mwifiex_private *priv)
(curr_bss->beacon_buf +
curr_bss->ht_cap_offset);
- if (curr_bss->bcn_ht_info)
- curr_bss->bcn_ht_info = (struct ieee80211_ht_info *)
+ if (curr_bss->bcn_ht_oper)
+ curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *)
(curr_bss->beacon_buf +
curr_bss->ht_info_offset);
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index a3fb322205b..0ead152e3d1 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -193,7 +193,7 @@
a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \
a->mp_end_port))); \
a->mpa_tx.pkt_cnt++; \
-} while (0);
+} while (0)
/* SDIO Tx aggregation limit ? */
#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \
@@ -211,7 +211,7 @@
a->mpa_tx.buf_len = 0; \
a->mpa_tx.ports = 0; \
a->mpa_tx.start_port = 0; \
-} while (0);
+} while (0)
/* SDIO Rx aggregation limit ? */
#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \
@@ -242,7 +242,7 @@
a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \
a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \
a->mpa_rx.pkt_cnt++; \
-} while (0);
+} while (0)
/* Reset SDIO Rx aggregation buffer parameters */
#define MP_RX_AGGR_BUF_RESET(a) do { \
@@ -250,7 +250,7 @@
a->mpa_rx.buf_len = 0; \
a->mpa_rx.ports = 0; \
a->mpa_rx.start_port = 0; \
-} while (0);
+} while (0)
/* data structure for SDIO MPA TX */
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index 6c8e4594b48..e90c34d9c63 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -907,6 +907,101 @@ mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv,
}
/*
+ * This function prepares command for event subscription, configuration
+ * and query. Events can be subscribed or unsubscribed. Current subscribed
+ * events can be queried. Also, current subscribed events are reported in
+ * every FW response.
+ */
+static int
+mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_ds_misc_subsc_evt *subsc_evt_cfg)
+{
+ struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt;
+ struct mwifiex_ie_types_rssi_threshold *rssi_tlv;
+ u16 event_bitmap;
+ u8 *pos;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) +
+ S_DS_GEN);
+
+ subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action);
+ dev_dbg(priv->adapter->dev, "cmd: action: %d\n", subsc_evt_cfg->action);
+
+ /*For query requests, no configuration TLV structures are to be added.*/
+ if (subsc_evt_cfg->action == HostCmd_ACT_GEN_GET)
+ return 0;
+
+ subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events);
+
+ event_bitmap = subsc_evt_cfg->events;
+ dev_dbg(priv->adapter->dev, "cmd: event bitmap : %16x\n",
+ event_bitmap);
+
+ if (((subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) ||
+ (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_SET)) &&
+ (event_bitmap == 0)) {
+ dev_dbg(priv->adapter->dev, "Error: No event specified "
+ "for bitwise action type\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Append TLV structures for each of the specified events for
+ * subscribing or re-configuring. This is not required for
+ * bitwise unsubscribing request.
+ */
+ if (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR)
+ return 0;
+
+ pos = ((u8 *)subsc_evt) +
+ sizeof(struct host_cmd_ds_802_11_subsc_evt);
+
+ if (event_bitmap & BITMASK_BCN_RSSI_LOW) {
+ rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos;
+
+ rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW);
+ rssi_tlv->header.len =
+ cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) -
+ sizeof(struct mwifiex_ie_types_header));
+ rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value;
+ rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq;
+
+ dev_dbg(priv->adapter->dev, "Cfg Beacon Low Rssi event, "
+ "RSSI:-%d dBm, Freq:%d\n",
+ subsc_evt_cfg->bcn_l_rssi_cfg.abs_value,
+ subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq);
+
+ pos += sizeof(struct mwifiex_ie_types_rssi_threshold);
+ le16_add_cpu(&cmd->size,
+ sizeof(struct mwifiex_ie_types_rssi_threshold));
+ }
+
+ if (event_bitmap & BITMASK_BCN_RSSI_HIGH) {
+ rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos;
+
+ rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH);
+ rssi_tlv->header.len =
+ cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) -
+ sizeof(struct mwifiex_ie_types_header));
+ rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value;
+ rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq;
+
+ dev_dbg(priv->adapter->dev, "Cfg Beacon Low Rssi event, "
+ "RSSI:-%d dBm, Freq:%d\n",
+ subsc_evt_cfg->bcn_h_rssi_cfg.abs_value,
+ subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq);
+
+ pos += sizeof(struct mwifiex_ie_types_rssi_threshold);
+ le16_add_cpu(&cmd->size,
+ sizeof(struct mwifiex_ie_types_rssi_threshold));
+ }
+
+ return 0;
+}
+
+/*
* This function prepares the commands before sending them to the firmware.
*
* This is a generic function which calls specific command preparation
@@ -1086,6 +1181,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_PCIE_DESC_DETAILS:
ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action);
break;
+ case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
+ ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf);
+ break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd- %#x\n", cmd_no);
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index 4da19ed0f07..3aa54243dea 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -119,11 +119,11 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
* calculated SNR values.
*/
static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
- struct host_cmd_ds_command *resp,
- struct mwifiex_ds_get_signal *signal)
+ struct host_cmd_ds_command *resp)
{
struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp =
&resp->params.rssi_info_rsp;
+ struct mwifiex_ds_misc_subsc_evt subsc_evt;
priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last);
priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last);
@@ -137,34 +137,29 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg);
priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg);
- /* Need to indicate IOCTL complete */
- if (signal) {
- memset(signal, 0, sizeof(*signal));
-
- signal->selector = ALL_RSSI_INFO_MASK;
-
- /* RSSI */
- signal->bcn_rssi_last = priv->bcn_rssi_last;
- signal->bcn_rssi_avg = priv->bcn_rssi_avg;
- signal->data_rssi_last = priv->data_rssi_last;
- signal->data_rssi_avg = priv->data_rssi_avg;
-
- /* SNR */
- signal->bcn_snr_last =
- CAL_SNR(priv->bcn_rssi_last, priv->bcn_nf_last);
- signal->bcn_snr_avg =
- CAL_SNR(priv->bcn_rssi_avg, priv->bcn_nf_avg);
- signal->data_snr_last =
- CAL_SNR(priv->data_rssi_last, priv->data_nf_last);
- signal->data_snr_avg =
- CAL_SNR(priv->data_rssi_avg, priv->data_nf_avg);
-
- /* NF */
- signal->bcn_nf_last = priv->bcn_nf_last;
- signal->bcn_nf_avg = priv->bcn_nf_avg;
- signal->data_nf_last = priv->data_nf_last;
- signal->data_nf_avg = priv->data_nf_avg;
+ if (priv->subsc_evt_rssi_state == EVENT_HANDLED)
+ return 0;
+
+ /* Resubscribe low and high rssi events with new thresholds */
+ memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt));
+ subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
+ subsc_evt.action = HostCmd_ACT_BITWISE_SET;
+ if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) {
+ subsc_evt.bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg -
+ priv->cqm_rssi_hyst);
+ subsc_evt.bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold);
+ } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) {
+ subsc_evt.bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold);
+ subsc_evt.bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg +
+ priv->cqm_rssi_hyst);
}
+ subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
+ subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
+
+ priv->subsc_evt_rssi_state = EVENT_HANDLED;
+
+ mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt);
return 0;
}
@@ -785,6 +780,28 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv,
}
/*
+ * This function handles the command response for subscribe event command.
+ */
+static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp,
+ struct mwifiex_ds_misc_subsc_evt *sub_event)
+{
+ struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event =
+ (struct host_cmd_ds_802_11_subsc_evt *)&resp->params.subsc_evt;
+
+ /* For every subscribe event command (Get/Set/Clear), FW reports the
+ * current set of subscribed events*/
+ dev_dbg(priv->adapter->dev, "Bitmap of currently subscribed events: %16x\n",
+ le16_to_cpu(cmd_sub_event->events));
+
+ /*Return the subscribed event info for a Get request*/
+ if (sub_event)
+ sub_event->events = le16_to_cpu(cmd_sub_event->events);
+
+ return 0;
+}
+
+/*
* This function handles the command responses.
*
* This is a generic function, which calls command specific
@@ -853,7 +870,7 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
ret = mwifiex_ret_get_log(priv, resp, data_buf);
break;
case HostCmd_CMD_RSSI_INFO:
- ret = mwifiex_ret_802_11_rssi_info(priv, resp, data_buf);
+ ret = mwifiex_ret_802_11_rssi_info(priv, resp);
break;
case HostCmd_CMD_802_11_SNMP_MIB:
ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf);
@@ -924,6 +941,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
break;
case HostCmd_CMD_PCIE_DESC_DETAILS:
break;
+ case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
+ ret = mwifiex_ret_subsc_evt(priv, resp, data_buf);
+ break;
default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
resp->command);
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index cc531b536a5..f6bbb9307f8 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -128,9 +128,6 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv)
mwifiex_stop_net_dev_queue(priv->netdev, adapter);
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
- /* Reset wireless stats signal info */
- priv->qual_level = 0;
- priv->qual_noise = 0;
}
/*
@@ -317,6 +314,12 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
break;
case EVENT_RSSI_LOW:
+ cfg80211_cqm_rssi_notify(priv->netdev,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ GFP_KERNEL);
+ mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO,
+ HostCmd_ACT_GEN_GET, 0, NULL);
+ priv->subsc_evt_rssi_state = RSSI_LOW_RECVD;
dev_dbg(adapter->dev, "event: Beacon RSSI_LOW\n");
break;
case EVENT_SNR_LOW:
@@ -326,6 +329,12 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
dev_dbg(adapter->dev, "event: MAX_FAIL\n");
break;
case EVENT_RSSI_HIGH:
+ cfg80211_cqm_rssi_notify(priv->netdev,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ GFP_KERNEL);
+ mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO,
+ HostCmd_ACT_GEN_GET, 0, NULL);
+ priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD;
dev_dbg(adapter->dev, "event: Beacon RSSI_HIGH\n");
break;
case EVENT_SNR_HIGH:
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index d7b11defafe..8ba58d93532 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -1185,39 +1185,6 @@ mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version,
}
/*
- * Sends IOCTL request to get signal information.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int mwifiex_get_signal_info(struct mwifiex_private *priv,
- struct mwifiex_ds_get_signal *signal)
-{
- int status;
-
- signal->selector = ALL_RSSI_INFO_MASK;
-
- /* Signal info can be obtained only if connected */
- if (!priv->media_connected) {
- dev_dbg(priv->adapter->dev,
- "info: Can not get signal in disconnected state\n");
- return -1;
- }
-
- status = mwifiex_send_cmd_sync(priv, HostCmd_CMD_RSSI_INFO,
- HostCmd_ACT_GEN_GET, 0, signal);
-
- if (!status) {
- if (signal->selector & BCN_RSSI_AVG_MASK)
- priv->qual_level = signal->bcn_rssi_avg;
- if (signal->selector & BCN_NF_AVG_MASK)
- priv->qual_noise = signal->bcn_nf_avg;
- }
-
- return status;
-}
-
-/*
* Sends IOCTL request to set encoding parameters.
*
* This function allocates the IOCTL request buffer, fills it
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index ee8af1f047c..7cffea795ad 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -796,11 +796,14 @@ int p54_register_common(struct ieee80211_hw *dev, struct device *pdev)
dev_err(pdev, "Cannot register device (%d).\n", err);
return err;
}
+ priv->registered = true;
#ifdef CONFIG_P54_LEDS
err = p54_init_leds(priv);
- if (err)
+ if (err) {
+ p54_unregister_common(dev);
return err;
+ }
#endif /* CONFIG_P54_LEDS */
dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy));
@@ -840,7 +843,11 @@ void p54_unregister_common(struct ieee80211_hw *dev)
p54_unregister_leds(priv);
#endif /* CONFIG_P54_LEDS */
- ieee80211_unregister_hw(dev);
+ if (priv->registered) {
+ priv->registered = false;
+ ieee80211_unregister_hw(dev);
+ }
+
mutex_destroy(&priv->conf_mutex);
mutex_destroy(&priv->eeprom_mutex);
}
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
index 452fa3a64aa..40b401ed684 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/p54/p54.h
@@ -173,6 +173,7 @@ struct p54_common {
struct sk_buff_head tx_pending;
struct sk_buff_head tx_queue;
struct mutex conf_mutex;
+ bool registered;
/* memory management (as seen by the firmware) */
u32 rx_start;
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index f4d28c39aac..bac3d03f578 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -117,21 +117,18 @@ static const struct {
u32 intf;
enum p54u_hw_type type;
const char *fw;
- const char *fw_legacy;
char hw[20];
} p54u_fwlist[__NUM_P54U_HWTYPES] = {
{
.type = P54U_NET2280,
.intf = FW_LM86,
.fw = "isl3886usb",
- .fw_legacy = "isl3890usb",
.hw = "ISL3886 + net2280",
},
{
.type = P54U_3887,
.intf = FW_LM87,
.fw = "isl3887usb",
- .fw_legacy = "isl3887usb_bare",
.hw = "ISL3887",
},
};
@@ -208,6 +205,16 @@ static void p54u_free_urbs(struct ieee80211_hw *dev)
usb_kill_anchored_urbs(&priv->submitted);
}
+static void p54u_stop(struct ieee80211_hw *dev)
+{
+ /*
+ * TODO: figure out how to reliably stop the 3887 and net2280 so
+ * the hardware is still usable next time we want to start it.
+ * until then, we just stop listening to the hardware..
+ */
+ p54u_free_urbs(dev);
+}
+
static int p54u_init_urbs(struct ieee80211_hw *dev)
{
struct p54u_priv *priv = dev->priv;
@@ -257,6 +264,16 @@ static int p54u_init_urbs(struct ieee80211_hw *dev)
return ret;
}
+static int p54u_open(struct ieee80211_hw *dev)
+{
+ /*
+ * TODO: Because we don't know how to reliably stop the 3887 and
+ * the isl3886+net2280, other than brutally cut off all
+ * communications. We have to reinitialize the urbs on every start.
+ */
+ return p54u_init_urbs(dev);
+}
+
static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
{
u32 chk = 0;
@@ -836,70 +853,137 @@ fail:
return err;
}
-static int p54u_load_firmware(struct ieee80211_hw *dev)
+static int p54_find_type(struct p54u_priv *priv)
{
- struct p54u_priv *priv = dev->priv;
- int err, i;
-
- BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
+ int i;
for (i = 0; i < __NUM_P54U_HWTYPES; i++)
if (p54u_fwlist[i].type == priv->hw_type)
break;
-
if (i == __NUM_P54U_HWTYPES)
return -EOPNOTSUPP;
- err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev);
- if (err) {
- dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
- "(%d)!\n", p54u_fwlist[i].fw, err);
+ return i;
+}
- err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
- &priv->udev->dev);
- if (err)
- return err;
- }
+static int p54u_start_ops(struct p54u_priv *priv)
+{
+ struct ieee80211_hw *dev = priv->common.hw;
+ int ret;
- err = p54_parse_firmware(dev, priv->fw);
- if (err)
- goto out;
+ ret = p54_parse_firmware(dev, priv->fw);
+ if (ret)
+ goto err_out;
+
+ ret = p54_find_type(priv);
+ if (ret < 0)
+ goto err_out;
- if (priv->common.fw_interface != p54u_fwlist[i].intf) {
+ if (priv->common.fw_interface != p54u_fwlist[ret].intf) {
dev_err(&priv->udev->dev, "wrong firmware, please get "
"a firmware for \"%s\" and try again.\n",
- p54u_fwlist[i].hw);
- err = -EINVAL;
+ p54u_fwlist[ret].hw);
+ ret = -ENODEV;
+ goto err_out;
}
-out:
- if (err)
- release_firmware(priv->fw);
+ ret = priv->upload_fw(dev);
+ if (ret)
+ goto err_out;
- return err;
+ ret = p54u_open(dev);
+ if (ret)
+ goto err_out;
+
+ ret = p54_read_eeprom(dev);
+ if (ret)
+ goto err_stop;
+
+ p54u_stop(dev);
+
+ ret = p54_register_common(dev, &priv->udev->dev);
+ if (ret)
+ goto err_stop;
+
+ return 0;
+
+err_stop:
+ p54u_stop(dev);
+
+err_out:
+ /*
+ * p54u_disconnect will do the rest of the
+ * cleanup
+ */
+ return ret;
}
-static int p54u_open(struct ieee80211_hw *dev)
+static void p54u_load_firmware_cb(const struct firmware *firmware,
+ void *context)
{
- struct p54u_priv *priv = dev->priv;
+ struct p54u_priv *priv = context;
+ struct usb_device *udev = priv->udev;
int err;
- err = p54u_init_urbs(dev);
- if (err) {
- return err;
+ complete(&priv->fw_wait_load);
+ if (firmware) {
+ priv->fw = firmware;
+ err = p54u_start_ops(priv);
+ } else {
+ err = -ENOENT;
+ dev_err(&udev->dev, "Firmware not found.\n");
}
- priv->common.open = p54u_init_urbs;
+ if (err) {
+ struct device *parent = priv->udev->dev.parent;
- return 0;
+ dev_err(&udev->dev, "failed to initialize device (%d)\n", err);
+
+ if (parent)
+ device_lock(parent);
+
+ device_release_driver(&udev->dev);
+ /*
+ * At this point p54u_disconnect has already freed
+ * the "priv" context. Do not use it anymore!
+ */
+ priv = NULL;
+
+ if (parent)
+ device_unlock(parent);
+ }
+
+ usb_put_dev(udev);
}
-static void p54u_stop(struct ieee80211_hw *dev)
+static int p54u_load_firmware(struct ieee80211_hw *dev,
+ struct usb_interface *intf)
{
- /* TODO: figure out how to reliably stop the 3887 and net2280 so
- the hardware is still usable next time we want to start it.
- until then, we just stop listening to the hardware.. */
- p54u_free_urbs(dev);
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct p54u_priv *priv = dev->priv;
+ struct device *device = &udev->dev;
+ int err, i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
+
+ init_completion(&priv->fw_wait_load);
+ i = p54_find_type(priv);
+ if (i < 0)
+ return i;
+
+ dev_info(&priv->udev->dev, "Loading firmware file %s\n",
+ p54u_fwlist[i].fw);
+
+ usb_get_dev(udev);
+ err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw,
+ device, GFP_KERNEL, priv,
+ p54u_load_firmware_cb);
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
+ "(%d)!\n", p54u_fwlist[i].fw, err);
+ }
+
+ return err;
}
static int __devinit p54u_probe(struct usb_interface *intf,
@@ -969,33 +1053,7 @@ static int __devinit p54u_probe(struct usb_interface *intf,
priv->common.tx = p54u_tx_net2280;
priv->upload_fw = p54u_upload_firmware_net2280;
}
- err = p54u_load_firmware(dev);
- if (err)
- goto err_free_dev;
-
- err = priv->upload_fw(dev);
- if (err)
- goto err_free_fw;
-
- p54u_open(dev);
- err = p54_read_eeprom(dev);
- p54u_stop(dev);
- if (err)
- goto err_free_fw;
-
- err = p54_register_common(dev, &udev->dev);
- if (err)
- goto err_free_fw;
-
- return 0;
-
-err_free_fw:
- release_firmware(priv->fw);
-
-err_free_dev:
- p54_free_common(dev);
- usb_set_intfdata(intf, NULL);
- usb_put_dev(udev);
+ err = p54u_load_firmware(dev, intf);
return err;
}
@@ -1007,9 +1065,10 @@ static void __devexit p54u_disconnect(struct usb_interface *intf)
if (!dev)
return;
+ priv = dev->priv;
+ wait_for_completion(&priv->fw_wait_load);
p54_unregister_common(dev);
- priv = dev->priv;
usb_put_dev(interface_to_usbdev(intf));
release_firmware(priv->fw);
p54_free_common(dev);
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h
index ed4034ade59..d273be7272b 100644
--- a/drivers/net/wireless/p54/p54usb.h
+++ b/drivers/net/wireless/p54/p54usb.h
@@ -143,6 +143,9 @@ struct p54u_priv {
struct sk_buff_head rx_queue;
struct usb_anchor submitted;
const struct firmware *fw;
+
+ /* asynchronous firmware callback */
+ struct completion fw_wait_load;
};
#endif /* P54USB_H */
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c
index a08a6f0e4dd..7c8f118c2b0 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/p54/txrx.c
@@ -914,8 +914,7 @@ void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb)
txhdr->hw_queue = queue;
txhdr->backlog = priv->tx_stats[queue].len - 1;
memset(txhdr->durations, 0, sizeof(txhdr->durations));
- txhdr->tx_antenna = ((info->antenna_sel_tx == 0) ?
- 2 : info->antenna_sel_tx - 1) & priv->tx_diversity_mask;
+ txhdr->tx_antenna = 2 & priv->tx_diversity_mask;
if (priv->rxhw == 5) {
txhdr->longbow.cts_rate = cts_rate;
txhdr->longbow.output_power = cpu_to_le16(priv->output_power);
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 471f87cab4a..5583214721e 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -692,6 +692,8 @@ enum rt2x00_state_flags {
*/
CONFIG_CHANNEL_HT40,
CONFIG_POWERSAVING,
+ CONFIG_HT_DISABLED,
+ CONFIG_QOS_DISABLED,
/*
* Mark we currently are sequentially reading TX_STA_FIFO register
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c
index 293676bfa57..e7361d913e8 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/rt2x00/rt2x00config.c
@@ -217,6 +217,11 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
libconf.conf = conf;
if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) {
+ if (!conf_is_ht(conf))
+ set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);
+ else
+ clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);
+
if (conf_is_ht40(conf)) {
set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);
hw_value = rt2x00ht_center_channel(rt2x00dev, conf);
diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/rt2x00/rt2x00leds.c
index ca585e34d00..8679d781a26 100644
--- a/drivers/net/wireless/rt2x00/rt2x00leds.c
+++ b/drivers/net/wireless/rt2x00/rt2x00leds.c
@@ -124,17 +124,15 @@ static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
void rt2x00leds_register(struct rt2x00_dev *rt2x00dev)
{
- char dev_name[16];
- char name[32];
+ char name[36];
int retval;
unsigned long on_period;
unsigned long off_period;
-
- snprintf(dev_name, sizeof(dev_name), "%s-%s",
- rt2x00dev->ops->name, wiphy_name(rt2x00dev->hw->wiphy));
+ const char *phy_name = wiphy_name(rt2x00dev->hw->wiphy);
if (rt2x00dev->led_radio.flags & LED_INITIALIZED) {
- snprintf(name, sizeof(name), "%s::radio", dev_name);
+ snprintf(name, sizeof(name), "%s-%s::radio",
+ rt2x00dev->ops->name, phy_name);
retval = rt2x00leds_register_led(rt2x00dev,
&rt2x00dev->led_radio,
@@ -144,7 +142,8 @@ void rt2x00leds_register(struct rt2x00_dev *rt2x00dev)
}
if (rt2x00dev->led_assoc.flags & LED_INITIALIZED) {
- snprintf(name, sizeof(name), "%s::assoc", dev_name);
+ snprintf(name, sizeof(name), "%s-%s::assoc",
+ rt2x00dev->ops->name, phy_name);
retval = rt2x00leds_register_led(rt2x00dev,
&rt2x00dev->led_assoc,
@@ -154,7 +153,8 @@ void rt2x00leds_register(struct rt2x00_dev *rt2x00dev)
}
if (rt2x00dev->led_qual.flags & LED_INITIALIZED) {
- snprintf(name, sizeof(name), "%s::quality", dev_name);
+ snprintf(name, sizeof(name), "%s-%s::quality",
+ rt2x00dev->ops->name, phy_name);
retval = rt2x00leds_register_led(rt2x00dev,
&rt2x00dev->led_qual,
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 2df2eb6d3e0..b49773ef72f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -709,9 +709,19 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
rt2x00dev->intf_associated--;
rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated);
+
+ clear_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags);
}
/*
+ * Check for access point which do not support 802.11e . We have to
+ * generate data frames sequence number in S/W for such AP, because
+ * of H/W bug.
+ */
+ if (changes & BSS_CHANGED_QOS && !bss_conf->qos)
+ set_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags);
+
+ /*
* When the erp information has changed, we should perform
* additional configuration steps. For all other changes we are done.
*/
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 9b1b2b7a780..8ecf409476c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -213,8 +213,19 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
__set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
- if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags))
- return;
+ if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags)) {
+ /*
+ * rt2800 has a H/W (or F/W) bug, device incorrectly increase
+ * seqno on retransmited data (non-QOS) frames. To workaround
+ * the problem let's generate seqno in software if QOS is
+ * disabled.
+ */
+ if (test_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags))
+ __clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags);
+ else
+ /* H/W will generate sequence number */
+ return;
+ }
/*
* The hardware is not able to insert a sequence number. Assign a
@@ -320,14 +331,6 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev,
txdesc->u.ht.wcid = sta_priv->wcid;
}
- txdesc->u.ht.ba_size = 7; /* FIXME: What value is needed? */
-
- /*
- * Only one STBC stream is supported for now.
- */
- if (tx_info->flags & IEEE80211_TX_CTL_STBC)
- txdesc->u.ht.stbc = 1;
-
/*
* If IEEE80211_TX_RC_MCS is set txrate->idx just contains the
* mcs rate to be used
@@ -351,6 +354,24 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev,
txdesc->u.ht.mcs |= 0x08;
}
+ if (test_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags)) {
+ if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT))
+ txdesc->u.ht.txop = TXOP_SIFS;
+ else
+ txdesc->u.ht.txop = TXOP_BACKOFF;
+
+ /* Left zero on all other settings. */
+ return;
+ }
+
+ txdesc->u.ht.ba_size = 7; /* FIXME: What value is needed? */
+
+ /*
+ * Only one STBC stream is supported for now.
+ */
+ if (tx_info->flags & IEEE80211_TX_CTL_STBC)
+ txdesc->u.ht.stbc = 1;
+
/*
* This frame is eligible for an AMPDU, however, don't aggregate
* frames that are intended to probe a specific tx rate.
diff --git a/drivers/net/wireless/rtlwifi/cam.c b/drivers/net/wireless/rtlwifi/cam.c
index 5c7d57947d2..3d8cc4a0c86 100644
--- a/drivers/net/wireless/rtlwifi/cam.c
+++ b/drivers/net/wireless/rtlwifi/cam.c
@@ -328,10 +328,9 @@ void rtl_cam_del_entry(struct ieee80211_hw *hw, u8 *sta_addr)
RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG, "sta_addr is NULL\n");
}
- if ((sta_addr[0]|sta_addr[1]|sta_addr[2]|sta_addr[3]|\
- sta_addr[4]|sta_addr[5]) == 0) {
+ if (is_zero_ether_addr(sta_addr)) {
RT_TRACE(rtlpriv, COMP_SEC, DBG_EMERG,
- "sta_addr is 00:00:00:00:00:00\n");
+ "sta_addr is %pM\n", sta_addr);
return;
}
/* Does STA already exist? */
diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c
index c66f08a0524..d5cbf01da8a 100644
--- a/drivers/net/wireless/rtlwifi/rc.c
+++ b/drivers/net/wireless/rtlwifi/rc.c
@@ -225,8 +225,7 @@ static void rtl_rate_init(void *ppriv,
static void rtl_rate_update(void *ppriv,
struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
- u32 changed,
- enum nl80211_channel_type oper_chan_type)
+ u32 changed)
{
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
index efb9ab27040..c4adb977736 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
@@ -530,12 +530,7 @@
SET_BITS_OFFSET_LE(__pdesc+28, 0, 32, __val)
#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \
-do { \
- if (_size > TX_DESC_NEXT_DESC_OFFSET) \
- memset(__pdesc, 0, TX_DESC_NEXT_DESC_OFFSET); \
- else \
- memset(__pdesc, 0, _size); \
-} while (0);
+ memset(__pdesc, 0, min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET))
struct rx_fwinfo_92c {
u8 gain_trsw[4];
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h
index 0dc736c2723..057a52431b0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h
@@ -530,12 +530,8 @@
SET_BITS_OFFSET_LE(__pdesc+28, 0, 32, __val)
#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \
-do { \
- if (_size > TX_DESC_NEXT_DESC_OFFSET) \
- memset((void *)__pdesc, 0, TX_DESC_NEXT_DESC_OFFSET); \
- else \
- memset((void *)__pdesc, 0, _size); \
-} while (0);
+ memset((void *)__pdesc, 0, \
+ min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET))
/* For 92D early mode */
#define SET_EARLYMODE_PKTNUM(__paddr, __value) \
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
index d1b0a1e1497..20afec62ce0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
@@ -252,12 +252,7 @@
* the desc is cleared. */
#define TX_DESC_NEXT_DESC_OFFSET 36
#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \
-do { \
- if (_size > TX_DESC_NEXT_DESC_OFFSET) \
- memset(__pdesc, 0, TX_DESC_NEXT_DESC_OFFSET); \
- else \
- memset(__pdesc, 0, _size); \
-} while (0);
+ memset(__pdesc, 0, min_t(size_t, _size, TX_DESC_NEXT_DESC_OFFSET))
/* Rx Desc */
#define RX_STATUS_DESC_SIZE 24
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/fw.h b/drivers/net/wireless/rtlwifi/rtl8192se/fw.h
index b4afff62643..d53f4332464 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/fw.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/fw.h
@@ -345,7 +345,7 @@ enum fw_h2c_cmd {
do { \
udelay(1000); \
rtlpriv->rtlhal.fwcmd_iomap &= (~_Bit); \
- } while (0);
+ } while (0)
#define FW_CMD_IO_UPDATE(rtlpriv, _val) \
rtlpriv->rtlhal.fwcmd_iomap = _val;
@@ -354,13 +354,13 @@ enum fw_h2c_cmd {
do { \
rtl_write_word(rtlpriv, LBUS_MON_ADDR, (u16)_val); \
FW_CMD_IO_UPDATE(rtlpriv, _val); \
- } while (0);
+ } while (0)
#define FW_CMD_PARA_SET(rtlpriv, _val) \
do { \
rtl_write_dword(rtlpriv, LBUS_ADDR_MASK, _val); \
rtlpriv->rtlhal.fwcmd_ioparam = _val; \
- } while (0);
+ } while (0)
#define FW_CMD_IO_QUERY(rtlpriv) \
(u16)(rtlpriv->rtlhal.fwcmd_iomap)
diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h
index 28ebc69218a..52139880309 100644
--- a/drivers/net/wireless/rtlwifi/wifi.h
+++ b/drivers/net/wireless/rtlwifi/wifi.h
@@ -1958,37 +1958,35 @@ static inline void rtl_write_dword(struct rtl_priv *rtlpriv,
static inline u32 rtl_get_bbreg(struct ieee80211_hw *hw,
u32 regaddr, u32 bitmask)
{
- return ((struct rtl_priv *)(hw)->priv)->cfg->ops->get_bbreg(hw,
- regaddr,
- bitmask);
+ struct rtl_priv *rtlpriv = hw->priv;
+
+ return rtlpriv->cfg->ops->get_bbreg(hw, regaddr, bitmask);
}
static inline void rtl_set_bbreg(struct ieee80211_hw *hw, u32 regaddr,
u32 bitmask, u32 data)
{
- ((struct rtl_priv *)(hw)->priv)->cfg->ops->set_bbreg(hw,
- regaddr, bitmask,
- data);
+ struct rtl_priv *rtlpriv = hw->priv;
+ rtlpriv->cfg->ops->set_bbreg(hw, regaddr, bitmask, data);
}
static inline u32 rtl_get_rfreg(struct ieee80211_hw *hw,
enum radio_path rfpath, u32 regaddr,
u32 bitmask)
{
- return ((struct rtl_priv *)(hw)->priv)->cfg->ops->get_rfreg(hw,
- rfpath,
- regaddr,
- bitmask);
+ struct rtl_priv *rtlpriv = hw->priv;
+
+ return rtlpriv->cfg->ops->get_rfreg(hw, rfpath, regaddr, bitmask);
}
static inline void rtl_set_rfreg(struct ieee80211_hw *hw,
enum radio_path rfpath, u32 regaddr,
u32 bitmask, u32 data)
{
- ((struct rtl_priv *)(hw)->priv)->cfg->ops->set_rfreg(hw,
- rfpath, regaddr,
- bitmask, data);
+ struct rtl_priv *rtlpriv = hw->priv;
+
+ rtlpriv->cfg->ops->set_rfreg(hw, rfpath, regaddr, bitmask, data);
}
static inline bool is_hal_stop(struct rtl_hal *rtlhal)
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 39002363611..362ff1a7067 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -232,7 +232,7 @@ static struct conf_drv_settings default_conf = {
.rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
},
[1] = {
- .ie = WLAN_EID_HT_INFORMATION,
+ .ie = WLAN_EID_HT_OPERATION,
.rule = CONF_BCN_RULE_PASS_ON_CHANGE,
},
},
@@ -5242,7 +5242,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->flags |= WIPHY_FLAG_AP_UAPSD;
+ wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
/* make sure all our channels fit in the scanned_ch bitmask */
BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 210e2c32553..db84e2f6b28 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -640,9 +640,9 @@ struct ieee80211_rann_ie {
u8 rann_hopcount;
u8 rann_ttl;
u8 rann_addr[6];
- u32 rann_seq;
- u32 rann_interval;
- u32 rann_metric;
+ __le32 rann_seq;
+ __le32 rann_interval;
+ __le32 rann_metric;
} __attribute__ ((packed));
enum ieee80211_rann_flags {
@@ -1007,13 +1007,13 @@ enum ieee80211_min_mpdu_spacing {
};
/**
- * struct ieee80211_ht_info - HT information
+ * struct ieee80211_ht_operation - HT operation IE
*
- * This structure is the "HT information element" as
- * described in 802.11n D5.0 7.3.2.58
+ * This structure is the "HT operation element" as
+ * described in 802.11n-2009 7.3.2.57
*/
-struct ieee80211_ht_info {
- u8 control_chan;
+struct ieee80211_ht_operation {
+ u8 primary_chan;
u8 ht_param;
__le16 operation_mode;
__le16 stbc_param;
@@ -1027,8 +1027,6 @@ struct ieee80211_ht_info {
#define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03
#define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04
#define IEEE80211_HT_PARAM_RIFS_MODE 0x08
-#define IEEE80211_HT_PARAM_SPSMP_SUPPORT 0x10
-#define IEEE80211_HT_PARAM_SERV_INTERVAL_GRAN 0xE0
/* for operation_mode */
#define IEEE80211_HT_OP_MODE_PROTECTION 0x0003
@@ -1301,7 +1299,7 @@ enum ieee80211_eid {
WLAN_EID_EXT_SUPP_RATES = 50,
WLAN_EID_HT_CAPABILITY = 45,
- WLAN_EID_HT_INFORMATION = 61,
+ WLAN_EID_HT_OPERATION = 61,
WLAN_EID_RSN = 48,
WLAN_EID_MMIE = 76,
@@ -1441,6 +1439,18 @@ enum ieee80211_tdls_actioncode {
#define WLAN_TDLS_SNAP_RFTYPE 0x2
/**
+ * enum - mesh synchronization method identifier
+ *
+ * @IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET: the default synchronization method
+ * @IEEE80211_SYNC_METHOD_VENDOR: a vendor specific synchronization method
+ * that will be specified in a vendor specific information element
+ */
+enum {
+ IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET = 1,
+ IEEE80211_SYNC_METHOD_VENDOR = 255,
+};
+
+/**
* enum - mesh path selection protocol identifier
*
* @IEEE80211_PATH_PROTOCOL_HWMP: the default path selection protocol
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index e474f6e780c..1335084b1c6 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -548,6 +548,11 @@
* @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
* No Acknowledgement Policy should be applied.
*
+ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
+ * independently of the userspace SME, send this event indicating
+ * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with
+ * %NL80211_ATTR_WIPHY_CHANNEL_TYPE.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -689,6 +694,8 @@ enum nl80211_commands {
NL80211_CMD_SET_NOACK_MAP,
+ NL80211_CMD_CH_SWITCH_NOTIFY,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1685,6 +1692,7 @@ enum nl80211_sta_bss_param {
* @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
* @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_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
@@ -1708,6 +1716,7 @@ enum nl80211_sta_info {
NL80211_STA_INFO_CONNECTED_TIME,
NL80211_STA_INFO_STA_FLAGS,
NL80211_STA_INFO_BEACON_LOSS,
+ NL80211_STA_INFO_T_OFFSET,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@@ -2142,6 +2151,9 @@ enum nl80211_mntr_flags {
*
* @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
*
+ * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors
+ * to synchronize to for 11s default synchronization method (see 11C.12.2.2)
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
@@ -2166,6 +2178,7 @@ enum nl80211_meshconf_params {
NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
NL80211_MESHCONF_FORWARDING,
NL80211_MESHCONF_RSSI_THRESHOLD,
+ NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -2205,6 +2218,11 @@ enum nl80211_meshconf_params {
* complete (unsecured) mesh peering without the need of a userspace daemon.
*
* @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a
+ * vendor specific synchronization method or disable it to use the default
+ * neighbor offset synchronization
+ *
* @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
*/
enum nl80211_mesh_setup_params {
@@ -2214,6 +2232,7 @@ enum nl80211_mesh_setup_params {
NL80211_MESH_SETUP_IE,
NL80211_MESH_SETUP_USERSPACE_AUTH,
NL80211_MESH_SETUP_USERSPACE_AMPE,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
/* keep last */
__NL80211_MESH_SETUP_ATTR_AFTER_LAST,
@@ -2223,7 +2242,7 @@ enum nl80211_mesh_setup_params {
/**
* enum nl80211_txq_attr - TX queue parameter attributes
* @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
- * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
+ * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*)
* @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
* disabled
* @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
@@ -2236,7 +2255,7 @@ enum nl80211_mesh_setup_params {
*/
enum nl80211_txq_attr {
__NL80211_TXQ_ATTR_INVALID,
- NL80211_TXQ_ATTR_QUEUE,
+ NL80211_TXQ_ATTR_AC,
NL80211_TXQ_ATTR_TXOP,
NL80211_TXQ_ATTR_CWMIN,
NL80211_TXQ_ATTR_CWMAX,
@@ -2247,13 +2266,21 @@ enum nl80211_txq_attr {
NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
};
-enum nl80211_txq_q {
- NL80211_TXQ_Q_VO,
- NL80211_TXQ_Q_VI,
- NL80211_TXQ_Q_BE,
- NL80211_TXQ_Q_BK
+enum nl80211_ac {
+ NL80211_AC_VO,
+ NL80211_AC_VI,
+ NL80211_AC_BE,
+ NL80211_AC_BK,
+ NL80211_NUM_ACS
};
+/* backward compat */
+#define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC
+#define NL80211_TXQ_Q_VO NL80211_AC_VO
+#define NL80211_TXQ_Q_VI NL80211_AC_VI
+#define NL80211_TXQ_Q_BE NL80211_AC_BE
+#define NL80211_TXQ_Q_BK NL80211_AC_BK
+
enum nl80211_channel_type {
NL80211_CHAN_NO_HT,
NL80211_CHAN_HT20,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 83d800c31e3..a587867375b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -521,6 +521,7 @@ struct station_parameters {
* @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled
* @STATION_INFO_STA_FLAGS: @sta_flags filled
* @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled
+ * @STATION_INFO_T_OFFSET: @t_offset filled
*/
enum station_info_flags {
STATION_INFO_INACTIVE_TIME = 1<<0,
@@ -542,7 +543,8 @@ enum station_info_flags {
STATION_INFO_CONNECTED_TIME = 1<<16,
STATION_INFO_ASSOC_REQ_IES = 1<<17,
STATION_INFO_STA_FLAGS = 1<<18,
- STATION_INFO_BEACON_LOSS_COUNT = 1<<19
+ STATION_INFO_BEACON_LOSS_COUNT = 1<<19,
+ STATION_INFO_T_OFFSET = 1<<20,
};
/**
@@ -643,6 +645,7 @@ struct sta_bss_parameters {
* @assoc_req_ies_len: Length of assoc_req_ies buffer in octets.
* @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.
*/
struct station_info {
u32 filled;
@@ -671,6 +674,7 @@ struct station_info {
size_t assoc_req_ies_len;
u32 beacon_loss_count;
+ s64 t_offset;
/*
* Note: Add a new enum station_info_flags value for each new field and
@@ -798,6 +802,8 @@ struct mesh_config {
/* ttl used in path selection information elements */
u8 element_ttl;
bool auto_open_plinks;
+ /* neighbor offset synchronization */
+ u32 dot11MeshNbrOffsetMaxNeighbor;
/* HWMP parameters */
u8 dot11MeshHWMPmaxPREQretries;
u32 path_refresh_time;
@@ -821,6 +827,7 @@ struct mesh_config {
* struct mesh_setup - 802.11s mesh setup configuration
* @mesh_id: the mesh ID
* @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes
+ * @sync_method: which synchronization method to use
* @path_sel_proto: which path selection protocol to use
* @path_metric: which metric to use
* @ie: vendor information elements (optional)
@@ -834,8 +841,9 @@ struct mesh_config {
struct mesh_setup {
const u8 *mesh_id;
u8 mesh_id_len;
- u8 path_sel_proto;
- u8 path_metric;
+ u8 sync_method;
+ u8 path_sel_proto;
+ u8 path_metric;
const u8 *ie;
u8 ie_len;
bool is_authenticated;
@@ -845,7 +853,7 @@ struct mesh_setup {
/**
* struct ieee80211_txq_params - TX queue parameters
- * @queue: TX queue identifier (NL80211_TXQ_Q_*)
+ * @ac: AC identifier
* @txop: Maximum burst time in units of 32 usecs, 0 meaning disabled
* @cwmin: Minimum contention window [a value of the form 2^n-1 in the range
* 1..32767]
@@ -854,7 +862,7 @@ struct mesh_setup {
* @aifs: Arbitration interframe space [0..255]
*/
struct ieee80211_txq_params {
- enum nl80211_txq_q queue;
+ enum nl80211_ac ac;
u16 txop;
u16 cwmin;
u16 cwmax;
@@ -1336,6 +1344,9 @@ struct cfg80211_gtk_rekey_data {
* be %NULL or contain the enabled Wake-on-Wireless triggers that are
* configured for the device.
* @resume: wiphy device needs to be resumed
+ * @set_wakeup: Called when WoWLAN is enabled/disabled, use this callback
+ * to call device_set_wakeup_enable() to enable/disable wakeup from
+ * the device.
*
* @add_virtual_intf: create a new virtual interface with the given name,
* must set the struct wireless_dev's iftype. Beware: You must create
@@ -1507,6 +1518,7 @@ struct cfg80211_gtk_rekey_data {
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
int (*resume)(struct wiphy *wiphy);
+ void (*set_wakeup)(struct wiphy *wiphy, bool enabled);
struct net_device * (*add_virtual_intf)(struct wiphy *wiphy,
char *name,
@@ -3343,6 +3355,17 @@ int cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
enum nl80211_channel_type channel_type);
/*
+ * cfg80211_ch_switch_notify - update wdev channel and notify userspace
+ * @dev: the device which switched channels
+ * @freq: new channel frequency (in MHz)
+ * @type: channel type
+ *
+ * Acquires wdev_lock, so must only be called from sleepable driver context!
+ */
+void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
+ enum nl80211_channel_type type);
+
+/*
* cfg80211_calculate_bitrate - calculate actual bitrate (in 100Kbps units)
* @rate: given rate_info to calculate bitrate from
*
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 9210bdc7bd8..32cd5171fa2 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -95,9 +95,11 @@ struct device;
* @IEEE80211_MAX_QUEUES: Maximum number of regular device queues.
*/
enum ieee80211_max_queues {
- IEEE80211_MAX_QUEUES = 4,
+ IEEE80211_MAX_QUEUES = 16,
};
+#define IEEE80211_INVAL_HW_QUEUE 0xff
+
/**
* enum ieee80211_ac_numbers - AC numbers as used in mac80211
* @IEEE80211_AC_VO: voice
@@ -244,7 +246,7 @@ enum ieee80211_rssi_event {
* @channel_type: Channel type for this BSS -- the hardware might be
* configured for HT40+ while this BSS only uses no-HT, for
* example.
- * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info).
+ * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation.
* This field is only valid when the channel type is one of the HT types.
* @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
* implies disabled
@@ -522,7 +524,7 @@ struct ieee80211_tx_rate {
*
* @flags: transmit info flags, defined above
* @band: the band to transmit on (use for checking for races)
- * @antenna_sel_tx: antenna to use, 0 for automatic diversity
+ * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC
* @ack_frame_id: internal frame ID for TX status, used internally
* @control: union for control data
* @status: union for status data
@@ -538,7 +540,7 @@ struct ieee80211_tx_info {
u32 flags;
u8 band;
- u8 antenna_sel_tx;
+ u8 hw_queue;
u16 ack_frame_id;
@@ -564,7 +566,8 @@ struct ieee80211_tx_info {
u8 ampdu_ack_len;
int ack_signal;
u8 ampdu_len;
- /* 15 bytes free */
+ u8 antenna;
+ /* 14 bytes free */
} status;
struct {
struct ieee80211_tx_rate driver_rates[
@@ -888,6 +891,8 @@ enum ieee80211_vif_flags {
* these need to be set (or cleared) when the interface is added
* or, if supported by the driver, the interface type is changed
* at runtime, mac80211 will never touch this field
+ * @hw_queue: hardware queue for each AC
+ * @cab_queue: content-after-beacon (DTIM beacon really) queue, AP mode only
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *).
*/
@@ -896,7 +901,12 @@ struct ieee80211_vif {
struct ieee80211_bss_conf bss_conf;
u8 addr[ETH_ALEN];
bool p2p;
+
+ u8 cab_queue;
+ u8 hw_queue[IEEE80211_NUM_ACS];
+
u32 driver_flags;
+
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};
@@ -1174,6 +1184,15 @@ enum sta_notify_cmd {
* @IEEE80211_HW_SCAN_WHILE_IDLE: The device can do hw scan while
* being idle (i.e. mac80211 doesn't have to go idle-off during the
* the scan).
+ *
+ * @IEEE80211_HW_WANT_MONITOR_VIF: The driver would like to be informed of
+ * a virtual monitor interface when monitor interfaces are the only
+ * active interfaces.
+ *
+ * @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
+ * queue mapping in order to use different queues (not just one per AC)
+ * for different virtual interfaces. See the doc section on HW queue
+ * control for more details.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -1190,13 +1209,13 @@ enum ieee80211_hw_flags {
IEEE80211_HW_PS_NULLFUNC_STACK = 1<<11,
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
IEEE80211_HW_MFP_CAPABLE = 1<<13,
- /* reuse bit 14 */
+ IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
- /* reuse bit 20 */
+ IEEE80211_HW_QUEUE_CONTROL = 1<<20,
IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21,
IEEE80211_HW_AP_LINK_PS = 1<<22,
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23,
@@ -1266,6 +1285,9 @@ enum ieee80211_hw_flags {
* @max_tx_aggregation_subframes: maximum number of subframes in an
* aggregate an HT driver will transmit, used by the peer as a
* hint to size its reorder buffer.
+ *
+ * @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
+ * (if %IEEE80211_HW_QUEUE_CONTROL is set)
*/
struct ieee80211_hw {
struct ieee80211_conf conf;
@@ -1286,6 +1308,7 @@ struct ieee80211_hw {
u8 max_rate_tries;
u8 max_rx_aggregation_subframes;
u8 max_tx_aggregation_subframes;
+ u8 offchannel_tx_hw_queue;
};
/**
@@ -1694,6 +1717,61 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
*/
/**
+ * DOC: HW queue control
+ *
+ * Before HW queue control was introduced, mac80211 only had a single static
+ * assignment of per-interface AC software queues to hardware queues. This
+ * was problematic for a few reasons:
+ * 1) off-channel transmissions might get stuck behind other frames
+ * 2) multiple virtual interfaces couldn't be handled correctly
+ * 3) after-DTIM frames could get stuck behind other frames
+ *
+ * To solve this, hardware typically uses multiple different queues for all
+ * the different usages, and this needs to be propagated into mac80211 so it
+ * won't have the same problem with the software queues.
+ *
+ * Therefore, mac80211 now offers the %IEEE80211_HW_QUEUE_CONTROL capability
+ * flag that tells it that the driver implements its own queue control. To do
+ * so, the driver will set up the various queues in each &struct ieee80211_vif
+ * and the offchannel queue in &struct ieee80211_hw. In response, mac80211 will
+ * use those queue IDs in the hw_queue field of &struct ieee80211_tx_info and
+ * if necessary will queue the frame on the right software queue that mirrors
+ * the hardware queue.
+ * Additionally, the driver has to then use these HW queue IDs for the queue
+ * management functions (ieee80211_stop_queue() et al.)
+ *
+ * The driver is free to set up the queue mappings as needed, multiple virtual
+ * interfaces may map to the same hardware queues if needed. The setup has to
+ * happen during add_interface or change_interface callbacks. For example, a
+ * driver supporting station+station and station+AP modes might decide to have
+ * 10 hardware queues to handle different scenarios:
+ *
+ * 4 AC HW queues for 1st vif: 0, 1, 2, 3
+ * 4 AC HW queues for 2nd vif: 4, 5, 6, 7
+ * after-DTIM queue for AP: 8
+ * off-channel queue: 9
+ *
+ * It would then set up the hardware like this:
+ * hw.offchannel_tx_hw_queue = 9
+ *
+ * and the first virtual interface that is added as follows:
+ * vif.hw_queue[IEEE80211_AC_VO] = 0
+ * vif.hw_queue[IEEE80211_AC_VI] = 1
+ * vif.hw_queue[IEEE80211_AC_BE] = 2
+ * vif.hw_queue[IEEE80211_AC_BK] = 3
+ * vif.cab_queue = 8 // if AP mode, otherwise %IEEE80211_INVAL_HW_QUEUE
+ * and the second virtual interface with 4-7.
+ *
+ * If queue 6 gets full, for example, mac80211 would only stop the second
+ * virtual interface's BE queue since virtual interface queues are per AC.
+ *
+ * Note that the vif.cab_queue value should be set to %IEEE80211_INVAL_HW_QUEUE
+ * whenever the queue is not used (i.e. the interface is not in AP mode) if the
+ * queue could potentially be shared since mac80211 will look at cab_queue when
+ * a queue is stopped/woken even if the interface is not in AP mode.
+ */
+
+/**
* enum ieee80211_filter_flags - hardware filter flags
*
* These flags determine what the filter in hardware should be
@@ -1780,6 +1858,18 @@ enum ieee80211_frame_release_type {
};
/**
+ * enum ieee80211_rate_control_changed - flags to indicate what changed
+ *
+ * @IEEE80211_RC_BW_CHANGED: The bandwidth that can be used to transmit
+ * to this station changed.
+ * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed.
+ */
+enum ieee80211_rate_control_changed {
+ IEEE80211_RC_BW_CHANGED = BIT(0),
+ IEEE80211_RC_SMPS_CHANGED = BIT(1),
+};
+
+/**
* struct ieee80211_ops - callbacks from mac80211 to the driver
*
* This structure contains various callbacks that the driver may
@@ -1980,6 +2070,14 @@ enum ieee80211_frame_release_type {
* up the list of states.
* The callback can sleep.
*
+ * @sta_rc_update: Notifies the driver of changes to the bitrates that can be
+ * used to transmit to the station. The changes are advertised with bits
+ * from &enum ieee80211_rate_control_changed and the values are reflected
+ * in the station data. This callback should only be used when the driver
+ * uses hardware rate control (%IEEE80211_HW_HAS_RATE_CONTROL) since
+ * otherwise the rate control algorithm is notified directly.
+ * Must be atomic.
+ *
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue.
* Returns a negative error code on failure.
@@ -2135,6 +2233,7 @@ struct ieee80211_ops {
#ifdef CONFIG_PM
int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
int (*resume)(struct ieee80211_hw *hw);
+ void (*set_wakeup)(struct ieee80211_hw *hw, bool enabled);
#endif
int (*add_interface)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
@@ -2196,8 +2295,12 @@ struct ieee80211_ops {
struct ieee80211_sta *sta,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state);
+ void (*sta_rc_update)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 changed);
int (*conf_tx)(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, u16 queue,
+ struct ieee80211_vif *vif, u16 ac,
const struct ieee80211_tx_queue_params *params);
u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
@@ -3512,19 +3615,6 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
/* Rate control API */
/**
- * enum rate_control_changed - flags to indicate which parameter changed
- *
- * @IEEE80211_RC_HT_CHANGED: The HT parameters of the operating channel have
- * changed, rate control algorithm can update its internal state if needed.
- * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed, the rate
- * control algorithm needs to adjust accordingly.
- */
-enum rate_control_changed {
- IEEE80211_RC_HT_CHANGED = BIT(0),
- IEEE80211_RC_SMPS_CHANGED = BIT(1),
-};
-
-/**
* struct ieee80211_tx_rate_control - rate control information for/from RC algo
*
* @hw: The hardware the algorithm is invoked for.
@@ -3569,9 +3659,8 @@ struct rate_control_ops {
void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta);
void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta,
- void *priv_sta, u32 changed,
- enum nl80211_channel_type oper_chan_type);
+ struct ieee80211_sta *sta, void *priv_sta,
+ u32 changed);
void (*free_sta)(void *priv, struct ieee80211_sta *sta,
void *priv_sta);
@@ -3706,8 +3795,9 @@ void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif,
void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif);
-int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb);
+int ieee80211_add_srates_ie(struct ieee80211_vif *vif,
+ struct sk_buff *skb, bool need_basic);
int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif,
- struct sk_buff *skb);
+ struct sk_buff *skb, bool need_basic);
#endif /* MAC80211_H */
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 96ddb72760b..8d249d70598 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -225,6 +225,17 @@ config MAC80211_VERBOSE_MHWMP_DEBUG
Do not select this option.
+config MAC80211_VERBOSE_MESH_SYNC_DEBUG
+ bool "Verbose mesh mesh synchronization debugging"
+ depends on MAC80211_DEBUG_MENU
+ depends on MAC80211_MESH
+ ---help---
+ Selecting this option causes mac80211 to print out very verbose mesh
+ synchronization debugging messages (when mac80211 is taking part in a
+ mesh network).
+
+ Do not select this option.
+
config MAC80211_VERBOSE_TDLS_DEBUG
bool "Verbose TDLS debugging"
depends on MAC80211_DEBUG_MENU
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 1be7a454aa7..3e9d931bba3 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -38,7 +38,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
mesh.o \
mesh_pathtbl.o \
mesh_plink.o \
- mesh_hwmp.o
+ mesh_hwmp.o \
+ mesh_sync.o
mac80211-$(CONFIG_PM) += pm.o
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 64d3ce5ea1a..a070d4f460e 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -142,6 +142,18 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
u8 *timer_to_id = ptid - *ptid;
struct sta_info *sta = container_of(timer_to_id, struct sta_info,
timer_to_tid[0]);
+ struct tid_ampdu_rx *tid_rx;
+ unsigned long timeout;
+
+ tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[*ptid]);
+ if (!tid_rx)
+ return;
+
+ timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout);
+ if (time_is_after_jiffies(timeout)) {
+ mod_timer(&tid_rx->session_timer, timeout);
+ return;
+ }
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
@@ -291,7 +303,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
/* rx timer */
tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired;
tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid];
- init_timer(&tid_agg_rx->session_timer);
+ init_timer_deferrable(&tid_agg_rx->session_timer);
/* rx reorder timer */
tid_agg_rx->reorder_timer.function = sta_rx_agg_reorder_timer_expired;
@@ -335,8 +347,10 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
/* activate it for RX */
rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
- if (timeout)
+ if (timeout) {
mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout));
+ tid_agg_rx->last_rx = jiffies;
+ }
end:
mutex_unlock(&sta->ampdu_mlme.mtx);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 76be6174419..5b7053c5873 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -286,25 +286,25 @@ static inline int ieee80211_ac_from_tid(int tid)
* a global "agg_queue_stop" refcount.
*/
static void __acquires(agg_queue)
-ieee80211_stop_queue_agg(struct ieee80211_local *local, int tid)
+ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
{
- int queue = ieee80211_ac_from_tid(tid);
+ int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
- if (atomic_inc_return(&local->agg_queue_stop[queue]) == 1)
+ if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1)
ieee80211_stop_queue_by_reason(
- &local->hw, queue,
+ &sdata->local->hw, queue,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
__acquire(agg_queue);
}
static void __releases(agg_queue)
-ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid)
+ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
{
- int queue = ieee80211_ac_from_tid(tid);
+ int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
- if (atomic_dec_return(&local->agg_queue_stop[queue]) == 0)
+ if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0)
ieee80211_wake_queue_by_reason(
- &local->hw, queue,
+ &sdata->local->hw, queue,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
__release(agg_queue);
}
@@ -314,13 +314,14 @@ ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid)
* requires a call to ieee80211_agg_splice_finish later
*/
static void __acquires(agg_queue)
-ieee80211_agg_splice_packets(struct ieee80211_local *local,
+ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_tx *tid_tx, u16 tid)
{
- int queue = ieee80211_ac_from_tid(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(local, tid);
+ ieee80211_stop_queue_agg(sdata, tid);
if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates"
" from the pending queue\n", tid))
@@ -336,9 +337,9 @@ ieee80211_agg_splice_packets(struct ieee80211_local *local,
}
static void __releases(agg_queue)
-ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid)
+ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid)
{
- ieee80211_wake_queue_agg(local, tid);
+ ieee80211_wake_queue_agg(sdata, tid);
}
void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
@@ -376,9 +377,9 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
" tid %d\n", tid);
#endif
spin_lock_bh(&sta->lock);
- ieee80211_agg_splice_packets(local, tid_tx, tid);
+ ieee80211_agg_splice_packets(sdata, tid_tx, tid);
ieee80211_assign_tid_tx(sta, tid, NULL);
- ieee80211_agg_splice_finish(local, tid);
+ ieee80211_agg_splice_finish(sdata, tid);
spin_unlock_bh(&sta->lock);
kfree_rcu(tid_tx, rcu_head);
@@ -417,6 +418,18 @@ static void sta_tx_agg_session_timer_expired(unsigned long data)
u8 *timer_to_id = ptid - *ptid;
struct sta_info *sta = container_of(timer_to_id, struct sta_info,
timer_to_tid[0]);
+ struct tid_ampdu_tx *tid_tx;
+ unsigned long timeout;
+
+ tid_tx = rcu_dereference_protected_tid_tx(sta, *ptid);
+ if (!tid_tx)
+ return;
+
+ timeout = tid_tx->last_tx + TU_TO_JIFFIES(tid_tx->timeout);
+ if (time_is_after_jiffies(timeout)) {
+ mod_timer(&tid_tx->session_timer, timeout);
+ return;
+ }
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "tx session timer expired on tid %d\n", (u16)*ptid);
@@ -542,7 +555,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
/* tx timer */
tid_tx->session_timer.function = sta_tx_agg_session_timer_expired;
tid_tx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid];
- init_timer(&tid_tx->session_timer);
+ init_timer_deferrable(&tid_tx->session_timer);
/* assign a dialog token */
sta->ampdu_mlme.dialog_token_allocator++;
@@ -586,14 +599,14 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
*/
spin_lock_bh(&sta->lock);
- ieee80211_agg_splice_packets(local, tid_tx, tid);
+ ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid);
/*
* Now mark as operational. This will be visible
* in the TX path, and lets it go lock-free in
* the common case.
*/
set_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state);
- ieee80211_agg_splice_finish(local, tid);
+ ieee80211_agg_splice_finish(sta->sdata, tid);
spin_unlock_bh(&sta->lock);
}
@@ -778,12 +791,12 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
* more.
*/
- ieee80211_agg_splice_packets(local, tid_tx, tid);
+ 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(local, tid);
+ ieee80211_agg_splice_finish(sta->sdata, tid);
kfree_rcu(tid_tx, rcu_head);
@@ -884,9 +897,11 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
sta->ampdu_mlme.addba_req_num[tid] = 0;
- if (tid_tx->timeout)
+ if (tid_tx->timeout) {
mod_timer(&tid_tx->session_timer,
TU_TO_EXP_TIME(tid_tx->timeout));
+ tid_tx->last_tx = jiffies;
+ }
} else {
___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 677d6592978..35573549125 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -412,6 +412,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->llid = le16_to_cpu(sta->llid);
sinfo->plid = le16_to_cpu(sta->plid);
sinfo->plink_state = sta->plink_state;
+ if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
+ sinfo->filled |= STATION_INFO_T_OFFSET;
+ sinfo->t_offset = sta->t_offset;
+ }
#endif
}
@@ -640,6 +644,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
ieee80211_bss_info_change_notify(sdata, changed);
+ netif_carrier_on(dev);
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ netif_carrier_on(vlan->dev);
+
return 0;
}
@@ -665,7 +673,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata, *vlan;
struct beacon_data *old;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -674,6 +682,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
if (!old)
return -ENOENT;
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ netif_carrier_off(vlan->dev);
+ netif_carrier_off(dev);
+
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
kfree_rcu(old, rcu_head);
@@ -1235,6 +1247,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
/* now copy the rest of the setup parameters */
ifmsh->mesh_id_len = setup->mesh_id_len;
memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
+ ifmsh->mesh_sp_id = setup->sync_method;
ifmsh->mesh_pp_id = setup->path_sel_proto;
ifmsh->mesh_pm_id = setup->path_metric;
ifmsh->security = IEEE80211_MESH_SEC_NONE;
@@ -1279,6 +1292,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
conf->dot11MeshTTL = nconf->element_ttl;
if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask))
conf->auto_open_plinks = nconf->auto_open_plinks;
+ if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask))
+ conf->dot11MeshNbrOffsetMaxNeighbor =
+ nconf->dot11MeshNbrOffsetMaxNeighbor;
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
conf->dot11MeshHWMPmaxPREQretries =
nconf->dot11MeshHWMPmaxPREQretries;
@@ -1437,6 +1453,9 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
if (!local->ops->conf_tx)
return -EOPNOTSUPP;
+ if (local->hw.queues < IEEE80211_NUM_ACS)
+ return -EOPNOTSUPP;
+
memset(&p, 0, sizeof(p));
p.aifs = params->aifs;
p.cw_max = params->cwmax;
@@ -1449,14 +1468,11 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
*/
p.uapsd = false;
- if (params->queue >= local->hw.queues)
- return -EINVAL;
-
- sdata->tx_conf[params->queue] = p;
- if (drv_conf_tx(local, sdata, params->queue, &p)) {
+ sdata->tx_conf[params->ac] = p;
+ if (drv_conf_tx(local, sdata, params->ac, &p)) {
wiphy_debug(local->hw.wiphy,
- "failed to set TX queue parameters for queue %d\n",
- params->queue);
+ "failed to set TX queue parameters for AC %d\n",
+ params->ac);
return -EINVAL;
}
@@ -2090,6 +2106,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
IEEE80211_SKB_CB(skb)->flags = flags;
+ if (flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+ IEEE80211_SKB_CB(skb)->hw_queue =
+ local->hw.offchannel_tx_hw_queue;
+
skb->dev = sdata->dev;
*cookie = (unsigned long) skb;
@@ -2131,6 +2151,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
/* modify cookie to prevent API mismatches */
*cookie ^= 2;
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+ IEEE80211_SKB_CB(skb)->hw_queue =
+ local->hw.offchannel_tx_hw_queue;
local->hw_roc_skb = skb;
local->hw_roc_skb_for_status = skb;
mutex_unlock(&local->mtx);
@@ -2350,8 +2372,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_req.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(&sdata->vif, skb);
- ieee80211_add_ext_srates_ie(&sdata->vif, skb);
+ ieee80211_add_srates_ie(&sdata->vif, skb, false);
+ ieee80211_add_ext_srates_ie(&sdata->vif, skb, false);
ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_RESPONSE:
@@ -2364,8 +2386,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(&sdata->vif, skb);
- ieee80211_add_ext_srates_ie(&sdata->vif, skb);
+ ieee80211_add_srates_ie(&sdata->vif, skb, false);
+ ieee80211_add_ext_srates_ie(&sdata->vif, skb, false);
ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_CONFIRM:
@@ -2425,8 +2447,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
mgmt->u.action.u.tdls_discover_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(&sdata->vif, skb);
- ieee80211_add_ext_srates_ie(&sdata->vif, skb);
+ ieee80211_add_srates_ie(&sdata->vif, skb, false);
+ ieee80211_add_ext_srates_ie(&sdata->vif, skb, false);
ieee80211_tdls_add_ext_capab(skb);
break;
default:
@@ -2673,6 +2695,13 @@ ieee80211_wiphy_get_channel(struct wiphy *wiphy)
return local->oper_channel;
}
+#ifdef CONFIG_PM
+static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled)
+{
+ drv_set_wakeup(wiphy_priv(wiphy), enabled);
+}
+#endif
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -2741,4 +2770,7 @@ struct cfg80211_ops mac80211_config_ops = {
.probe_client = ieee80211_probe_client,
.get_channel = ieee80211_wiphy_get_channel,
.set_noack_map = ieee80211_set_noack_map,
+#ifdef CONFIG_PM
+ .set_wakeup = ieee80211_set_wakeup,
+#endif
};
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index e00ce8c3e28..c76cf7230c7 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -135,29 +135,3 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
return result;
}
-
-/*
- * ieee80211_get_tx_channel_type returns the channel type we should
- * use for packet transmission, given the channel capability and
- * whatever regulatory flags we have been given.
- */
-enum nl80211_channel_type ieee80211_get_tx_channel_type(
- struct ieee80211_local *local,
- enum nl80211_channel_type channel_type)
-{
- switch (channel_type) {
- case NL80211_CHAN_HT40PLUS:
- if (local->hw.conf.channel->flags &
- IEEE80211_CHAN_NO_HT40PLUS)
- return NL80211_CHAN_HT20;
- break;
- case NL80211_CHAN_HT40MINUS:
- if (local->hw.conf.channel->flags &
- IEEE80211_CHAN_NO_HT40MINUS)
- return NL80211_CHAN_HT20;
- break;
- default:
- break;
- }
- return channel_type;
-}
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 30f99c34484..e7af5227e32 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -424,6 +424,7 @@ static ssize_t ieee80211_if_parse_tsf(
struct ieee80211_local *local = sdata->local;
unsigned long long tsf;
int ret;
+ int tsf_is_delta = 0;
if (strncmp(buf, "reset", 5) == 0) {
if (local->ops->reset_tsf) {
@@ -431,9 +432,20 @@ static ssize_t ieee80211_if_parse_tsf(
wiphy_info(local->hw.wiphy, "debugfs reset TSF\n");
}
} else {
+ if (buflen > 10 && buf[1] == '=') {
+ if (buf[0] == '+')
+ tsf_is_delta = 1;
+ else if (buf[0] == '-')
+ tsf_is_delta = -1;
+ else
+ return -EINVAL;
+ buf += 2;
+ }
ret = kstrtoull(buf, 10, &tsf);
if (ret < 0)
return -EINVAL;
+ if (tsf_is_delta)
+ tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf;
if (local->ops->set_tsf) {
drv_set_tsf(local, sdata, tsf);
wiphy_info(local->hw.wiphy,
@@ -499,26 +511,23 @@ IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC);
IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC);
#endif
-
-#define DEBUGFS_ADD(name) \
- debugfs_create_file(#name, 0400, sdata->debugfs.dir, \
- sdata, &name##_ops);
-
#define DEBUGFS_ADD_MODE(name, mode) \
debugfs_create_file(#name, mode, sdata->debugfs.dir, \
sdata, &name##_ops);
-static void add_sta_files(struct ieee80211_sub_if_data *sdata)
+#define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400)
+
+static void add_common_files(struct ieee80211_sub_if_data *sdata)
{
DEBUGFS_ADD(drop_unencrypted);
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
DEBUGFS_ADD(rc_rateidx_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
+}
+static void add_sta_files(struct ieee80211_sub_if_data *sdata)
+{
DEBUGFS_ADD(bssid);
DEBUGFS_ADD(aid);
DEBUGFS_ADD(last_beacon);
@@ -531,15 +540,6 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(drop_unencrypted);
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
- DEBUGFS_ADD(rc_rateidx_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mask_5ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
-
DEBUGFS_ADD(num_sta_authorized);
DEBUGFS_ADD(num_sta_ps);
DEBUGFS_ADD(dtim_count);
@@ -549,48 +549,14 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata)
static void add_ibss_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(channel_type);
- DEBUGFS_ADD(rc_rateidx_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mask_5ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
-
DEBUGFS_ADD_MODE(tsf, 0600);
}
static void add_wds_files(struct ieee80211_sub_if_data *sdata)
{
- DEBUGFS_ADD(drop_unencrypted);
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
- DEBUGFS_ADD(rc_rateidx_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mask_5ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
-
DEBUGFS_ADD(peer);
}
-static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
-{
- DEBUGFS_ADD(drop_unencrypted);
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
- DEBUGFS_ADD(rc_rateidx_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mask_5ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
- DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
-}
-
-static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
-{
- DEBUGFS_ADD(flags);
- DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
-}
-
#ifdef CONFIG_MAC80211_MESH
static void add_mesh_files(struct ieee80211_sub_if_data *sdata)
@@ -651,6 +617,13 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
if (!sdata->debugfs.dir)
return;
+ DEBUGFS_ADD(flags);
+ DEBUGFS_ADD(state);
+ DEBUGFS_ADD(channel_type);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
+ add_common_files(sdata);
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_MESH_POINT:
#ifdef CONFIG_MAC80211_MESH
@@ -671,12 +644,6 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
case NL80211_IFTYPE_WDS:
add_wds_files(sdata);
break;
- case NL80211_IFTYPE_MONITOR:
- add_monitor_files(sdata);
- break;
- case NL80211_IFTYPE_AP_VLAN:
- add_vlan_files(sdata);
- break;
default:
break;
}
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 832b2da5e4c..5ccec2c1e9f 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -63,7 +63,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
int res = scnprintf(buf, sizeof(buf),
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
TEST(SHORT_PREAMBLE),
@@ -71,7 +71,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
- TEST(INSERTED), TEST(RATE_CONTROL));
+ TEST(INSERTED), TEST(RATE_CONTROL),
+ TEST(TOFFSET_KNOWN));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index af4691fed64..4a0e559cb26 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -7,7 +7,9 @@
static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)
{
- WARN_ON(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER));
+ WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER),
+ "%s: Failed check-sdata-in-driver check, flags: 0x%x\n",
+ sdata->dev->name, sdata->flags);
}
static inline struct ieee80211_sub_if_data *
@@ -89,6 +91,19 @@ static inline int drv_resume(struct ieee80211_local *local)
trace_drv_return_int(local, ret);
return ret;
}
+
+static inline void drv_set_wakeup(struct ieee80211_local *local,
+ bool enabled)
+{
+ might_sleep();
+
+ if (!local->ops->set_wakeup)
+ return;
+
+ trace_drv_set_wakeup(local, enabled);
+ local->ops->set_wakeup(&local->hw, enabled);
+ trace_drv_return_void(local);
+}
#endif
static inline int drv_add_interface(struct ieee80211_local *local,
@@ -99,7 +114,8 @@ static inline int drv_add_interface(struct ieee80211_local *local,
might_sleep();
if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_MONITOR))
+ (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
+ !(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))))
return -EINVAL;
trace_drv_add_interface(local, sdata);
@@ -474,8 +490,23 @@ int drv_sta_state(struct ieee80211_local *local,
return ret;
}
+static inline void drv_sta_rc_update(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta, u32 changed)
+{
+ sdata = get_bss_sdata(sdata);
+ check_sdata_in_driver(sdata);
+
+ trace_drv_sta_rc_update(local, sdata, sta, changed);
+ if (local->ops->sta_rc_update)
+ local->ops->sta_rc_update(&local->hw, &sdata->vif,
+ sta, changed);
+
+ trace_drv_return_void(local);
+}
+
static inline int drv_conf_tx(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata, u16 queue,
+ struct ieee80211_sub_if_data *sdata, u16 ac,
const struct ieee80211_tx_queue_params *params)
{
int ret = -EOPNOTSUPP;
@@ -484,10 +515,10 @@ static inline int drv_conf_tx(struct ieee80211_local *local,
check_sdata_in_driver(sdata);
- trace_drv_conf_tx(local, sdata, queue, params);
+ trace_drv_conf_tx(local, sdata, ac, params);
if (local->ops->conf_tx)
ret = local->ops->conf_tx(&local->hw, &sdata->vif,
- queue, params);
+ ac, params);
trace_drv_return_int(local, ret);
return ret;
}
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 21d6f5290a1..7c0754bed61 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -171,6 +171,20 @@ DEFINE_EVENT(local_only_evt, drv_resume,
TP_ARGS(local)
);
+TRACE_EVENT(drv_set_wakeup,
+ TP_PROTO(struct ieee80211_local *local, bool enabled),
+ TP_ARGS(local, enabled),
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(bool, enabled)
+ ),
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->enabled = enabled;
+ ),
+ TP_printk(LOCAL_PR_FMT " enabled:%d", LOCAL_PR_ARG, __entry->enabled)
+);
+
DEFINE_EVENT(local_only_evt, drv_stop,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
@@ -624,6 +638,34 @@ TRACE_EVENT(drv_sta_state,
)
);
+TRACE_EVENT(drv_sta_rc_update,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ u32 changed),
+
+ TP_ARGS(local, sdata, sta, changed),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u32, changed)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->changed = changed;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " changed: 0x%x",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->changed
+ )
+);
+
TRACE_EVENT(drv_sta_add,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -677,15 +719,14 @@ TRACE_EVENT(drv_sta_remove,
TRACE_EVENT(drv_conf_tx,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
- u16 queue,
- const struct ieee80211_tx_queue_params *params),
+ u16 ac, const struct ieee80211_tx_queue_params *params),
- TP_ARGS(local, sdata, queue, params),
+ TP_ARGS(local, sdata, ac, params),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
- __field(u16, queue)
+ __field(u16, ac)
__field(u16, txop)
__field(u16, cw_min)
__field(u16, cw_max)
@@ -696,7 +737,7 @@ TRACE_EVENT(drv_conf_tx,
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
- __entry->queue = queue;
+ __entry->ac = ac;
__entry->txop = params->txop;
__entry->cw_max = params->cw_max;
__entry->cw_min = params->cw_min;
@@ -705,8 +746,8 @@ TRACE_EVENT(drv_conf_tx,
),
TP_printk(
- LOCAL_PR_FMT VIF_PR_FMT " queue:%d",
- LOCAL_PR_ARG, VIF_PR_ARG, __entry->queue
+ LOCAL_PR_FMT VIF_PR_FMT " AC:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->ac
)
);
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index f25fff7607d..9b603366943 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -19,15 +19,6 @@
#include "ieee80211_i.h"
#include "rate.h"
-bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata)
-{
- const __le16 flg = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40);
- if ((sdata->u.mgd.ht_capa_mask.cap_info & flg) &&
- !(sdata->u.mgd.ht_capa.cap_info & flg))
- return true;
- return false;
-}
-
static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_ht_cap *ht_cap,
u16 flag)
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 33fd8d9f714..49a20798033 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -160,16 +160,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
if (channel_type && sband->ht_cap.ht_supported) {
pos = skb_put(skb, 4 +
sizeof(struct ieee80211_ht_cap) +
- sizeof(struct ieee80211_ht_info));
+ sizeof(struct ieee80211_ht_operation));
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
sband->ht_cap.cap);
- pos = ieee80211_ie_build_ht_info(pos,
- &sband->ht_cap,
- chan,
- channel_type);
+ pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
+ chan, channel_type);
}
- if (local->hw.queues >= 4) {
+ if (local->hw.queues >= IEEE80211_NUM_ACS) {
pos = skb_put(skb, 9);
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 7; /* len */
@@ -410,7 +408,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (elems->supp_rates) {
supp_rates = ieee80211_sta_get_rates(local, elems,
- band);
+ band, NULL);
if (sta) {
u32 prev_rates;
@@ -441,13 +439,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (sta && elems->wmm_info)
set_sta_flag(sta, WLAN_STA_WME);
- if (sta && elems->ht_info_elem && elems->ht_cap_elem &&
+ if (sta && elems->ht_operation && elems->ht_cap_elem &&
sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
/* we both use HT */
struct ieee80211_sta_ht_cap sta_ht_cap_new;
enum nl80211_channel_type channel_type =
- ieee80211_ht_info_to_channel_type(
- elems->ht_info_elem);
+ ieee80211_ht_oper_to_channel_type(
+ elems->ht_operation);
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
@@ -560,7 +558,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->name, mgmt->bssid);
#endif
ieee80211_sta_join_ibss(sdata, bss);
- supp_rates = ieee80211_sta_get_rates(local, elems, band);
+ supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates, true);
rcu_read_unlock();
@@ -1063,7 +1061,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
4 /* IBSS params */ +
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
2 + sizeof(struct ieee80211_ht_cap) +
- 2 + sizeof(struct ieee80211_ht_info) +
+ 2 + sizeof(struct ieee80211_ht_operation) +
params->ie_len);
if (!skb)
return -ENOMEM;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d9798a307f2..4be11ea3dfc 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -52,7 +52,8 @@ struct ieee80211_local;
* increased memory use (about 2 kB of RAM per entry). */
#define IEEE80211_FRAGMENT_MAX 4
-#define TU_TO_EXP_TIME(x) (jiffies + usecs_to_jiffies((x) * 1024))
+#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
+#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
#define IEEE80211_DEFAULT_UAPSD_QUEUES \
(IEEE80211_WMM_IE_STA_QOSINFO_AC_BK | \
@@ -378,6 +379,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
+ IEEE80211_STA_DISABLE_40MHZ = BIT(10),
};
struct ieee80211_mgd_auth_data {
@@ -397,7 +399,7 @@ struct ieee80211_mgd_auth_data {
struct ieee80211_mgd_assoc_data {
struct cfg80211_bss *bss;
const u8 *supp_rates;
- const u8 *ht_information_ie;
+ const u8 *ht_operation_ie;
unsigned long timeout;
int tries;
@@ -552,6 +554,24 @@ struct ieee80211_if_ibss {
} state;
};
+/**
+ * struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
+ *
+ * these declarations define the interface, which enables
+ * vendor-specific mesh synchronization
+ *
+ */
+struct ieee802_11_elems;
+struct ieee80211_mesh_sync_ops {
+ void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata,
+ u16 stype,
+ struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems,
+ struct ieee80211_rx_status *rx_status);
+ void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata);
+ /* add other framework functions here */
+};
+
struct ieee80211_if_mesh {
struct timer_list housekeeping_timer;
struct timer_list mesh_path_timer;
@@ -600,6 +620,11 @@ struct ieee80211_if_mesh {
IEEE80211_MESH_SEC_AUTHED = 0x1,
IEEE80211_MESH_SEC_SECURED = 0x2,
} security;
+ /* Extensible Synchronization Framework */
+ struct ieee80211_mesh_sync_ops *sync_ops;
+ s64 sync_offset_clockdrift_max;
+ spinlock_t sync_offset_lock;
+ bool adjusting_tbtt;
};
#ifdef CONFIG_MAC80211_MESH
@@ -666,12 +691,6 @@ struct ieee80211_sub_if_data {
char name[IFNAMSIZ];
- /*
- * keep track of whether the HT opmode (stored in
- * vif.bss_info.ht_operation_mode) is valid.
- */
- bool ht_opmode_valid;
-
/* to detect idle changes */
bool old_idle;
@@ -691,7 +710,7 @@ struct ieee80211_sub_if_data {
__be16 control_port_protocol;
bool control_port_no_encrypt;
- struct ieee80211_tx_queue_params tx_conf[IEEE80211_MAX_QUEUES];
+ struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
struct work_struct work;
struct sk_buff_head skb_queue;
@@ -761,7 +780,6 @@ enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
- IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE,
};
#ifdef CONFIG_MAC80211_LEDS
@@ -1082,6 +1100,9 @@ struct ieee80211_local {
struct net_device napi_dev;
struct napi_struct napi;
+
+ /* virtual monitor interface */
+ struct ieee80211_sub_if_data __rcu *monitor_sdata;
};
static inline struct ieee80211_sub_if_data *
@@ -1117,7 +1138,7 @@ struct ieee802_11_elems {
u8 *wmm_info;
u8 *wmm_param;
struct ieee80211_ht_cap *ht_cap_elem;
- struct ieee80211_ht_info *ht_info_elem;
+ struct ieee80211_ht_operation *ht_operation;
struct ieee80211_meshconf_ie *mesh_config;
u8 *mesh_id;
u8 *peering;
@@ -1299,7 +1320,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev);
/* HT */
-bool ieee80111_cfg_override_disables_ht40(struct ieee80211_sub_if_data *sdata);
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_ht_cap *ht_cap);
void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
@@ -1429,13 +1449,17 @@ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason);
void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason);
+void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);
void ieee80211_add_pending_skb(struct ieee80211_local *local,
struct sk_buff *skb);
-void ieee80211_add_pending_skbs(struct ieee80211_local *local,
- struct sk_buff_head *skbs);
void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
struct sk_buff_head *skbs,
void (*fn)(void *data), void *data);
+static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local,
+ struct sk_buff_head *skbs)
+{
+ ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL);
+}
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg,
@@ -1460,7 +1484,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const u8 *supp_rates);
u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
- enum ieee80211_band band);
+ enum ieee80211_band band, u32 *basic_rates);
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode);
void ieee80211_recalc_smps(struct ieee80211_local *local);
@@ -1470,10 +1494,9 @@ size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 cap);
-u8 *ieee80211_ie_build_ht_info(u8 *pos,
- struct ieee80211_sta_ht_cap *ht_cap,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type);
+u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type channel_type);
/* internal work items */
void ieee80211_work_init(struct ieee80211_local *local);
@@ -1501,10 +1524,7 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum nl80211_channel_type chantype);
enum nl80211_channel_type
-ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info);
-enum nl80211_channel_type ieee80211_get_tx_channel_type(
- struct ieee80211_local *local,
- enum nl80211_channel_type channel_type);
+ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 401c01f0731..6e85faed053 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -149,6 +149,34 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
return 0;
}
+static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata)
+{
+ int n_queues = sdata->local->hw.queues;
+ int i;
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ if (WARN_ON_ONCE(sdata->vif.hw_queue[i] ==
+ IEEE80211_INVAL_HW_QUEUE))
+ return -EINVAL;
+ if (WARN_ON_ONCE(sdata->vif.hw_queue[i] >=
+ n_queues))
+ return -EINVAL;
+ }
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP) {
+ sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
+ return 0;
+ }
+
+ if (WARN_ON_ONCE(sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE))
+ return -EINVAL;
+
+ if (WARN_ON_ONCE(sdata->vif.cab_queue >= n_queues))
+ return -EINVAL;
+
+ return 0;
+}
+
void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
const int offset)
{
@@ -169,6 +197,81 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
#undef ADJUST
}
+static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ int i;
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
+ sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE;
+ else
+ sdata->vif.hw_queue[i] = i;
+ }
+ sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
+}
+
+static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+ int ret;
+
+ if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
+ return 0;
+
+ if (local->monitor_sdata)
+ return 0;
+
+ sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
+ if (!sdata)
+ return -ENOMEM;
+
+ /* set up data */
+ sdata->local = local;
+ sdata->vif.type = NL80211_IFTYPE_MONITOR;
+ snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
+ wiphy_name(local->hw.wiphy));
+
+ ieee80211_set_default_queues(sdata);
+
+ ret = drv_add_interface(local, sdata);
+ if (WARN_ON(ret)) {
+ /* ok .. stupid driver, it asked for this! */
+ kfree(sdata);
+ return ret;
+ }
+
+ ret = ieee80211_check_queues(sdata);
+ if (ret) {
+ kfree(sdata);
+ return ret;
+ }
+
+ rcu_assign_pointer(local->monitor_sdata, sdata);
+
+ return 0;
+}
+
+static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
+ return;
+
+ sdata = rtnl_dereference(local->monitor_sdata);
+
+ if (!sdata)
+ return;
+
+ rcu_assign_pointer(local->monitor_sdata, NULL);
+ synchronize_net();
+
+ drv_remove_interface(local, sdata);
+
+ kfree(sdata);
+}
+
/*
* NOTE: Be very careful when changing this function, it must NOT return
* an error on interface type changes that have been pre-checked, so most
@@ -246,15 +349,18 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN);
if (!is_valid_ether_addr(dev->dev_addr)) {
- if (!local->open_count)
- drv_stop(local);
- return -EADDRNOTAVAIL;
+ res = -EADDRNOTAVAIL;
+ goto err_stop;
}
}
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
- /* no need to tell driver */
+ /* no need to tell driver, but set carrier */
+ if (rtnl_dereference(sdata->bss->beacon))
+ netif_carrier_on(dev);
+ else
+ netif_carrier_off(dev);
break;
case NL80211_IFTYPE_MONITOR:
if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
@@ -262,6 +368,12 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
break;
}
+ if (local->monitors == 0 && local->open_count == 0) {
+ res = ieee80211_add_virtual_monitor(local);
+ if (res)
+ goto err_stop;
+ }
+
/* must be before the call to ieee80211_configure_filter */
local->monitors++;
if (local->monitors == 1) {
@@ -276,9 +388,14 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
break;
default:
if (coming_up) {
+ ieee80211_del_virtual_monitor(local);
+
res = drv_add_interface(local, sdata);
if (res)
goto err_stop;
+ res = ieee80211_check_queues(sdata);
+ if (res)
+ goto err_del_interface;
}
if (sdata->vif.type == NL80211_IFTYPE_AP) {
@@ -294,7 +411,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
ieee80211_bss_info_change_notify(sdata, changed);
if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ sdata->vif.type == NL80211_IFTYPE_ADHOC ||
+ sdata->vif.type == NL80211_IFTYPE_AP)
netif_carrier_off(dev);
else
netif_carrier_on(dev);
@@ -366,6 +484,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
sdata->bss = NULL;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
list_del(&sdata->u.vlan.list);
+ /* might already be clear but that doesn't matter */
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
return res;
}
@@ -506,6 +625,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
if (local->monitors == 0) {
local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
+ ieee80211_del_virtual_monitor(local);
}
ieee80211_adjust_monitor_flags(sdata, -1);
@@ -579,6 +699,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
}
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+ if (local->monitors == local->open_count && local->monitors > 0)
+ ieee80211_add_virtual_monitor(local);
}
static int ieee80211_stop(struct net_device *dev)
@@ -676,7 +799,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
struct ieee80211_hdr *hdr;
struct ieee80211_radiotap_header *rtap = (void *)skb->data;
- if (local->hw.queues < 4)
+ if (local->hw.queues < IEEE80211_NUM_ACS)
return 0;
if (skb->len < 4 ||
@@ -970,6 +1093,13 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
if (ret)
type = sdata->vif.type;
+ /*
+ * Ignore return value here, there's not much we can do since
+ * the driver changed the interface type internally already.
+ * The warnings will hopefully make driver authors fix it :-)
+ */
+ ieee80211_check_queues(sdata);
+
ieee80211_setup_sdata(sdata, type);
err = ieee80211_do_open(sdata->dev, false);
@@ -1133,11 +1263,15 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct net_device *ndev;
struct ieee80211_sub_if_data *sdata = NULL;
int ret, i;
+ int txqs = 1;
ASSERT_RTNL();
+ if (local->hw.queues >= IEEE80211_NUM_ACS)
+ txqs = IEEE80211_NUM_ACS;
+
ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,
- name, ieee80211_if_setup, local->hw.queues, 1);
+ name, ieee80211_if_setup, txqs, 1);
if (!ndev)
return -ENOMEM;
dev_net_set(ndev, wiphy_net(local->hw.wiphy));
@@ -1192,6 +1326,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sizeof(sdata->rc_rateidx_mcs_mask[i]));
}
+ ieee80211_set_default_queues(sdata);
+
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 16336480c63..ac79d5e8e0d 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -557,8 +557,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
WIPHY_FLAG_4ADDR_AP |
WIPHY_FLAG_4ADDR_STATION |
WIPHY_FLAG_REPORTS_OBSS |
- WIPHY_FLAG_OFFCHAN_TX |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ WIPHY_FLAG_OFFCHAN_TX;
+
+ if (ops->remain_on_channel)
+ wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
wiphy->features = NL80211_FEATURE_SK_TX_STATUS |
NL80211_FEATURE_HT_IBSS;
@@ -589,6 +591,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.max_report_rates = 0;
local->hw.max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
local->hw.max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
+ local->hw.offchannel_tx_hw_queue = IEEE80211_INVAL_HW_QUEUE;
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
local->user_power_level = -1;
@@ -685,6 +688,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
WLAN_CIPHER_SUITE_AES_CMAC
};
+ if (hw->flags & IEEE80211_HW_QUEUE_CONTROL &&
+ (local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||
+ local->hw.offchannel_tx_hw_queue >= local->hw.queues))
+ return -EINVAL;
+
if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns)
#ifdef CONFIG_PM
&& (!local->ops->suspend || !local->ops->resume)
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index e5fbb7cf356..133c118526f 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -13,9 +13,6 @@
#include "ieee80211_i.h"
#include "mesh.h"
-#define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01
-#define MESHCONF_CAPAB_FORWARDING 0x08
-
#define TMR_RUNNING_HK 0
#define TMR_RUNNING_MP 1
#define TMR_RUNNING_MPR 2
@@ -69,11 +66,13 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
*
* @ie: information elements of a management frame from the mesh peer
* @sdata: local mesh subif
+ * @basic_rates: BSSBasicRateSet of the peer candidate
*
* This function checks if the mesh configuration of a mesh point matches the
* local mesh configuration, i.e. if both nodes belong to the same mesh network.
*/
-bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_data *sdata)
+bool mesh_matches_local(struct ieee802_11_elems *ie,
+ struct ieee80211_sub_if_data *sdata, u32 basic_rates)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
@@ -97,10 +96,13 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat
(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
goto mismatch;
+ if (sdata->vif.bss_conf.basic_rates != basic_rates)
+ goto mismatch;
+
/* disallow peering with mismatched channel types for now */
- if (ie->ht_info_elem &&
+ if (ie->ht_operation &&
(local->_oper_channel_type !=
- ieee80211_ht_info_to_channel_type(ie->ht_info_elem)))
+ ieee80211_ht_oper_to_channel_type(ie->ht_operation)))
goto mismatch;
return true;
@@ -251,8 +253,10 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
/* Mesh capability */
ifmsh->accepting_plinks = mesh_plink_availables(sdata);
*pos = MESHCONF_CAPAB_FORWARDING;
- *pos++ |= ifmsh->accepting_plinks ?
+ *pos |= ifmsh->accepting_plinks ?
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
+ *pos++ |= ifmsh->adjusting_tbtt ?
+ MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
*pos++ = 0x00;
return 0;
@@ -371,7 +375,7 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb,
return 0;
}
-int mesh_add_ht_info_ie(struct sk_buff *skb,
+int mesh_add_ht_oper_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@@ -385,11 +389,11 @@ int mesh_add_ht_info_ie(struct sk_buff *skb,
if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT)
return 0;
- if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_info))
+ if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation))
return -ENOMEM;
- pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_info));
- ieee80211_ie_build_ht_info(pos, ht_cap, channel, channel_type);
+ pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
+ ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type);
return 0;
}
@@ -573,14 +577,21 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
ieee80211_configure_filter(local);
ifmsh->mesh_cc_id = 0; /* Disabled */
- ifmsh->mesh_sp_id = 0; /* Neighbor Offset */
ifmsh->mesh_auth_id = 0; /* Disabled */
+ /* register sync ops from extensible synchronization framework */
+ ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
+ ifmsh->adjusting_tbtt = false;
+ ifmsh->sync_offset_clockdrift_max = 0;
set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
ieee80211_mesh_root_setup(ifmsh);
ieee80211_queue_work(&local->hw, &sdata->work);
sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
+ sdata->vif.bss_conf.basic_rates =
+ ieee80211_mandatory_rates(sdata->local,
+ sdata->local->hw.conf.channel->band);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED |
+ BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT);
}
@@ -616,9 +627,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_status *rx_status)
{
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee802_11_elems elems;
struct ieee80211_channel *channel;
- u32 supp_rates = 0;
+ u32 supp_rates = 0, basic_rates = 0;
size_t baselen;
int freq;
enum ieee80211_band band = rx_status->band;
@@ -649,11 +661,16 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
+ supp_rates = ieee80211_sta_get_rates(local, &elems,
+ band, &basic_rates);
+
if (elems.mesh_id && elems.mesh_config &&
- mesh_matches_local(&elems, sdata)) {
- supp_rates = ieee80211_sta_get_rates(local, &elems, band);
+ mesh_matches_local(&elems, sdata, basic_rates))
mesh_neighbour_update(mgmt->sa, supp_rates, sdata, &elems);
- }
+
+ if (ifmsh->sync_ops)
+ ifmsh->sync_ops->rx_bcn_presp(sdata,
+ stype, mgmt, &elems, rx_status);
}
static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
@@ -721,6 +738,9 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags))
ieee80211_mesh_rootpath(sdata);
+
+ if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
+ mesh_sync_adjust_tbtt(sdata);
}
void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
@@ -761,4 +781,5 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
(unsigned long) sdata);
INIT_LIST_HEAD(&ifmsh->preq_queue.list);
spin_lock_init(&ifmsh->mesh_preq_queue_lock);
+ spin_lock_init(&ifmsh->sync_offset_lock);
}
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 8d53b71378e..4ad73898880 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -19,6 +19,20 @@
/* Data structures */
/**
+ * enum mesh_config_capab_flags - mesh config IE capability flags
+ *
+ * @MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish
+ * additional mesh peerings with other mesh STAs
+ * @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs
+ * @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing
+ */
+enum mesh_config_capab_flags {
+ MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0),
+ MESHCONF_CAPAB_FORWARDING = BIT(3),
+ MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5),
+};
+
+/**
* enum mesh_path_flags - mac80211 mesh path flags
*
*
@@ -56,12 +70,15 @@ enum mesh_path_flags {
* @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to
* grow
* @MESH_WORK_ROOT: the mesh root station needs to send a frame
+ * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other
+ * mesh nodes
*/
enum mesh_deferred_task_flags {
MESH_WORK_HOUSEKEEPING,
MESH_WORK_GROW_MPATH_TABLE,
MESH_WORK_GROW_MPP_TABLE,
MESH_WORK_ROOT,
+ MESH_WORK_DRIFT_ADJUST,
};
/**
@@ -86,6 +103,7 @@ enum mesh_deferred_task_flags {
* mpath itself. No need to take this lock when adding or removing
* an mpath to a hash bucket on a path table.
* @rann_snd_addr: the RANN sender address
+ * @rann_metric: the aggregated path metric towards the root node
* @is_root: the destination station of this path is a root node
* @is_gate: the destination station of this path is a mesh gate
*
@@ -112,6 +130,7 @@ struct mesh_path {
enum mesh_path_flags flags;
spinlock_t state_lock;
u8 rann_snd_addr[ETH_ALEN];
+ u32 rann_metric;
bool is_root;
bool is_gate;
};
@@ -204,7 +223,7 @@ int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
struct ieee80211_sub_if_data *sdata);
bool mesh_matches_local(struct ieee802_11_elems *ie,
- struct ieee80211_sub_if_data *sdata);
+ struct ieee80211_sub_if_data *sdata, u32 basic_rates);
void mesh_ids_set_default(struct ieee80211_if_mesh *mesh);
void mesh_mgmt_ies_add(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
@@ -220,7 +239,7 @@ int mesh_add_ds_params_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
int mesh_add_ht_cap_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
-int mesh_add_ht_info_ie(struct sk_buff *skb,
+int mesh_add_ht_oper_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
@@ -232,6 +251,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
+struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
@@ -325,6 +345,7 @@ void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
void mesh_plink_quiesce(struct sta_info *sta);
void mesh_plink_restart(struct sta_info *sta);
+void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata);
#else
#define mesh_allocated 0
static inline void
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 1c6f3d02aeb..a80da3784a2 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -86,8 +86,8 @@ static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae)
#define PERR_IE_TARGET_RCODE(x) u16_field_get(x, 13, 0)
#define MSEC_TO_TU(x) (x*1000/1024)
-#define SN_GT(x, y) ((long) (y) - (long) (x) < 0)
-#define SN_LT(x, y) ((long) (x) - (long) (y) < 0)
+#define SN_GT(x, y) ((s32)(y - x) < 0)
+#define SN_LT(x, y) ((s32)(x - y) < 0)
#define net_traversal_jiffies(s) \
msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
@@ -732,11 +732,12 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rann_ie *rann)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
struct mesh_path *mpath;
u8 ttl, flags, hopcount;
u8 *orig_addr;
- u32 orig_sn, metric;
- u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval;
+ u32 orig_sn, metric, metric_txsta, interval;
bool root_is_gate;
ttl = rann->rann_ttl;
@@ -748,10 +749,11 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
flags = rann->rann_flags;
root_is_gate = !!(flags & RANN_FLAG_IS_GATE);
orig_addr = rann->rann_addr;
- orig_sn = rann->rann_seq;
+ orig_sn = le32_to_cpu(rann->rann_seq);
+ interval = le32_to_cpu(rann->rann_interval);
hopcount = rann->rann_hopcount;
hopcount++;
- metric = rann->rann_metric;
+ metric = le32_to_cpu(rann->rann_metric);
/* Ignore our own RANNs */
if (compare_ether_addr(orig_addr, sdata->vif.addr) == 0)
@@ -761,6 +763,14 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
orig_addr, mgmt->sa, root_is_gate);
rcu_read_lock();
+ sta = sta_info_get(sdata, mgmt->sa);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
+ metric_txsta = airtime_link_metric_get(local, sta);
+
mpath = mesh_path_lookup(orig_addr, sdata);
if (!mpath) {
mesh_path_add(orig_addr, sdata);
@@ -780,14 +790,16 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH);
}
- if (mpath->sn < orig_sn && ifmsh->mshcfg.dot11MeshForwarding) {
+ if ((SN_LT(mpath->sn, orig_sn) || (mpath->sn == orig_sn &&
+ metric < mpath->rann_metric)) && ifmsh->mshcfg.dot11MeshForwarding) {
mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr,
cpu_to_le32(orig_sn),
0, NULL, 0, broadcast_addr,
hopcount, ttl, cpu_to_le32(interval),
- cpu_to_le32(metric + mpath->metric),
+ cpu_to_le32(metric + metric_txsta),
0, sdata);
mpath->sn = orig_sn;
+ mpath->rann_metric = metric + metric_txsta;
}
/* Using individually addressed PREQ for root node */
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 4e53c4cbca9..9c836e774fb 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -187,7 +187,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
2 + sdata->u.mesh.mesh_id_len +
2 + sizeof(struct ieee80211_meshconf_ie) +
2 + sizeof(struct ieee80211_ht_cap) +
- 2 + sizeof(struct ieee80211_ht_info) +
+ 2 + sizeof(struct ieee80211_ht_operation) +
2 + 8 + /* peering IE */
sdata->u.mesh.ie_len);
if (!skb)
@@ -212,8 +212,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, 2);
memcpy(pos + 2, &plid, 2);
}
- if (ieee80211_add_srates_ie(&sdata->vif, skb) ||
- ieee80211_add_ext_srates_ie(&sdata->vif, skb) ||
+ if (ieee80211_add_srates_ie(&sdata->vif, skb, true) ||
+ ieee80211_add_ext_srates_ie(&sdata->vif, skb, true) ||
mesh_add_rsn_ie(skb, sdata) ||
mesh_add_meshid_ie(skb, sdata) ||
mesh_add_meshconf_ie(skb, sdata))
@@ -263,7 +263,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
if (action != WLAN_SP_MESH_PEERING_CLOSE) {
if (mesh_add_ht_cap_ie(skb, sdata) ||
- mesh_add_ht_info_ie(skb, sdata))
+ mesh_add_ht_oper_ie(skb, sdata))
return -1;
}
@@ -465,6 +465,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
bool deactivated, matches_local = true;
u8 ie_len;
u8 *baseaddr;
+ u32 rates, basic_rates = 0;
__le16 plid, llid, reason;
#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
static const char *mplstates[] = {
@@ -559,8 +560,11 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
/* Now we will figure out the appropriate event... */
event = PLINK_UNDEFINED;
+ rates = ieee80211_sta_get_rates(local, &elems,
+ rx_status->band, &basic_rates);
+
if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
- (!mesh_matches_local(&elems, sdata))) {
+ (!mesh_matches_local(&elems, sdata, basic_rates))) {
matches_local = false;
switch (ftype) {
case WLAN_SP_MESH_PEERING_OPEN:
@@ -583,7 +587,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
return;
} else if (!sta) {
/* ftype == WLAN_SP_MESH_PEERING_OPEN */
- u32 rates;
rcu_read_unlock();
@@ -591,8 +594,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
mpl_dbg("Mesh plink error: no more free plinks\n");
return;
}
-
- rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
sta = mesh_plink_alloc(sdata, mgmt->sa, rates, &elems);
if (!sta) {
mpl_dbg("Mesh plink error: plink table full\n");
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
new file mode 100644
index 00000000000..f78b0139856
--- /dev/null
+++ b/net/mac80211/mesh_sync.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2011-2012, Pavel Zubarev <pavel.zubarev@gmail.com>
+ * Copyright 2011-2012, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de>
+ * Copyright 2011-2012, cozybit 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.
+ */
+
+#include "ieee80211_i.h"
+#include "mesh.h"
+#include "driver-ops.h"
+
+#ifdef CONFIG_MAC80211_VERBOSE_MESH_SYNC_DEBUG
+#define msync_dbg(fmt, args...) \
+ printk(KERN_DEBUG "Mesh sync (%s): " fmt "\n", sdata->name, ##args)
+#else
+#define msync_dbg(fmt, args...) do { (void)(0); } while (0)
+#endif
+
+/* This is not in the standard. It represents a tolerable tbtt drift below
+ * which we do no TSF adjustment.
+ */
+#define TBTT_MINIMUM_ADJUSTMENT 10
+
+struct sync_method {
+ u8 method;
+ struct ieee80211_mesh_sync_ops ops;
+};
+
+/**
+ * mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT
+ *
+ * @ie: information elements of a management frame from the mesh peer
+ */
+static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
+{
+ return (ie->mesh_config->meshconf_cap &
+ MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
+}
+
+void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ /* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */
+ u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500;
+ u64 tsf;
+ u64 tsfdelta;
+
+ spin_lock_bh(&ifmsh->sync_offset_lock);
+
+ if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) {
+ msync_dbg("TBTT : max clockdrift=%lld; adjusting",
+ (long long) ifmsh->sync_offset_clockdrift_max);
+ tsfdelta = -ifmsh->sync_offset_clockdrift_max;
+ ifmsh->sync_offset_clockdrift_max = 0;
+ } else {
+ msync_dbg("TBTT : max clockdrift=%lld; adjusting by %llu",
+ (long long) ifmsh->sync_offset_clockdrift_max,
+ (unsigned long long) beacon_int_fraction);
+ tsfdelta = -beacon_int_fraction;
+ ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction;
+ }
+
+ tsf = drv_get_tsf(local, sdata);
+ if (tsf != -1ULL)
+ drv_set_tsf(local, sdata, tsf + tsfdelta);
+ spin_unlock_bh(&ifmsh->sync_offset_lock);
+}
+
+static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
+ u16 stype,
+ struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+ u64 t_t, t_r;
+
+ WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
+
+ /* standard mentions only beacons */
+ if (stype != IEEE80211_STYPE_BEACON)
+ return;
+
+ /* The current tsf is a first approximation for the timestamp
+ * for the received beacon. Further down we try to get a
+ * better value from the rx_status->mactime field if
+ * available. Also we have to call drv_get_tsf() before
+ * entering the rcu-read section.*/
+ t_r = drv_get_tsf(local, sdata);
+
+ rcu_read_lock();
+ sta = sta_info_get(sdata, mgmt->sa);
+ if (!sta)
+ goto no_sync;
+
+ /* check offset sync conditions (13.13.2.2.1)
+ *
+ * TODO also sync to
+ * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors
+ */
+
+ if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) {
+ clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
+ msync_dbg("STA %pM : is adjusting TBTT", sta->sta.addr);
+ goto no_sync;
+ }
+
+ if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) {
+ /*
+ * The mactime is defined as the time the first data symbol
+ * of the frame hits the PHY, and the timestamp of the beacon
+ * is defined as "the time that the data symbol containing the
+ * first bit of the timestamp is transmitted to the PHY plus
+ * the transmitting STA's delays through its local PHY from the
+ * MAC-PHY interface to its interface with the WM" (802.11
+ * 11.1.2)
+ *
+ * T_r, in 13.13.2.2.2, is just defined as "the frame reception
+ * time" but we unless we interpret that time to be the same
+ * time of the beacon timestamp, the offset calculation will be
+ * off. Below we adjust t_r to be "the time at which the first
+ * symbol of the timestamp element in the beacon is received".
+ * This correction depends on the rate.
+ *
+ * Based on similar code in ibss.c
+ */
+ int rate;
+
+ if (rx_status->flag & RX_FLAG_HT) {
+ /* TODO:
+ * In principle there could be HT-beacons (Dual Beacon
+ * HT Operation options), but for now ignore them and
+ * just use the primary (i.e. non-HT) beacons for
+ * synchronization.
+ * */
+ goto no_sync;
+ } else
+ rate = local->hw.wiphy->bands[rx_status->band]->
+ bitrates[rx_status->rate_idx].bitrate;
+
+ /* 24 bytes of header * 8 bits/byte *
+ * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/
+ t_r = rx_status->mactime + (24 * 8 * 10 / rate);
+ }
+
+ /* Timing offset calculation (see 13.13.2.2.2) */
+ t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
+ sta->t_offset = t_t - t_r;
+
+ if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
+ s64 t_clockdrift = sta->t_offset_setpoint
+ - sta->t_offset;
+
+ msync_dbg("STA %pM : sta->t_offset=%lld,"
+ " sta->t_offset_setpoint=%lld,"
+ " t_clockdrift=%lld",
+ sta->sta.addr,
+ (long long) sta->t_offset,
+ (long long)
+ sta->t_offset_setpoint,
+ (long long) t_clockdrift);
+ rcu_read_unlock();
+
+ spin_lock_bh(&ifmsh->sync_offset_lock);
+ if (t_clockdrift >
+ ifmsh->sync_offset_clockdrift_max)
+ ifmsh->sync_offset_clockdrift_max
+ = t_clockdrift;
+ spin_unlock_bh(&ifmsh->sync_offset_lock);
+
+ } else {
+ sta->t_offset_setpoint = sta->t_offset;
+ set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
+ msync_dbg("STA %pM : offset was invalid, "
+ " sta->t_offset=%lld",
+ sta->sta.addr,
+ (long long) sta->t_offset);
+ rcu_read_unlock();
+ }
+ return;
+
+no_sync:
+ rcu_read_unlock();
+}
+
+static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ WARN_ON(ifmsh->mesh_sp_id
+ != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
+ BUG_ON(!rcu_read_lock_held());
+
+ spin_lock_bh(&ifmsh->sync_offset_lock);
+
+ if (ifmsh->sync_offset_clockdrift_max >
+ TBTT_MINIMUM_ADJUSTMENT) {
+ /* Since ajusting the tsf here would
+ * require a possibly blocking call
+ * to the driver tsf setter, we punt
+ * the tsf adjustment to the mesh tasklet
+ */
+ msync_dbg("TBTT : kicking off TBTT "
+ "adjustment with "
+ "clockdrift_max=%lld",
+ ifmsh->sync_offset_clockdrift_max);
+ set_bit(MESH_WORK_DRIFT_ADJUST,
+ &ifmsh->wrkq_flags);
+ } else {
+ msync_dbg("TBTT : max clockdrift=%lld; "
+ "too small to adjust",
+ (long long)
+ ifmsh->sync_offset_clockdrift_max);
+ ifmsh->sync_offset_clockdrift_max = 0;
+ }
+ spin_unlock_bh(&ifmsh->sync_offset_lock);
+}
+
+static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ u8 offset;
+
+ if (!ifmsh->ie || !ifmsh->ie_len)
+ return NULL;
+
+ offset = ieee80211_ie_split_vendor(ifmsh->ie,
+ ifmsh->ie_len, 0);
+
+ if (!offset)
+ return NULL;
+
+ return ifmsh->ie + offset + 2;
+}
+
+static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
+ u16 stype,
+ struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems,
+ struct ieee80211_rx_status *rx_status)
+{
+ const u8 *oui;
+
+ WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
+ msync_dbg("called mesh_sync_vendor_rx_bcn_presp");
+ oui = mesh_get_vendor_oui(sdata);
+ /* here you would implement the vendor offset tracking for this oui */
+}
+
+static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
+{
+ const u8 *oui;
+
+ WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
+ msync_dbg("called mesh_sync_vendor_adjust_tbtt");
+ oui = mesh_get_vendor_oui(sdata);
+ /* here you would implement the vendor tsf adjustment for this oui */
+}
+
+/* global variable */
+static struct sync_method sync_methods[] = {
+ {
+ .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
+ .ops = {
+ .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp,
+ .adjust_tbtt = &mesh_sync_offset_adjust_tbtt,
+ }
+ },
+ {
+ .method = IEEE80211_SYNC_METHOD_VENDOR,
+ .ops = {
+ .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp,
+ .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt,
+ }
+ },
+};
+
+struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method)
+{
+ struct ieee80211_mesh_sync_ops *ops = NULL;
+ u8 i;
+
+ for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) {
+ if (sync_methods[i].method == method) {
+ ops = &sync_methods[i].ops;
+ break;
+ }
+ }
+ return ops;
+}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index f76da5b3f5c..bbf1100d90d 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -171,122 +171,64 @@ static int ecw2cw(int ecw)
return (1 << ecw) - 1;
}
-/*
- * ieee80211_enable_ht should be called only after the operating band
- * has been determined as ht configuration depends on the hw's
- * HT abilities for a specific band.
- */
-static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_ht_info *hti,
- const u8 *bssid, u16 ap_ht_cap_flags,
- bool beacon_htcap_ie)
+static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_operation *ht_oper,
+ const u8 *bssid, bool reconfig)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct sta_info *sta;
u32 changed = 0;
- int hti_cfreq;
u16 ht_opmode;
- bool enable_ht = true;
- enum nl80211_channel_type prev_chantype;
- enum nl80211_channel_type rx_channel_type = NL80211_CHAN_NO_HT;
- enum nl80211_channel_type tx_channel_type;
+ bool disable_40 = false;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- prev_chantype = sdata->vif.bss_conf.channel_type;
-
- hti_cfreq = ieee80211_channel_to_frequency(hti->control_chan,
- sband->band);
- /* check that channel matches the right operating channel */
- if (local->hw.conf.channel->center_freq != hti_cfreq) {
- /* Some APs mess this up, evidently.
- * Netgear WNDR3700 sometimes reports 4 higher than
- * the actual channel, for instance.
- */
- printk(KERN_DEBUG
- "%s: Wrong control channel in association"
- " response: configured center-freq: %d"
- " hti-cfreq: %d hti->control_chan: %d"
- " band: %d. Disabling HT.\n",
- sdata->name,
- local->hw.conf.channel->center_freq,
- hti_cfreq, hti->control_chan,
- sband->band);
- enable_ht = false;
- }
-
- if (enable_ht) {
- rx_channel_type = NL80211_CHAN_HT20;
-
- if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
- !ieee80111_cfg_override_disables_ht40(sdata) &&
- (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
- (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
- switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- rx_channel_type = NL80211_CHAN_HT40PLUS;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- rx_channel_type = NL80211_CHAN_HT40MINUS;
- break;
- }
- }
+ switch (sdata->vif.bss_conf.channel_type) {
+ case NL80211_CHAN_HT40PLUS:
+ if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ disable_40 = true;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ disable_40 = true;
+ break;
+ default:
+ break;
}
- tx_channel_type = ieee80211_get_tx_channel_type(local, rx_channel_type);
-
- if (local->tmp_channel)
- local->tmp_channel_type = rx_channel_type;
+ /* This can change during the lifetime of the BSS */
+ if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
+ disable_40 = true;
- if (!ieee80211_set_channel_type(local, sdata, rx_channel_type)) {
- /* can only fail due to HT40+/- mismatch */
- rx_channel_type = NL80211_CHAN_HT20;
- WARN_ON(!ieee80211_set_channel_type(local, sdata,
- rx_channel_type));
- }
-
- if (beacon_htcap_ie && (prev_chantype != rx_channel_type)) {
- /*
- * Whenever the AP announces the HT mode change that can be
- * 40MHz intolerant or etc., it would be safer to stop tx
- * queues before doing hw config to avoid buffer overflow.
- */
- ieee80211_stop_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, bssid);
- /* flush out all packets */
- synchronize_net();
+ WARN_ON_ONCE(!sta);
- drv_flush(local, false);
- }
+ if (sta && !sta->supports_40mhz)
+ disable_40 = true;
- /* channel_type change automatically detected */
- ieee80211_hw_config(local, 0);
+ if (sta && (!reconfig ||
+ (disable_40 != !!(sta->sta.ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) {
- if (prev_chantype != tx_channel_type) {
- rcu_read_lock();
- sta = sta_info_get(sdata, bssid);
- if (sta)
- rate_control_rate_update(local, sband, sta,
- IEEE80211_RC_HT_CHANGED,
- tx_channel_type);
- rcu_read_unlock();
+ if (disable_40)
+ sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ else
+ sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- if (beacon_htcap_ie)
- ieee80211_wake_queues_by_reason(&sdata->local->hw,
- IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
+ rate_control_rate_update(local, sband, sta,
+ IEEE80211_RC_BW_CHANGED);
}
+ mutex_unlock(&local->sta_mtx);
- ht_opmode = le16_to_cpu(hti->operation_mode);
+ ht_opmode = le16_to_cpu(ht_oper->operation_mode);
/* if bss configuration changed store the new one */
- if (sdata->ht_opmode_valid != enable_ht ||
- sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
- prev_chantype != rx_channel_type) {
+ if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) {
changed |= BSS_CHANGED_HT;
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
- sdata->ht_opmode_valid = enable_ht;
}
return changed;
@@ -316,12 +258,12 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
}
static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, const u8 *ht_info_ie,
+ struct sk_buff *skb, const u8 *ht_oper_ie,
struct ieee80211_supported_band *sband,
struct ieee80211_channel *channel,
enum ieee80211_smps_mode smps)
{
- struct ieee80211_ht_info *ht_info;
+ struct ieee80211_ht_operation *ht_oper;
u8 *pos;
u32 flags = channel->flags;
u16 cap;
@@ -329,21 +271,21 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
- if (!ht_info_ie)
+ if (!ht_oper_ie)
return;
- if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
+ if (ht_oper_ie[1] < sizeof(struct ieee80211_ht_operation))
return;
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
- ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
+ ht_oper = (struct ieee80211_ht_operation *)(ht_oper_ie + 2);
/* determine capability flags */
cap = ht_cap.cap;
- switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -358,6 +300,16 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
break;
}
+ /*
+ * If 40 MHz was disabled associate as though we weren't
+ * capable of 40 MHz -- some broken APs will never fall
+ * back to trying to transmit in 20 MHz.
+ */
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_40MHZ) {
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ cap &= ~IEEE80211_HT_CAP_SGI_40;
+ }
+
/* set SM PS mode properly */
cap &= ~IEEE80211_HT_CAP_SM_PS;
switch (smps) {
@@ -557,7 +509,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
- ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_information_ie,
+ ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_operation_ie,
sband, local->oper_channel, ifmgd->ap_smps);
/* if present, add any custom non-vendor IEs that go after HT */
@@ -1182,7 +1134,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
if (!local->ops->conf_tx)
return;
- if (local->hw.queues < 4)
+ if (local->hw.queues < IEEE80211_NUM_ACS)
return;
if (!wmm_param)
@@ -1435,7 +1387,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.assoc = false;
/* on the next assoc, re-program HT parameters */
- sdata->ht_opmode_valid = false;
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
@@ -1567,14 +1518,23 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ifmgd->nullfunc_failed = false;
ieee80211_send_nullfunc(sdata->local, sdata, 0);
} else {
+ int ssid_len;
+
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
- ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0,
- (u32) -1, true, false);
+ if (WARN_ON_ONCE(ssid == NULL))
+ ssid_len = 0;
+ else
+ ssid_len = ssid[1];
+
+ ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
+ 0, (u32) -1, true, false);
}
ifmgd->probe_send_count++;
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
run_again(ifmgd, ifmgd->probe_timeout);
+ if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ drv_flush(sdata->local, false);
}
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@ -1643,6 +1603,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct sk_buff *skb;
const u8 *ssid;
+ int ssid_len;
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return NULL;
@@ -1653,8 +1614,13 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
return NULL;
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+ if (WARN_ON_ONCE(ssid == NULL))
+ ssid_len = 0;
+ else
+ ssid_len = ssid[1];
+
skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
- (u32) -1, ssid + 2, ssid[1],
+ (u32) -1, ssid + 2, ssid_len,
NULL, 0, true);
return skb;
@@ -2000,7 +1966,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
u32 changed = 0;
int err;
- u16 ap_ht_cap_flags;
/* AssocResp and ReassocResp have identical structure */
@@ -2051,7 +2016,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
- ap_ht_cap_flags = sta->sta.ht_cap.cap;
+ sta->supports_40mhz =
+ sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
rate_control_rate_init(sta);
@@ -2092,11 +2058,10 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ieee80211_set_wmm_default(sdata, false);
changed |= BSS_CHANGED_QOS;
- if (elems.ht_info_elem && elems.wmm_param &&
+ if (elems.ht_operation && elems.wmm_param &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
- changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- cbss->bssid, ap_ht_cap_flags,
- false);
+ changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
+ cbss->bssid, false);
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
@@ -2319,7 +2284,7 @@ static const u64 care_about_ies =
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
(1ULL << WLAN_EID_HT_CAPABILITY) |
- (1ULL << WLAN_EID_HT_INFORMATION);
+ (1ULL << WLAN_EID_HT_OPERATION);
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
@@ -2468,11 +2433,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
if (directed_tim) {
if (local->hw.conf.dynamic_ps_timeout > 0) {
- local->hw.conf.flags &= ~IEEE80211_CONF_PS;
- ieee80211_hw_config(local,
- IEEE80211_CONF_CHANGE_PS);
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_PS);
+ }
ieee80211_send_nullfunc(local, sdata, 0);
- } else {
+ } else if (!local->pspolling && sdata->u.mgd.powersave) {
local->pspolling = true;
/*
@@ -2504,31 +2471,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
erp_valid, erp_value);
- if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
+ if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
- struct sta_info *sta;
struct ieee80211_supported_band *sband;
- u16 ap_ht_cap_flags;
-
- rcu_read_lock();
-
- sta = sta_info_get(sdata, bssid);
- if (WARN_ON(!sta)) {
- rcu_read_unlock();
- return;
- }
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
- elems.ht_cap_elem, &sta->sta.ht_cap);
-
- ap_ht_cap_flags = sta->sta.ht_cap.cap;
-
- rcu_read_unlock();
-
- changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- bssid, ap_ht_cap_flags, true);
+ changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
+ bssid, true);
}
/* Note: country IE parsing is done for us by cfg80211 */
@@ -3060,6 +3010,11 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta;
bool have_sta = false;
int err;
+ int ht_cfreq;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+ const u8 *ht_oper_ie;
+ const struct ieee80211_ht_operation *ht_oper = NULL;
+ struct ieee80211_supported_band *sband;
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
return -EINVAL;
@@ -3081,17 +3036,76 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->mtx);
/* switch to the right channel */
+ sband = local->hw.wiphy->bands[cbss->channel->band];
+
+ ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
+
+ if (sband->ht_cap.ht_supported) {
+ ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
+ cbss->information_elements,
+ cbss->len_information_elements);
+ if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
+ ht_oper = (void *)(ht_oper_ie + 2);
+ }
+
+ if (ht_oper) {
+ ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
+ cbss->channel->band);
+ /* check that channel matches the right operating channel */
+ if (cbss->channel->center_freq != ht_cfreq) {
+ /*
+ * It's possible that some APs are confused here;
+ * Netgear WNDR3700 sometimes reports 4 higher than
+ * the actual channel in association responses, but
+ * since we look at probe response/beacon data here
+ * it should be OK.
+ */
+ printk(KERN_DEBUG
+ "%s: Wrong control channel: center-freq: %d"
+ " ht-cfreq: %d ht->primary_chan: %d"
+ " band: %d. Disabling HT.\n",
+ sdata->name, cbss->channel->center_freq,
+ ht_cfreq, ht_oper->primary_chan,
+ cbss->channel->band);
+ ht_oper = NULL;
+ }
+ }
+
+ if (ht_oper) {
+ channel_type = NL80211_CHAN_HT20;
+
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ switch (ht_oper->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ }
+ }
+ }
+
+ if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
+ /* can only fail due to HT40+/- mismatch */
+ channel_type = NL80211_CHAN_HT20;
+ printk(KERN_DEBUG
+ "%s: disabling 40 MHz due to multi-vif mismatch\n",
+ sdata->name);
+ ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
+ WARN_ON(!ieee80211_set_channel_type(local, sdata,
+ channel_type));
+ }
+
local->oper_channel = cbss->channel;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ ieee80211_hw_config(local, 0);
if (!have_sta) {
- struct ieee80211_supported_band *sband;
u32 rates = 0, basic_rates = 0;
bool have_higher_than_11mbit;
int min_rate = INT_MAX, min_rate_index = -1;
- sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
-
ieee80211_get_rates(sband, bss->supp_rates,
bss->supp_rates_len,
&rates, &basic_rates,
@@ -3311,7 +3325,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* Also disable HT if we don't support it or the AP doesn't use WMM */
sband = local->hw.wiphy->bands[req->bss->channel->band];
if (!sband->ht_cap.ht_supported ||
- local->hw.queues < 4 || !bss->wmm_used)
+ local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used)
ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
@@ -3334,11 +3348,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->ap_smps = ifmgd->req_smps;
assoc_data->capability = req->bss->capability;
- assoc_data->wmm = bss->wmm_used && (local->hw.queues >= 4);
+ assoc_data->wmm = bss->wmm_used &&
+ (local->hw.queues >= IEEE80211_NUM_ACS);
assoc_data->supp_rates = bss->supp_rates;
assoc_data->supp_rates_len = bss->supp_rates_len;
- assoc_data->ht_information_ie =
- ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
+ assoc_data->ht_operation_ie =
+ ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
if (bss->wmm_used && bss->uapsd_supported &&
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index ef8eba1d736..af1c4e26e96 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -127,6 +127,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
drv_remove_interface(local, sdata);
}
+ sdata = rtnl_dereference(local->monitor_sdata);
+ if (sdata)
+ drv_remove_interface(local, sdata);
+
/* stop hardware - this must stop RX */
if (local->open_count)
ieee80211_stop_device(local);
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index fbb1efdc4d0..6e4fd32c661 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -17,6 +17,7 @@
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "sta_info.h"
+#include "driver-ops.h"
struct rate_control_ref {
struct ieee80211_local *local;
@@ -63,8 +64,7 @@ static inline void rate_control_rate_init(struct sta_info *sta)
static inline void rate_control_rate_update(struct ieee80211_local *local,
struct ieee80211_supported_band *sband,
- struct sta_info *sta, u32 changed,
- enum nl80211_channel_type oper_chan_type)
+ struct sta_info *sta, u32 changed)
{
struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
@@ -72,7 +72,8 @@ static inline void rate_control_rate_update(struct ieee80211_local *local,
if (ref && ref->ops->rate_update)
ref->ops->rate_update(ref->priv, sband, ista,
- priv_sta, changed, oper_chan_type);
+ priv_sta, changed);
+ drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
}
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 16e0b277b9a..3b3dcae13bb 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -686,8 +686,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
static void
minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- enum nl80211_channel_type oper_chan_type)
+ struct ieee80211_sta *sta, void *priv_sta)
{
struct minstrel_priv *mp = priv;
struct minstrel_ht_sta_priv *msp = priv_sta;
@@ -735,10 +734,6 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
- if (oper_chan_type != NL80211_CHAN_HT40MINUS &&
- oper_chan_type != NL80211_CHAN_HT40PLUS)
- sta_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-
smps = (sta_cap & IEEE80211_HT_CAP_SM_PS) >>
IEEE80211_HT_CAP_SM_PS_SHIFT;
@@ -788,17 +783,15 @@ static void
minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta)
{
- struct minstrel_priv *mp = priv;
-
- minstrel_ht_update_caps(priv, sband, sta, priv_sta, mp->hw->conf.channel_type);
+ minstrel_ht_update_caps(priv, sband, sta, priv_sta);
}
static void
minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
- u32 changed, enum nl80211_channel_type oper_chan_type)
+ u32 changed)
{
- minstrel_ht_update_caps(priv, sband, sta, priv_sta, oper_chan_type);
+ minstrel_ht_update_caps(priv, sband, sta, priv_sta);
}
static void *
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index bcfe8c77c83..54a049123a6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -793,8 +793,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
/* reset session timer */
if (tid_agg_rx->timeout)
- mod_timer(&tid_agg_rx->session_timer,
- TU_TO_EXP_TIME(tid_agg_rx->timeout));
+ tid_agg_rx->last_rx = jiffies;
/* if this mpdu is fragmented - terminate rx aggregation session */
sc = le16_to_cpu(hdr->seq_ctrl);
@@ -2269,11 +2268,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
sband = rx->local->hw.wiphy->bands[status->band];
- rate_control_rate_update(
- local, sband, rx->sta,
- IEEE80211_RC_SMPS_CHANGED,
- ieee80211_get_tx_channel_type(
- local, local->_oper_channel_type));
+ rate_control_rate_update(local, sband, rx->sta,
+ IEEE80211_RC_SMPS_CHANGED);
goto handled;
}
default:
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 38137cb5f6f..7fd7ac48f89 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1195,13 +1195,15 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
ieee80211_is_qos_nullfunc(hdr->frame_control))
qoshdr = ieee80211_get_qos_ctl(hdr);
- /* set EOSP for the frame */
- if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
- qoshdr && skb_queue_empty(&frames))
- *qoshdr |= IEEE80211_QOS_CTL_EOSP;
-
- info->flags |= IEEE80211_TX_STATUS_EOSP |
- IEEE80211_TX_CTL_REQ_TX_STATUS;
+ /* end service period after last frame */
+ if (skb_queue_empty(&frames)) {
+ if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
+ qoshdr)
+ *qoshdr |= IEEE80211_QOS_CTL_EOSP;
+
+ info->flags |= IEEE80211_TX_STATUS_EOSP |
+ IEEE80211_TX_CTL_REQ_TX_STATUS;
+ }
if (qoshdr)
tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index ab0576827ba..f75f5d9ac06 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -55,6 +55,7 @@
* @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame.
* @WLAN_STA_INSERTED: This station is inserted into the hash table.
* @WLAN_STA_RATE_CONTROL: rate control was initialized for this station.
+ * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH,
@@ -76,6 +77,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_4ADDR_EVENT,
WLAN_STA_INSERTED,
WLAN_STA_RATE_CONTROL,
+ WLAN_STA_TOFFSET_KNOWN,
};
#define STA_TID_NUM 16
@@ -101,6 +103,7 @@ enum ieee80211_sta_info_flags {
* @dialog_token: dialog token for aggregation session
* @timeout: session timeout value to be filled in ADDBA requests
* @state: session state (see above)
+ * @last_tx: jiffies of last tx activity
* @stop_initiator: initiator of a session stop
* @tx_stop: TX DelBA frame when stopping
* @buf_size: reorder buffer size at receiver
@@ -122,6 +125,7 @@ struct tid_ampdu_tx {
struct timer_list addba_resp_timer;
struct sk_buff_head pending;
unsigned long state;
+ unsigned long last_tx;
u16 timeout;
u8 dialog_token;
u8 stop_initiator;
@@ -139,6 +143,7 @@ struct tid_ampdu_tx {
* @reorder_time: jiffies when skb was added
* @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
* @reorder_timer: releases expired frames from the reorder buffer.
+ * @last_rx: jiffies of last rx activity
* @head_seq_num: head sequence number in reordering buffer.
* @stored_mpdu_num: number of MPDUs in reordering buffer
* @ssn: Starting Sequence Number expected to be aggregated.
@@ -163,6 +168,7 @@ struct tid_ampdu_rx {
unsigned long *reorder_time;
struct timer_list session_timer;
struct timer_list reorder_timer;
+ unsigned long last_rx;
u16 head_seq_num;
u16 stored_mpdu_num;
u16 ssn;
@@ -264,6 +270,7 @@ struct sta_ampdu_mlme {
* @plink_timeout: timeout of peer link
* @plink_timer: peer link watch timer
* @plink_timer_was_running: used by suspend/resume to restore timers
+ * @t_offset: timing offset relative to this host
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver
@@ -353,6 +360,8 @@ struct sta_info {
enum nl80211_plink_state plink_state;
u32 plink_timeout;
struct timer_list plink_timer;
+ s64 t_offset;
+ s64 t_offset_setpoint;
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -365,6 +374,8 @@ struct sta_info {
unsigned int lost_packets;
unsigned int beacon_loss_count;
+ bool supports_40mhz;
+
/* keep last! */
struct ieee80211_sta sta;
};
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 782a60198df..4f6aac16ac3 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -230,9 +230,9 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
* changed via debugfs, user needs to reassociate manually to have
* everything in sync.
*/
- if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
- && (ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
- && skb_get_queue_mapping(tx->skb) == 0)
+ if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) &&
+ (ifmgd->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) &&
+ skb_get_queue_mapping(tx->skb) == IEEE80211_AC_VO)
return TX_CONTINUE;
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
@@ -400,6 +400,8 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
+ if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
+ info->hw_queue = tx->sdata->vif.cab_queue;
/* device releases frame after DTIM beacon */
if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING))
@@ -1118,8 +1120,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
/* reset session timer */
if (reset_agg_timer && tid_tx->timeout)
- mod_timer(&tid_tx->session_timer,
- TU_TO_EXP_TIME(tid_tx->timeout));
+ tid_tx->last_tx = jiffies;
return queued;
}
@@ -1215,11 +1216,19 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
bool txpending)
{
struct sk_buff *skb, *tmp;
- struct ieee80211_tx_info *info;
unsigned long flags;
skb_queue_walk_safe(skbs, skb, tmp) {
- int q = skb_get_queue_mapping(skb);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int q = info->hw_queue;
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ if (WARN_ON_ONCE(q >= local->hw.queues)) {
+ __skb_unlink(skb, skbs);
+ dev_kfree_skb(skb);
+ continue;
+ }
+#endif
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
if (local->queue_stop_reasons[q] ||
@@ -1241,7 +1250,6 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
- info = IEEE80211_SKB_CB(skb);
info->control.vif = vif;
info->control.sta = sta;
@@ -1284,8 +1292,16 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR:
- sdata = NULL;
- vif = NULL;
+ sdata = rcu_dereference(local->monitor_sdata);
+ if (sdata) {
+ vif = &sdata->vif;
+ info->hw_queue =
+ vif->hw_queue[skb_get_queue_mapping(skb)];
+ } else if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) {
+ dev_kfree_skb(skb);
+ return true;
+ } else
+ vif = NULL;
break;
case NL80211_IFTYPE_AP_VLAN:
sdata = container_of(sdata->bss,
@@ -1400,6 +1416,12 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
tx.channel = local->hw.conf.channel;
info->band = tx.channel->band;
+ /* set up hw_queue value early */
+ if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
+ !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
+ info->hw_queue =
+ sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
+
if (!invoke_tx_handlers(&tx))
result = __ieee80211_tx(local, &tx.skbs, led_len,
tx.sta, txpending);
@@ -1468,12 +1490,12 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
if (ieee80211_vif_is_mesh(&sdata->vif) &&
ieee80211_is_data(hdr->frame_control) &&
- !is_multicast_ether_addr(hdr->addr1))
- if (mesh_nexthop_resolve(skb, sdata)) {
- /* skb queued: don't free */
- rcu_read_unlock();
- return;
- }
+ !is_multicast_ether_addr(hdr->addr1) &&
+ mesh_nexthop_resolve(skb, sdata)) {
+ /* skb queued: don't free */
+ rcu_read_unlock();
+ return;
+ }
ieee80211_set_qos_hdr(sdata, skb);
ieee80211_tx(sdata, skb, false);
@@ -1929,7 +1951,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
wme_sta = true;
/* receiver and we are QoS enabled, use a QoS type frame */
- if (wme_sta && local->hw.queues >= 4) {
+ if (wme_sta && local->hw.queues >= IEEE80211_NUM_ACS) {
fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
hdrlen += 2;
}
@@ -2170,7 +2192,6 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
void ieee80211_tx_pending(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *)data;
- struct ieee80211_sub_if_data *sdata;
unsigned long flags;
int i;
bool txok;
@@ -2207,8 +2228,7 @@ void ieee80211_tx_pending(unsigned long data)
}
if (skb_queue_empty(&local->pending[i]))
- list_for_each_entry_rcu(sdata, &local->interfaces, list)
- netif_wake_subqueue(sdata->dev, i);
+ ieee80211_propagate_queue_wake(local, i);
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -2374,6 +2394,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
IEEE80211_STYPE_BEACON);
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
struct ieee80211_mgmt *mgmt;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u8 *pos;
int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
sizeof(mgmt->u.beacon);
@@ -2383,6 +2404,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
goto out;
#endif
+ if (ifmsh->sync_ops)
+ ifmsh->sync_ops->adjust_tbtt(
+ sdata);
+
skb = dev_alloc_skb(local->tx_headroom +
hdr_len +
2 + /* NULL SSID */
@@ -2390,7 +2415,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2 + 3 + /* DS params */
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
2 + sizeof(struct ieee80211_ht_cap) +
- 2 + sizeof(struct ieee80211_ht_info) +
+ 2 + sizeof(struct ieee80211_ht_operation) +
2 + sdata->u.mesh.mesh_id_len +
2 + sizeof(struct ieee80211_meshconf_ie) +
sdata->u.mesh.ie_len);
@@ -2414,12 +2439,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*pos++ = WLAN_EID_SSID;
*pos++ = 0x0;
- if (ieee80211_add_srates_ie(&sdata->vif, skb) ||
+ if (ieee80211_add_srates_ie(&sdata->vif, skb, true) ||
mesh_add_ds_params_ie(skb, sdata) ||
- ieee80211_add_ext_srates_ie(&sdata->vif, skb) ||
+ ieee80211_add_ext_srates_ie(&sdata->vif, skb, true) ||
mesh_add_rsn_ie(skb, sdata) ||
mesh_add_ht_cap_ie(skb, sdata) ||
- mesh_add_ht_info_ie(skb, sdata) ||
+ mesh_add_ht_oper_ie(skb, sdata) ||
mesh_add_meshid_ie(skb, sdata) ||
mesh_add_meshconf_ie(skb, sdata) ||
mesh_add_vendor_ies(skb, sdata)) {
@@ -2603,7 +2628,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
pos = skb_put(skb, ie_ssid_len);
*pos++ = WLAN_EID_SSID;
*pos++ = ssid_len;
- if (ssid)
+ if (ssid_len)
memcpy(pos, ssid, ssid_len);
pos += ssid_len;
@@ -2710,11 +2735,13 @@ EXPORT_SYMBOL(ieee80211_get_buffered_bc);
void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid)
{
+ int ac = ieee802_1d_to_ac[tid];
+
skb_set_mac_header(skb, 0);
skb_set_network_header(skb, 0);
skb_set_transport_header(skb, 0);
- skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]);
+ skb_set_queue_mapping(skb, ac);
skb->priority = tid;
/*
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 32f7a3b3d43..e67fe5c1def 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -265,17 +265,45 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ctstoself_duration);
+void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ int ac;
+
+ if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
+ continue;
+
+ if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
+ local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
+ continue;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ int ac_queue = sdata->vif.hw_queue[ac];
+
+ if (ac_queue == queue ||
+ (sdata->vif.cab_queue == queue &&
+ local->queue_stop_reasons[ac_queue] == 0 &&
+ skb_queue_empty(&local->pending[ac_queue])))
+ netif_wake_subqueue(sdata->dev, ac);
+ }
+ }
+}
+
static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_sub_if_data *sdata;
trace_wake_queue(local, queue, reason);
if (WARN_ON(queue >= hw->queues))
return;
+ if (!test_bit(reason, &local->queue_stop_reasons[queue]))
+ return;
+
__clear_bit(reason, &local->queue_stop_reasons[queue]);
if (local->queue_stop_reasons[queue] != 0)
@@ -284,11 +312,7 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
if (skb_queue_empty(&local->pending[queue])) {
rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
- continue;
- netif_wake_subqueue(sdata->dev, queue);
- }
+ ieee80211_propagate_queue_wake(local, queue);
rcu_read_unlock();
} else
tasklet_schedule(&local->tx_pending_tasklet);
@@ -323,11 +347,21 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
if (WARN_ON(queue >= hw->queues))
return;
+ if (test_bit(reason, &local->queue_stop_reasons[queue]))
+ return;
+
__set_bit(reason, &local->queue_stop_reasons[queue]);
rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list)
- netif_stop_subqueue(sdata->dev, queue);
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ int ac;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ if (sdata->vif.hw_queue[ac] == queue ||
+ sdata->vif.cab_queue == queue)
+ netif_stop_subqueue(sdata->dev, ac);
+ }
+ }
rcu_read_unlock();
}
@@ -354,8 +388,8 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
{
struct ieee80211_hw *hw = &local->hw;
unsigned long flags;
- int queue = skb_get_queue_mapping(skb);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int queue = info->hw_queue;
if (WARN_ON(!info->control.vif)) {
kfree_skb(skb);
@@ -379,10 +413,6 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
int queue, i;
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- for (i = 0; i < hw->queues; i++)
- __ieee80211_stop_queue(hw, i,
- IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
-
while ((skb = skb_dequeue(skbs))) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -391,7 +421,11 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
continue;
}
- queue = skb_get_queue_mapping(skb);
+ queue = info->hw_queue;
+
+ __ieee80211_stop_queue(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+
__skb_queue_tail(&local->pending[queue], skb);
}
@@ -404,12 +438,6 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}
-void ieee80211_add_pending_skbs(struct ieee80211_local *local,
- struct sk_buff_head *skbs)
-{
- ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL);
-}
-
void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason)
{
@@ -684,9 +712,9 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
else
elem_parse_failed = true;
break;
- case WLAN_EID_HT_INFORMATION:
- if (elen >= sizeof(struct ieee80211_ht_info))
- elems->ht_info_elem = (void *)pos;
+ case WLAN_EID_HT_OPERATION:
+ if (elen >= sizeof(struct ieee80211_ht_operation))
+ elems->ht_operation = (void *)pos;
else
elem_parse_failed = true;
break;
@@ -775,19 +803,22 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_queue_params qparam;
- int queue;
+ int ac;
bool use_11b;
int aCWmin, aCWmax;
if (!local->ops->conf_tx)
return;
+ if (local->hw.queues < IEEE80211_NUM_ACS)
+ return;
+
memset(&qparam, 0, sizeof(qparam));
use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) &&
!(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
- for (queue = 0; queue < local->hw.queues; queue++) {
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
/* Set defaults according to 802.11-2007 Table 7-37 */
aCWmax = 1023;
if (use_11b)
@@ -795,21 +826,21 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
else
aCWmin = 15;
- switch (queue) {
- case 3: /* AC_BK */
+ switch (ac) {
+ case IEEE80211_AC_BK:
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
qparam.aifs = 7;
break;
default: /* never happens but let's not leave undefined */
- case 2: /* AC_BE */
+ case IEEE80211_AC_BE:
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
qparam.aifs = 3;
break;
- case 1: /* AC_VI */
+ case IEEE80211_AC_VI:
qparam.cw_max = aCWmin;
qparam.cw_min = (aCWmin + 1) / 2 - 1;
if (use_11b)
@@ -818,7 +849,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
qparam.txop = 3008/32;
qparam.aifs = 2;
break;
- case 0: /* AC_VO */
+ case IEEE80211_AC_VO:
qparam.cw_max = (aCWmin + 1) / 2 - 1;
qparam.cw_min = (aCWmin + 1) / 4 - 1;
if (use_11b)
@@ -831,8 +862,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
qparam.uapsd = false;
- sdata->tx_conf[queue] = qparam;
- drv_conf_tx(local, sdata, queue, &qparam);
+ sdata->tx_conf[ac] = qparam;
+ drv_conf_tx(local, sdata, ac, &qparam);
}
/* after reinitialize QoS TX queues setting to default,
@@ -1106,7 +1137,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
- enum ieee80211_band band)
+ enum ieee80211_band band, u32 *basic_rates)
{
struct ieee80211_supported_band *sband;
struct ieee80211_rate *bitrates;
@@ -1127,15 +1158,25 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
elems->ext_supp_rates_len; i++) {
u8 rate = 0;
int own_rate;
+ bool is_basic;
if (i < elems->supp_rates_len)
rate = elems->supp_rates[i];
else if (elems->ext_supp_rates)
rate = elems->ext_supp_rates
[i - elems->supp_rates_len];
own_rate = 5 * (rate & 0x7f);
- for (j = 0; j < num_rates; j++)
- if (bitrates[j].bitrate == own_rate)
+ is_basic = !!(rate & 0x80);
+
+ if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
+ continue;
+
+ for (j = 0; j < num_rates; j++) {
+ if (bitrates[j].bitrate == own_rate) {
supp_rates |= BIT(j);
+ if (basic_rates && is_basic)
+ *basic_rates |= BIT(j);
+ }
+ }
}
return supp_rates;
}
@@ -1210,6 +1251,16 @@ int ieee80211_reconfig(struct ieee80211_local *local)
IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
/* add interfaces */
+ sdata = rtnl_dereference(local->monitor_sdata);
+ if (sdata) {
+ res = drv_add_interface(local, sdata);
+ if (WARN_ON(res)) {
+ rcu_assign_pointer(local->monitor_sdata, NULL);
+ synchronize_net();
+ kfree(sdata);
+ }
+ }
+
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
@@ -1232,14 +1283,17 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mutex_unlock(&local->sta_mtx);
/* reconfigure tx conf */
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_MONITOR ||
- !ieee80211_sdata_running(sdata))
- continue;
+ if (hw->queues >= IEEE80211_NUM_ACS) {
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_MONITOR ||
+ !ieee80211_sdata_running(sdata))
+ continue;
- for (i = 0; i < hw->queues; i++)
- drv_conf_tx(local, sdata, i, &sdata->tx_conf[i]);
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ drv_conf_tx(local, sdata, i,
+ &sdata->tx_conf[i]);
+ }
}
/* reconfigure hardware */
@@ -1611,57 +1665,56 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
return pos;
}
-u8 *ieee80211_ie_build_ht_info(u8 *pos,
- struct ieee80211_sta_ht_cap *ht_cap,
+u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
struct ieee80211_channel *channel,
enum nl80211_channel_type channel_type)
{
- struct ieee80211_ht_info *ht_info;
+ struct ieee80211_ht_operation *ht_oper;
/* Build HT Information */
- *pos++ = WLAN_EID_HT_INFORMATION;
- *pos++ = sizeof(struct ieee80211_ht_info);
- ht_info = (struct ieee80211_ht_info *)pos;
- ht_info->control_chan =
+ *pos++ = WLAN_EID_HT_OPERATION;
+ *pos++ = sizeof(struct ieee80211_ht_operation);
+ ht_oper = (struct ieee80211_ht_operation *)pos;
+ ht_oper->primary_chan =
ieee80211_frequency_to_channel(channel->center_freq);
switch (channel_type) {
case NL80211_CHAN_HT40MINUS:
- ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
break;
case NL80211_CHAN_HT40PLUS:
- ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
break;
case NL80211_CHAN_HT20:
default:
- ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
break;
}
if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
- ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+ ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
/*
* Note: According to 802.11n-2009 9.13.3.1, HT Protection field and
* RIFS Mode are reserved in IBSS mode, therefore keep them at 0
*/
- ht_info->operation_mode = 0x0000;
- ht_info->stbc_param = 0x0000;
+ ht_oper->operation_mode = 0x0000;
+ ht_oper->stbc_param = 0x0000;
/* It seems that Basic MCS set and Supported MCS set
are identical for the first 10 bytes */
- memset(&ht_info->basic_set, 0, 16);
- memcpy(&ht_info->basic_set, &ht_cap->mcs, 10);
+ memset(&ht_oper->basic_set, 0, 16);
+ memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
- return pos + sizeof(struct ieee80211_ht_info);
+ return pos + sizeof(struct ieee80211_ht_operation);
}
enum nl80211_channel_type
-ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info)
+ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper)
{
enum nl80211_channel_type channel_type;
- if (!ht_info)
+ if (!ht_oper)
return NL80211_CHAN_NO_HT;
- switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
channel_type = NL80211_CHAN_HT20;
break;
@@ -1678,13 +1731,15 @@ ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info)
return channel_type;
}
-int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb)
+int ieee80211_add_srates_ie(struct ieee80211_vif *vif,
+ struct sk_buff *skb, bool need_basic)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
int rate;
u8 i, rates, *pos;
+ u32 basic_rates = vif->bss_conf.basic_rates;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
rates = sband->n_bitrates;
@@ -1698,20 +1753,25 @@ int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb)
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = rates;
for (i = 0; i < rates; i++) {
+ u8 basic = 0;
+ if (need_basic && basic_rates & BIT(i))
+ basic = 0x80;
rate = sband->bitrates[i].bitrate;
- *pos++ = (u8) (rate / 5);
+ *pos++ = basic | (u8) (rate / 5);
}
return 0;
}
-int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb)
+int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif,
+ struct sk_buff *skb, bool need_basic)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
int rate;
u8 i, exrates, *pos;
+ u32 basic_rates = vif->bss_conf.basic_rates;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
exrates = sband->n_bitrates;
@@ -1728,8 +1788,11 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb)
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = exrates;
for (i = 8; i < sband->n_bitrates; i++) {
+ u8 basic = 0;
+ if (need_basic && basic_rates & BIT(i))
+ basic = 0x80;
rate = sband->bitrates[i].bitrate;
- *pos++ = (u8) (rate / 5);
+ *pos++ = basic | (u8) (rate / 5);
}
}
return 0;
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 89511be3111..c3d643a6536 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -52,6 +52,26 @@ static int wme_downgrade_ac(struct sk_buff *skb)
}
}
+static u16 ieee80211_downgrade_queue(struct ieee80211_local *local,
+ struct sk_buff *skb)
+{
+ /* in case we are a client verify acm is not set for this ac */
+ while (unlikely(local->wmm_acm & BIT(skb->priority))) {
+ if (wme_downgrade_ac(skb)) {
+ /*
+ * This should not really happen. The AP has marked all
+ * lower ACs to require admission control which is not
+ * a reasonable configuration. Allow the frame to be
+ * transmitted using AC_BK as a workaround.
+ */
+ break;
+ }
+ }
+
+ /* look up which queue to use for frames with this 1d tag */
+ return ieee802_1d_to_ac[skb->priority];
+}
+
/* Indicate which queue to use for this fully formed 802.11 frame */
u16 ieee80211_select_queue_80211(struct ieee80211_local *local,
struct sk_buff *skb,
@@ -59,7 +79,7 @@ u16 ieee80211_select_queue_80211(struct ieee80211_local *local,
{
u8 *p;
- if (local->hw.queues < 4)
+ if (local->hw.queues < IEEE80211_NUM_ACS)
return 0;
if (!ieee80211_is_data(hdr->frame_control)) {
@@ -86,9 +106,9 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
const u8 *ra = NULL;
bool qos = false;
- if (local->hw.queues < 4 || skb->len < 6) {
+ if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
skb->priority = 0; /* required for correct WPA/11i MIC */
- return min_t(u16, local->hw.queues - 1, IEEE80211_AC_BE);
+ return 0;
}
rcu_read_lock();
@@ -139,26 +159,6 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
return ieee80211_downgrade_queue(local, skb);
}
-u16 ieee80211_downgrade_queue(struct ieee80211_local *local,
- struct sk_buff *skb)
-{
- /* in case we are a client verify acm is not set for this ac */
- while (unlikely(local->wmm_acm & BIT(skb->priority))) {
- if (wme_downgrade_ac(skb)) {
- /*
- * This should not really happen. The AP has marked all
- * lower ACs to require admission control which is not
- * a reasonable configuration. Allow the frame to be
- * transmitted using AC_BK as a workaround.
- */
- break;
- }
- }
-
- /* look up which queue to use for frames with this 1d tag */
- return ieee802_1d_to_ac[skb->priority];
-}
-
void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index 94edceb617f..ca80818b7b6 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -22,8 +22,5 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
-u16 ieee80211_downgrade_queue(struct ieee80211_local *local,
- struct sk_buff *skb);
-
#endif /* _WME_H */
diff --git a/net/wireless/core.c b/net/wireless/core.c
index ccdfed89765..59f4a7e7c09 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -708,6 +708,10 @@ void wiphy_unregister(struct wiphy *wiphy)
flush_work(&rdev->scan_done_wk);
cancel_work_sync(&rdev->conn_work);
flush_work(&rdev->event_work);
+
+ if (rdev->wowlan && rdev->ops->set_wakeup)
+ rdev->ops->set_wakeup(&rdev->wiphy, false);
+ cfg80211_rdev_free_wowlan(rdev);
}
EXPORT_SYMBOL(wiphy_unregister);
@@ -720,7 +724,6 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
mutex_destroy(&rdev->sched_scan_mtx);
list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
cfg80211_put_bss(&scan->pub);
- cfg80211_rdev_free_wowlan(rdev);
kfree(rdev);
}
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index ba21ab22187..8c747fa9319 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -38,6 +38,7 @@
#define MESH_MAX_PREQ_RETRIES 4
+#define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50
const struct mesh_config default_mesh_config = {
.dot11MeshRetryTimeout = MESH_RET_T,
@@ -48,6 +49,7 @@ const struct mesh_config default_mesh_config = {
.element_ttl = MESH_DEFAULT_ELEMENT_TTL,
.auto_open_plinks = true,
.dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS,
+ .dot11MeshNbrOffsetMaxNeighbor = MESH_SYNC_NEIGHBOR_OFFSET_MAX,
.dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT,
.dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT,
.dot11MeshHWMPperrMinInterval = MESH_PERR_MIN_INT,
@@ -62,6 +64,7 @@ const struct mesh_config default_mesh_config = {
};
const struct mesh_setup default_mesh_setup = {
+ .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
.path_metric = IEEE80211_PATH_METRIC_AIRTIME,
.ie = NULL,
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index f5a7ac3a093..6801d96bc22 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -6,6 +6,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/nl80211.h>
#include <linux/slab.h>
@@ -100,7 +101,7 @@ void __cfg80211_send_deauth(struct net_device *dev,
ASSERT_WDEV_LOCK(wdev);
if (wdev->current_bss &&
- memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
+ compare_ether_addr(wdev->current_bss->pub.bssid, bssid) == 0) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
wdev->current_bss = NULL;
@@ -115,7 +116,7 @@ void __cfg80211_send_deauth(struct net_device *dev,
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
- from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
+ from_ap = compare_ether_addr(mgmt->sa, dev->dev_addr) != 0;
__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
} else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
@@ -154,7 +155,7 @@ void __cfg80211_send_disassoc(struct net_device *dev,
return;
if (wdev->current_bss &&
- memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
+ compare_ether_addr(wdev->current_bss->pub.bssid, bssid) == 0) {
cfg80211_sme_disassoc(dev, wdev->current_bss);
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
@@ -165,7 +166,7 @@ void __cfg80211_send_disassoc(struct net_device *dev,
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
- from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
+ from_ap = compare_ether_addr(mgmt->sa, dev->dev_addr) != 0;
__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
}
EXPORT_SYMBOL(__cfg80211_send_disassoc);
@@ -285,7 +286,7 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
return -EINVAL;
if (wdev->current_bss &&
- memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
+ compare_ether_addr(bssid, wdev->current_bss->pub.bssid) == 0)
return -EALREADY;
memset(&req, 0, sizeof(req));
@@ -362,7 +363,7 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
memset(&req, 0, sizeof(req));
if (wdev->current_bss && prev_bssid &&
- memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) {
+ compare_ether_addr(wdev->current_bss->pub.bssid, prev_bssid) == 0) {
/*
* Trying to reassociate: Allow this to proceed and let the old
* association to be dropped when the new one is completed.
@@ -446,7 +447,8 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
if (local_state_change) {
if (wdev->current_bss &&
- memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
+ compare_ether_addr(wdev->current_bss->pub.bssid, bssid)
+ == 0) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
wdev->current_bss = NULL;
@@ -495,7 +497,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
req.local_state_change = local_state_change;
req.ie = ie;
req.ie_len = ie_len;
- if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
+ if (compare_ether_addr(wdev->current_bss->pub.bssid, bssid) == 0)
req.bss = &wdev->current_bss->pub;
else
return -ENOTCONN;
@@ -758,8 +760,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
break;
}
- if (memcmp(wdev->current_bss->pub.bssid,
- mgmt->bssid, ETH_ALEN)) {
+ if (compare_ether_addr(wdev->current_bss->pub.bssid,
+ mgmt->bssid)) {
err = -ENOTCONN;
break;
}
@@ -772,8 +774,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
break;
/* for station, check that DA is the AP */
- if (memcmp(wdev->current_bss->pub.bssid,
- mgmt->da, ETH_ALEN)) {
+ if (compare_ether_addr(wdev->current_bss->pub.bssid,
+ mgmt->da)) {
err = -ENOTCONN;
break;
}
@@ -781,11 +783,11 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_AP_VLAN:
- if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN))
+ if (compare_ether_addr(mgmt->bssid, dev->dev_addr))
err = -EINVAL;
break;
case NL80211_IFTYPE_MESH_POINT:
- if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) {
+ if (compare_ether_addr(mgmt->sa, mgmt->bssid)) {
err = -EINVAL;
break;
}
@@ -804,7 +806,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
return err;
}
- if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0)
+ if (compare_ether_addr(mgmt->sa, dev->dev_addr) != 0)
return -EINVAL;
/* Transmit the Action frame as requested by user space */
@@ -928,6 +930,33 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
+ enum nl80211_channel_type type)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ struct ieee80211_channel *chan;
+
+ wdev_lock(wdev);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO))
+ goto out;
+
+ chan = rdev_freq_to_chan(rdev, freq, type);
+ if (WARN_ON(!chan))
+ goto out;
+
+ wdev->channel = chan;
+
+ nl80211_ch_switch_notify(rdev, dev, freq, type, 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)
{
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 65622e9c7c5..ff1a6c7fbe3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1142,17 +1142,20 @@ static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
static int parse_txq_params(struct nlattr *tb[],
struct ieee80211_txq_params *txq_params)
{
- if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
+ if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
!tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
!tb[NL80211_TXQ_ATTR_AIFS])
return -EINVAL;
- txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
+ txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
+ if (txq_params->ac >= NL80211_NUM_ACS)
+ return -EINVAL;
+
return 0;
}
@@ -2545,6 +2548,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
sizeof(struct nl80211_sta_flag_update),
&sinfo->sta_flags))
goto nla_put_failure;
+ if ((sinfo->filled & STATION_INFO_T_OFFSET) &&
+ nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET,
+ sinfo->t_offset))
+ goto nla_put_failure;
nla_nest_end(msg, sinfoattr);
if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) &&
@@ -3345,6 +3352,8 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
cur_params.element_ttl) ||
nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
cur_params.auto_open_plinks) ||
+ nla_put_u32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+ cur_params.dot11MeshNbrOffsetMaxNeighbor) ||
nla_put_u8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
cur_params.dot11MeshHWMPmaxPREQretries) ||
nla_put_u32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
@@ -3390,6 +3399,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
[NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
[NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
[NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 },
[NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
[NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
@@ -3407,6 +3417,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
static const struct nla_policy
nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
+ [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
@@ -3459,6 +3470,9 @@ do {\
mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8);
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor,
+ mask, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+ nla_get_u32);
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
nla_get_u8);
@@ -3516,6 +3530,12 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
nl80211_mesh_setup_params_policy))
return -EINVAL;
+ if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])
+ setup->sync_method =
+ (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ?
+ IEEE80211_SYNC_METHOD_VENDOR :
+ IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET;
+
if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
setup->path_sel_proto =
(nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
@@ -6082,6 +6102,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_wowlan new_triggers = {};
struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
int err, i;
+ bool prev_enabled = rdev->wowlan;
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
return -EOPNOTSUPP;
@@ -6214,6 +6235,9 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
rdev->wowlan = NULL;
}
+ if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
+ rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan);
+
return 0;
error:
for (i = 0; i < new_triggers.n_patterns; i++)
@@ -8017,6 +8041,39 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int freq,
+ enum nl80211_channel_type type, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 4ffe50df9f3..01a1122c3b3 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -118,6 +118,10 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, int index,
const u8 *bssid, bool preauth, gfp_t gfp);
+void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int freq,
+ enum nl80211_channel_type type, gfp_t gfp);
+
bool nl80211_unexpected_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp);
bool nl80211_unexpected_4addr_frame(struct net_device *dev,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index e9a0ac83b84..15f347477a9 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -388,7 +388,15 @@ static void reg_regdb_query(const char *alpha2)
schedule_work(&reg_regdb_work);
}
+
+/* Feel free to add any other sanity checks here */
+static void reg_regdb_size_check(void)
+{
+ /* We should ideally BUILD_BUG_ON() but then random builds would fail */
+ WARN_ONCE(!reg_regdb_size, "db.txt is empty, you should update it...");
+}
#else
+static inline void reg_regdb_size_check(void) {}
static inline void reg_regdb_query(const char *alpha2) {}
#endif /* CONFIG_CFG80211_INTERNAL_REGDB */
@@ -2322,6 +2330,8 @@ int __init regulatory_init(void)
spin_lock_init(&reg_requests_lock);
spin_lock_init(&reg_pending_beacons_lock);
+ reg_regdb_size_check();
+
cfg80211_regdomain = cfg80211_world_regdom;
user_alpha2[0] = '9';
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 70faadf16a3..fdbcfe692a3 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -378,7 +378,7 @@ static int cmp_bss_core(struct cfg80211_bss *a,
b->len_information_elements);
}
- return memcmp(a->bssid, b->bssid, ETH_ALEN);
+ return compare_ether_addr(a->bssid, b->bssid);
}
static int cmp_bss(struct cfg80211_bss *a,