diff options
author | David S. Miller <davem@davemloft.net> | 2012-04-12 20:12:31 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-04-12 20:12:31 -0400 |
commit | 816a7854d56f19aeba3c83e1ed8c2c0ef275365a (patch) | |
tree | c3b82ad98006fc5fa7b408c142e75a6053195dfa | |
parent | 011e3c63251be832d23df9f0697626ab7b354d02 (diff) | |
parent | 7eab0f64a9eba5405222fdef0ede2468bf495efd (diff) | |
download | linux-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
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(¬if_wait->notif_waits)) { struct iwl_notification_wait *w; spin_lock(¬if_wait->notif_wait_lock); list_for_each_entry(w, ¬if_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(¬if_wait->notif_wait_lock); - wake_up_all(¬if_wait->notif_waitq); } + + if (triggered) + wake_up_all(¬if_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(®_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(®_requests_lock); spin_lock_init(®_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, |