diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k')
27 files changed, 2017 insertions, 376 deletions
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 8fcd586d1c39..6b4020a57984 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -5,7 +5,8 @@ ath9k-y += beacon.o \ recv.o \ xmit.o \ link.o \ - antenna.o + antenna.o \ + channel.o ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o ath9k-$(CONFIG_ATH9K_PCI) += pci.o diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index be3eb2a8d602..4173838f4684 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -113,6 +113,7 @@ static int ath_ahb_probe(struct platform_device *pdev) irq = res->start; + ath9k_fill_chanctx_ops(); hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (hw == NULL) { dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index 741b38ddcb37..59af9f9712da 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -281,7 +281,7 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen) | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) - | SM(i->txpower, AR_XmitPower) + | SM(i->txpower, AR_XmitPower0) | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) | (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0) | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0) @@ -306,6 +306,10 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) | set11nRateFlags(i->rates, 2) | set11nRateFlags(i->rates, 3) | SM(i->rtscts_rate, AR_RTSCTSRate); + + ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1); + ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2); + ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3); } static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 235053ba7737..80c6eacbda53 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3535,7 +3535,8 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz) { int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl; - if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah)) + if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah) || + AR_SREV_9531(ah)) REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias); else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah)) REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index ec1da0cc25f5..ddef9eedbac6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -314,10 +314,17 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) qca953x_1p0_mac_core); INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], qca953x_1p0_mac_postamble); - INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], - qca953x_1p0_baseband_core); - INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], - qca953x_1p0_baseband_postamble); + if (AR_SREV_9531_20(ah)) { + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + qca953x_2p0_baseband_core); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + qca953x_2p0_baseband_postamble); + } else { + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + qca953x_1p0_baseband_core); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + qca953x_1p0_baseband_postamble); + } INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], qca953x_1p0_radio_core); INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 729ffbf07343..71e38e85aa99 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -101,7 +101,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen) | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) - | SM(i->txpower, AR_XmitPower) + | SM(i->txpower, AR_XmitPower0) | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0) | (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0) @@ -151,6 +151,10 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) | SM(i->rtscts_rate, AR_RTSCTSRate); ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding; + + ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1); + ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2); + ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3); } static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 8927fc34d84c..542a8d51d3b0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1552,13 +1552,15 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, u8 *ini_reloaded) { unsigned int regWrites = 0; - u32 modesIndex; + u32 modesIndex, txgain_index; if (IS_CHAN_5GHZ(chan)) modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; else modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; + txgain_index = AR_SREV_9531(ah) ? 1 : modesIndex; + if (modesIndex == ah->modes_index) { *ini_reloaded = false; goto set_rfmode; @@ -1573,7 +1575,7 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant, modesIndex); - REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); + REG_WRITE_ARRAY(&ah->iniModesTxGain, txgain_index, regWrites); if (AR_SREV_9462_20_OR_LATER(ah)) { /* diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h index 8e5c3b9786e3..812a9d787bf3 100644 --- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h @@ -219,7 +219,7 @@ static const u32 qca953x_1p0_baseband_core[][2] = { {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, - {0x00009d10, 0x01884061}, + {0x00009d10, 0x018848c6}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, @@ -715,4 +715,203 @@ static const u32 qca953x_1p1_modes_no_xpa_tx_gain_table[][2] = { {0x00016448, 0x6c927a70}, }; +static const u32 qca953x_2p0_baseband_core[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafe68e30}, + {0x00009804, 0xfd14e000}, + {0x00009808, 0x9c0a9f6b}, + {0x0000980c, 0x04900000}, + {0x00009814, 0x0280c00a}, + {0x00009818, 0x00000000}, + {0x0000981c, 0x00020028}, + {0x00009834, 0x6400a190}, + {0x00009838, 0x0108ecff}, + {0x0000983c, 0x14000600}, + {0x00009880, 0x201fff00}, + {0x00009884, 0x00001042}, + {0x000098a4, 0x00200400}, + {0x000098b0, 0x32840bbe}, + {0x000098bc, 0x00000002}, + {0x000098d0, 0x004b6a8e}, + {0x000098d4, 0x00000820}, + {0x000098dc, 0x00000000}, + {0x000098f0, 0x00000000}, + {0x000098f4, 0x00000000}, + {0x00009c04, 0xff55ff55}, + {0x00009c08, 0x0320ff55}, + {0x00009c0c, 0x00000000}, + {0x00009c10, 0x00000000}, + {0x00009c14, 0x00046384}, + {0x00009c18, 0x05b6b440}, + {0x00009c1c, 0x00b6b440}, + {0x00009d00, 0xc080a333}, + {0x00009d04, 0x40206c10}, + {0x00009d08, 0x009c4060}, + {0x00009d0c, 0x9883800a}, + {0x00009d10, 0x018848c6}, + {0x00009d14, 0x00c0040b}, + {0x00009d18, 0x00000000}, + {0x00009e08, 0x0038230c}, + {0x00009e24, 0x990bb515}, + {0x00009e28, 0x0c6f0000}, + {0x00009e30, 0x06336f77}, + {0x00009e34, 0x6af6532f}, + {0x00009e38, 0x0cc80c00}, + {0x00009e40, 0x0d261820}, + {0x00009e4c, 0x00001004}, + {0x00009e50, 0x00ff03f1}, + {0x00009fc0, 0x813e4788}, + {0x00009fc4, 0x0001efb5}, + {0x00009fcc, 0x40000014}, + {0x00009fd0, 0x02993b93}, + {0x0000a20c, 0x00000000}, + {0x0000a220, 0x00000000}, + {0x0000a224, 0x00000000}, + {0x0000a228, 0x10002310}, + {0x0000a23c, 0x00000000}, + {0x0000a244, 0x0c000000}, + {0x0000a248, 0x00000140}, + {0x0000a2a0, 0x00000007}, + {0x0000a2c0, 0x00000007}, + {0x0000a2c8, 0x00000000}, + {0x0000a2d4, 0x00000000}, + {0x0000a2ec, 0x00000000}, + {0x0000a2f0, 0x00000000}, + {0x0000a2f4, 0x00000000}, + {0x0000a2f8, 0x00000000}, + {0x0000a344, 0x00000000}, + {0x0000a34c, 0x00000000}, + {0x0000a350, 0x0000a000}, + {0x0000a364, 0x00000000}, + {0x0000a370, 0x00000000}, + {0x0000a390, 0x00000001}, + {0x0000a394, 0x00000444}, + {0x0000a398, 0x001f0e0f}, + {0x0000a39c, 0x0075393f}, + {0x0000a3a0, 0xb79f6427}, + {0x0000a3a4, 0x000400ff}, + {0x0000a3a8, 0x6a6a6a6a}, + {0x0000a3ac, 0x6a6a6a6a}, + {0x0000a3b0, 0x00c8641a}, + {0x0000a3b4, 0x0000001a}, + {0x0000a3b8, 0x0088642a}, + {0x0000a3bc, 0x000001fa}, + {0x0000a3c0, 0x20202020}, + {0x0000a3c4, 0x22222220}, + {0x0000a3c8, 0x20200020}, + {0x0000a3cc, 0x20202020}, + {0x0000a3d0, 0x20202020}, + {0x0000a3d4, 0x20202020}, + {0x0000a3d8, 0x20202020}, + {0x0000a3dc, 0x20202020}, + {0x0000a3e0, 0x20202020}, + {0x0000a3e4, 0x20202020}, + {0x0000a3e8, 0x20202020}, + {0x0000a3ec, 0x20202020}, + {0x0000a3f0, 0x00000000}, + {0x0000a3f4, 0x00000000}, + {0x0000a3f8, 0x0c9bd380}, + {0x0000a3fc, 0x000f0f01}, + {0x0000a400, 0x8fa91f01}, + {0x0000a404, 0x00000000}, + {0x0000a408, 0x0e79e5c6}, + {0x0000a40c, 0x00820820}, + {0x0000a414, 0x1ce42108}, + {0x0000a418, 0x2d001dce}, + {0x0000a41c, 0x1ce73908}, + {0x0000a420, 0x000001ce}, + {0x0000a424, 0x1ce738e7}, + {0x0000a428, 0x000001ce}, + {0x0000a42c, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce}, + {0x0000a434, 0x00000000}, + {0x0000a438, 0x00001801}, + {0x0000a43c, 0x00100000}, + {0x0000a444, 0x00000000}, + {0x0000a448, 0x05000080}, + {0x0000a44c, 0x00000001}, + {0x0000a450, 0x00010000}, + {0x0000a458, 0x00000000}, + {0x0000a644, 0xbfad9d74}, + {0x0000a648, 0x0048060a}, + {0x0000a64c, 0x00003c37}, + {0x0000a670, 0x03020100}, + {0x0000a674, 0x09080504}, + {0x0000a678, 0x0d0c0b0a}, + {0x0000a67c, 0x13121110}, + {0x0000a680, 0x31301514}, + {0x0000a684, 0x35343332}, + {0x0000a688, 0x00000036}, + {0x0000a690, 0x08000838}, + {0x0000a7cc, 0x00000000}, + {0x0000a7d0, 0x00000000}, + {0x0000a7d4, 0x00000004}, + {0x0000a7dc, 0x00000000}, + {0x0000a8d0, 0x004b6a8e}, + {0x0000a8d4, 0x00000820}, + {0x0000a8dc, 0x00000000}, + {0x0000a8f0, 0x00000000}, + {0x0000a8f4, 0x00000000}, + {0x0000b2d0, 0x00000080}, + {0x0000b2d4, 0x00000000}, + {0x0000b2ec, 0x00000000}, + {0x0000b2f0, 0x00000000}, + {0x0000b2f4, 0x00000000}, + {0x0000b2f8, 0x00000000}, + {0x0000b408, 0x0e79e5c0}, + {0x0000b40c, 0x00820820}, + {0x0000b420, 0x00000000}, +}; + +static const u32 qca953x_2p0_baseband_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, + {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, + {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, + {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, + {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, + {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, + {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, + {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, + {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222}, + {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, + {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, + {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0}, + {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, + {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, + {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, + {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018}, + {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, + {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, + {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e}, + {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, + {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, + {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, + {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33}, + {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982}, + {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, + {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, + {0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, +}; + #endif /* INITVALS_953X_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 2ca8f7e06174..7fc13a8da675 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/leds.h> #include <linux/completion.h> +#include <linux/time.h> #include "common.h" #include "debug.h" @@ -35,10 +36,7 @@ extern struct ieee80211_ops ath9k_ops; extern int ath9k_modparam_nohwcrypt; extern int led_blink; extern bool is_ath9k_unloaded; - -struct ath_config { - u16 txpowlimit; -}; +extern int ath9k_use_chanctx; /*************************/ /* Descriptor Management */ @@ -167,7 +165,6 @@ struct ath_txq { u32 axq_ampdu_depth; bool stopped; bool axq_tx_inprogress; - struct list_head axq_acq; struct list_head txq_fifo[ATH_TXFIFO_DEPTH]; u8 txq_headidx; u8 txq_tailidx; @@ -185,7 +182,8 @@ struct ath_atx_ac { struct ath_frame_info { struct ath_buf *bf; - int framelen; + u16 framelen; + s8 txq; enum ath9k_key_type keytype; u8 keyix; u8 rtscts_rate; @@ -280,8 +278,9 @@ struct ath_node { struct ath_tx_control { struct ath_txq *txq; struct ath_node *an; - u8 paprd; struct ieee80211_sta *sta; + u8 paprd; + bool force_channel; }; @@ -325,6 +324,116 @@ struct ath_rx { u32 ampdu_ref; }; +struct ath_chanctx { + struct cfg80211_chan_def chandef; + struct list_head vifs; + struct list_head acq[IEEE80211_NUM_ACS]; + int hw_queue_base; + + /* do not dereference, use for comparison only */ + struct ieee80211_vif *primary_sta; + + struct ath_beacon_config beacon; + struct ath9k_hw_cal_data caldata; + struct timespec tsf_ts; + u64 tsf_val; + u32 last_beacon; + + u16 txpower; + bool offchannel; + bool stopped; + bool active; + bool assigned; + bool switch_after_beacon; +}; + +enum ath_chanctx_event { + ATH_CHANCTX_EVENT_BEACON_PREPARE, + ATH_CHANCTX_EVENT_BEACON_SENT, + ATH_CHANCTX_EVENT_TSF_TIMER, + ATH_CHANCTX_EVENT_BEACON_RECEIVED, + ATH_CHANCTX_EVENT_ASSOC, + ATH_CHANCTX_EVENT_SWITCH, + ATH_CHANCTX_EVENT_UNASSIGN, + ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL, +}; + +enum ath_chanctx_state { + ATH_CHANCTX_STATE_IDLE, + ATH_CHANCTX_STATE_WAIT_FOR_BEACON, + ATH_CHANCTX_STATE_WAIT_FOR_TIMER, + ATH_CHANCTX_STATE_SWITCH, + ATH_CHANCTX_STATE_FORCE_ACTIVE, +}; + +struct ath_chanctx_sched { + bool beacon_pending; + bool offchannel_pending; + enum ath_chanctx_state state; + u8 beacon_miss; + + u32 next_tbtt; + u32 switch_start_time; + unsigned int offchannel_duration; + unsigned int channel_switch_time; + + /* backup, in case the hardware timer fails */ + struct timer_list timer; +}; + +enum ath_offchannel_state { + ATH_OFFCHANNEL_IDLE, + ATH_OFFCHANNEL_PROBE_SEND, + ATH_OFFCHANNEL_PROBE_WAIT, + ATH_OFFCHANNEL_SUSPEND, + ATH_OFFCHANNEL_ROC_START, + ATH_OFFCHANNEL_ROC_WAIT, + ATH_OFFCHANNEL_ROC_DONE, +}; + +struct ath_offchannel { + struct ath_chanctx chan; + struct timer_list timer; + struct cfg80211_scan_request *scan_req; + struct ieee80211_vif *scan_vif; + int scan_idx; + enum ath_offchannel_state state; + struct ieee80211_channel *roc_chan; + struct ieee80211_vif *roc_vif; + int roc_duration; + int duration; +}; +#define ath_for_each_chanctx(_sc, _ctx) \ + for (ctx = &sc->chanctx[0]; \ + ctx <= &sc->chanctx[ARRAY_SIZE(sc->chanctx) - 1]; \ + ctx++) + +void ath9k_fill_chanctx_ops(void); +void ath9k_chanctx_force_active(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +static inline struct ath_chanctx * +ath_chanctx_get(struct ieee80211_chanctx_conf *ctx) +{ + struct ath_chanctx **ptr = (void *) ctx->drv_priv; + return *ptr; +} +void ath_chanctx_init(struct ath_softc *sc); +void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef); +void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef); +void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx); +void ath_offchannel_timer(unsigned long data); +void ath_offchannel_channel_change(struct ath_softc *sc); +void ath_chanctx_offchan_switch(struct ath_softc *sc, + struct ieee80211_channel *chan); +struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, + bool active); +void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, + enum ath_chanctx_event ev); +void ath_chanctx_timer(unsigned long data); + +int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); int ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); u32 ath_calcrxfilter(struct ath_softc *sc); @@ -341,6 +450,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq); void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an); void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an); void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); +void ath_txq_schedule_all(struct ath_softc *sc); int ath_tx_init(struct ath_softc *sc, int nbufs); int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_tx_queue_info *q); @@ -370,32 +480,47 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, /********/ struct ath_vif { + struct list_head list; + struct ieee80211_vif *vif; struct ath_node mcast_node; int av_bslot; - bool primary_sta_vif; __le64 tsf_adjust; /* TSF adjustment for staggered beacons */ struct ath_buf *av_bcbuf; + struct ath_chanctx *chanctx; /* P2P Client */ struct ieee80211_noa_data noa; + + /* P2P GO */ + u8 noa_index; + u32 offchannel_start; + u32 offchannel_duration; + + u32 periodic_noa_start; + u32 periodic_noa_duration; }; struct ath9k_vif_iter_data { u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */ u8 mask[ETH_ALEN]; /* bssid mask */ bool has_hw_macaddr; + u8 slottime; + bool beacons; int naps; /* number of AP vifs */ int nmeshes; /* number of mesh vifs */ int nstations; /* number of station vifs */ int nwds; /* number of WDS vifs */ int nadhocs; /* number of adhoc vifs */ + struct ieee80211_vif *primary_sta; }; -void ath9k_calculate_iter_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, +void ath9k_calculate_iter_data(struct ath_softc *sc, + struct ath_chanctx *ctx, struct ath9k_vif_iter_data *iter_data); +void ath9k_calculate_summary_state(struct ath_softc *sc, + struct ath_chanctx *ctx); /*******************/ /* Beacon Handling */ @@ -458,6 +583,7 @@ void ath9k_csa_update(struct ath_softc *sc); #define ATH_PAPRD_TIMEOUT 100 /* msecs */ #define ATH_PLL_WORK_INTERVAL 100 +void ath_chanctx_work(struct work_struct *work); void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); bool ath_hw_check(struct ath_softc *sc); @@ -473,6 +599,7 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); void ath_ps_full_sleep(unsigned long data); void ath9k_p2p_ps_timer(void *priv); void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif); +void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop); /**********/ /* BTCOEX */ @@ -702,6 +829,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); #define PS_BEACON_SYNC BIT(4) #define PS_WAIT_FOR_ANI BIT(5) +#define ATH9K_NUM_CHANCTX 2 /* supports 2 operating channels */ + struct ath_softc { struct ieee80211_hw *hw; struct device *dev; @@ -720,6 +849,7 @@ struct ath_softc { struct mutex mutex; struct work_struct paprd_work; struct work_struct hw_reset_work; + struct work_struct chanctx_work; struct completion paprd_complete; wait_queue_head_t tx_wait; @@ -738,23 +868,27 @@ struct ath_softc { short nvifs; unsigned long ps_usecount; - struct ath_config config; struct ath_rx rx; struct ath_tx tx; struct ath_beacon beacon; + struct cfg80211_chan_def cur_chandef; + struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX]; + struct ath_chanctx *cur_chan; + struct ath_chanctx *next_chan; + spinlock_t chan_lock; + struct ath_offchannel offchannel; + struct ath_chanctx_sched sched; + #ifdef CONFIG_MAC80211_LEDS bool led_registered; char led_name[32]; struct led_classdev led_cdev; #endif - struct ath9k_hw_cal_data caldata; - #ifdef CONFIG_ATH9K_DEBUGFS struct ath9k_debug debug; #endif - struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; struct delayed_work hw_pll_work; struct timer_list sleep_timer; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index e387f0b2954a..eaf8f058c151 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, u8 chainmask = ah->txchainmask; u8 rate = 0; - sband = &common->sbands[common->hw->conf.chandef.chan->band]; + sband = &common->sbands[sc->cur_chandef.chan->band]; rate = sband->bitrates[rateidx].hw_value; if (vif->bss_conf.use_short_preamble) rate |= sband->bitrates[rateidx].hw_value_short; @@ -108,6 +108,55 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); } +static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, + struct sk_buff *skb) +{ + static const u8 noa_ie_hdr[] = { + WLAN_EID_VENDOR_SPECIFIC, /* type */ + 0, /* length */ + 0x50, 0x6f, 0x9a, /* WFA OUI */ + 0x09, /* P2P subtype */ + 0x0c, /* Notice of Absence */ + 0x00, /* LSB of little-endian len */ + 0x00, /* MSB of little-endian len */ + }; + + struct ieee80211_p2p_noa_attr *noa; + int noa_len, noa_desc, i = 0; + u8 *hdr; + + if (!avp->offchannel_duration && !avp->periodic_noa_duration) + return; + + noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration; + noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc; + + hdr = skb_put(skb, sizeof(noa_ie_hdr)); + memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr)); + hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2; + hdr[7] = noa_len; + + noa = (void *) skb_put(skb, noa_len); + memset(noa, 0, noa_len); + + noa->index = avp->noa_index; + if (avp->periodic_noa_duration) { + u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + + noa->desc[i].count = 255; + noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start); + noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration); + noa->desc[i].interval = cpu_to_le32(interval); + i++; + } + + if (avp->offchannel_duration) { + noa->desc[i].count = 1; + noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start); + noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration); + } +} + static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -155,6 +204,9 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } + if (vif->p2p) + ath9k_beacon_add_noa(sc, avp, skb); + bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, skb->len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { @@ -249,7 +301,7 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) static int ath9k_beacon_choose_slot(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; u16 intval; u32 tsftu; u64 tsf; @@ -277,8 +329,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc) static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_beacon_config *cur_conf = &avp->chanctx->beacon; u32 tsfadjust; if (avp->av_bslot == 0) @@ -374,12 +426,19 @@ void ath9k_beacon_tasklet(unsigned long data) vif = sc->beacon.bslot[slot]; /* EDMA devices check that in the tx completion function. */ - if (!edma && ath9k_csa_is_finished(sc, vif)) - return; + if (!edma) { + if (sc->sched.beacon_pending) + ath_chanctx_event(sc, NULL, + ATH_CHANCTX_EVENT_BEACON_SENT); + + if (ath9k_csa_is_finished(sc, vif)) + return; + } if (!vif || !vif->bss_conf.enable_beacon) return; + ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE); bf = ath9k_beacon_generate(sc->hw, vif); if (sc->beacon.bmisscnt != 0) { @@ -500,7 +559,6 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_vif *avp = (void *)vif->drv_priv; if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { if ((vif->type != NL80211_IFTYPE_AP) || @@ -514,7 +572,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { if ((vif->type == NL80211_IFTYPE_STATION) && test_bit(ATH_OP_BEACONS, &common->op_flags) && - !avp->primary_sta_vif) { + vif != sc->cur_chan->primary_sta) { ath_dbg(common, CONFIG, "Beacon already configured for a station interface\n"); return false; @@ -525,10 +583,11 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, } static void ath9k_cache_beacon_config(struct ath_softc *sc, + struct ath_chanctx *ctx, struct ieee80211_bss_conf *bss_conf) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &ctx->beacon; ath_dbg(common, BEACON, "Caching beacon data for BSS: %pM\n", bss_conf->bssid); @@ -564,20 +623,29 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, u32 changed) { struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_chanctx *ctx = avp->chanctx; + struct ath_beacon_config *cur_conf; unsigned long flags; bool skip_beacon = false; + if (!ctx) + return; + + cur_conf = &avp->chanctx->beacon; if (vif->type == NL80211_IFTYPE_AP) ath9k_set_tsfadjust(sc, vif); if (!ath9k_allow_beacon_config(sc, vif)) return; - if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { - ath9k_cache_beacon_config(sc, bss_conf); + if (vif->type == NL80211_IFTYPE_STATION) { + ath9k_cache_beacon_config(sc, ctx, bss_conf); + if (ctx != sc->cur_chan) + return; + ath9k_set_beacon(sc); set_bit(ATH_OP_BEACONS, &common->op_flags); return; @@ -593,10 +661,13 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, cur_conf->enable_beacon = false; } else if (bss_conf->enable_beacon) { cur_conf->enable_beacon = true; - ath9k_cache_beacon_config(sc, bss_conf); + ath9k_cache_beacon_config(sc, ctx, bss_conf); } } + if (ctx != sc->cur_chan) + return; + /* * Configure the HW beacon registers only when we have a valid * beacon interval. @@ -631,7 +702,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, void ath9k_set_beacon(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; switch (sc->sc_ah->opmode) { case NL80211_IFTYPE_AP: diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c new file mode 100644 index 000000000000..ba214ebdcd16 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -0,0 +1,685 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +/* Set/change channels. If the channel is really being changed, it's done + * by reseting the chip. To accomplish this we must first cleanup any pending + * DMA, then restart stuff. + */ +static int ath_set_channel(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_hw *hw = sc->hw; + struct ath9k_channel *hchan; + struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef; + struct ieee80211_channel *chan = chandef->chan; + int pos = chan->hw_value; + int old_pos = -1; + int r; + + if (test_bit(ATH_OP_INVALID, &common->op_flags)) + return -EIO; + + if (ah->curchan) + old_pos = ah->curchan - &ah->channels[0]; + + ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", + chan->center_freq, chandef->width); + + /* update survey stats for the old channel before switching */ + spin_lock_bh(&common->cc_lock); + ath_update_survey_stats(sc); + spin_unlock_bh(&common->cc_lock); + + ath9k_cmn_get_channel(hw, ah, chandef); + + /* If the operating channel changes, change the survey in-use flags + * along with it. + * Reset the survey data for the new channel, unless we're switching + * back to the operating channel from an off-channel operation. + */ + if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) { + if (sc->cur_survey) + sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; + + sc->cur_survey = &sc->survey[pos]; + + memset(sc->cur_survey, 0, sizeof(struct survey_info)); + sc->cur_survey->filled |= SURVEY_INFO_IN_USE; + } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { + memset(&sc->survey[pos], 0, sizeof(struct survey_info)); + } + + hchan = &sc->sc_ah->channels[pos]; + r = ath_reset_internal(sc, hchan); + if (r) + return r; + + /* The most recent snapshot of channel->noisefloor for the old + * channel is only available after the hardware reset. Copy it to + * the survey stats now. + */ + if (old_pos >= 0) + ath_update_survey_nf(sc, old_pos); + + /* Enable radar pulse detection if on a DFS channel. Spectral + * scanning and radar detection can not be used concurrently. + */ + if (hw->conf.radar_enabled) { + u32 rxfilter; + + /* set HW specific DFS configuration */ + ath9k_hw_set_radar_params(ah); + rxfilter = ath9k_hw_getrxfilter(ah); + rxfilter |= ATH9K_RX_FILTER_PHYRADAR | + ATH9K_RX_FILTER_PHYERR; + ath9k_hw_setrxfilter(ah, rxfilter); + ath_dbg(common, DFS, "DFS enabled at freq %d\n", + chan->center_freq); + } else { + /* perform spectral scan if requested. */ + if (test_bit(ATH_OP_SCANNING, &common->op_flags) && + sc->spectral_mode == SPECTRAL_CHANSCAN) + ath9k_spectral_scan_trigger(hw); + } + + return 0; +} + +static bool +ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, + bool powersave) +{ + struct ieee80211_vif *vif = avp->vif; + struct ieee80211_sta *sta = NULL; + struct ieee80211_hdr_3addr *nullfunc; + struct ath_tx_control txctl; + struct sk_buff *skb; + int band = sc->cur_chan->chandef.chan->band; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (!vif->bss_conf.assoc) + return false; + + skb = ieee80211_nullfunc_get(sc->hw, vif); + if (!skb) + return false; + + nullfunc = (struct ieee80211_hdr_3addr *) skb->data; + if (powersave) + nullfunc->frame_control |= + cpu_to_le16(IEEE80211_FCTL_PM); + + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) { + dev_kfree_skb_any(skb); + return false; + } + break; + default: + return false; + } + + memset(&txctl, 0, sizeof(txctl)); + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + txctl.sta = sta; + txctl.force_channel = true; + if (ath_tx_start(sc->hw, skb, &txctl)) { + ieee80211_free_txskb(sc->hw, skb); + return false; + } + + return true; +} + +void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp; + bool active = false; + u8 n_active = 0; + + if (!ctx) + return; + + list_for_each_entry(avp, &ctx->vifs, list) { + struct ieee80211_vif *vif = avp->vif; + + switch (vif->type) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + if (vif->bss_conf.assoc) + active = true; + break; + default: + active = true; + break; + } + } + ctx->active = active; + + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + n_active++; + } + + if (n_active <= 1) { + clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); + return; + } + if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + return; + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); +} + +static bool +ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave) +{ + struct ath_vif *avp; + bool sent = false; + + rcu_read_lock(); + list_for_each_entry(avp, &sc->cur_chan->vifs, list) { + if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave)) + sent = true; + } + rcu_read_unlock(); + + return sent; +} + +static bool ath_chanctx_defer_switch(struct ath_softc *sc) +{ + if (sc->cur_chan == &sc->offchannel.chan) + return false; + + switch (sc->sched.state) { + case ATH_CHANCTX_STATE_SWITCH: + return false; + case ATH_CHANCTX_STATE_IDLE: + if (!sc->cur_chan->switch_after_beacon) + return false; + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + break; + default: + break; + } + + return true; +} + +static void ath_chanctx_set_next(struct ath_softc *sc, bool force) +{ + struct timespec ts; + bool measure_time = false; + bool send_ps = false; + + spin_lock_bh(&sc->chan_lock); + if (!sc->next_chan) { + spin_unlock_bh(&sc->chan_lock); + return; + } + + if (!force && ath_chanctx_defer_switch(sc)) { + spin_unlock_bh(&sc->chan_lock); + return; + } + + if (sc->cur_chan != sc->next_chan) { + sc->cur_chan->stopped = true; + spin_unlock_bh(&sc->chan_lock); + + if (sc->next_chan == &sc->offchannel.chan) { + getrawmonotonic(&ts); + measure_time = true; + } + __ath9k_flush(sc->hw, ~0, true); + + if (ath_chanctx_send_ps_frame(sc, true)) + __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false); + + send_ps = true; + spin_lock_bh(&sc->chan_lock); + + if (sc->cur_chan != &sc->offchannel.chan) { + getrawmonotonic(&sc->cur_chan->tsf_ts); + sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah); + } + } + sc->cur_chan = sc->next_chan; + sc->cur_chan->stopped = false; + sc->next_chan = NULL; + sc->sched.offchannel_duration = 0; + if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE) + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + + spin_unlock_bh(&sc->chan_lock); + + if (sc->sc_ah->chip_fullsleep || + memcmp(&sc->cur_chandef, &sc->cur_chan->chandef, + sizeof(sc->cur_chandef))) { + ath_set_channel(sc); + if (measure_time) + sc->sched.channel_switch_time = + ath9k_hw_get_tsf_offset(&ts, NULL); + } + if (send_ps) + ath_chanctx_send_ps_frame(sc, false); + + ath_offchannel_channel_change(sc); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH); +} + +void ath_chanctx_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + chanctx_work); + mutex_lock(&sc->mutex); + ath_chanctx_set_next(sc, false); + mutex_unlock(&sc->mutex); +} + +void ath_chanctx_init(struct ath_softc *sc) +{ + struct ath_chanctx *ctx; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + int i, j; + + sband = &common->sbands[IEEE80211_BAND_2GHZ]; + if (!sband->n_channels) + sband = &common->sbands[IEEE80211_BAND_5GHZ]; + + chan = &sband->channels[0]; + for (i = 0; i < ATH9K_NUM_CHANCTX; i++) { + ctx = &sc->chanctx[i]; + cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); + INIT_LIST_HEAD(&ctx->vifs); + ctx->txpower = ATH_TXPOWER_MAX; + for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) + INIT_LIST_HEAD(&ctx->acq[j]); + } + ctx = &sc->offchannel.chan; + cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); + INIT_LIST_HEAD(&ctx->vifs); + ctx->txpower = ATH_TXPOWER_MAX; + for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) + INIT_LIST_HEAD(&ctx->acq[j]); + sc->offchannel.chan.offchannel = true; + +} + +void ath9k_chanctx_force_active(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp = (struct ath_vif *) vif->drv_priv; + bool changed = false; + + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + return; + + if (!avp->chanctx) + return; + + mutex_lock(&sc->mutex); + + spin_lock_bh(&sc->chan_lock); + if (sc->next_chan || (sc->cur_chan != avp->chanctx)) { + sc->next_chan = avp->chanctx; + changed = true; + } + sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE; + spin_unlock_bh(&sc->chan_lock); + + if (changed) + ath_chanctx_set_next(sc, true); + + mutex_unlock(&sc->mutex); +} + +void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + spin_lock_bh(&sc->chan_lock); + + if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) && + (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) { + sc->sched.offchannel_pending = true; + spin_unlock_bh(&sc->chan_lock); + return; + } + + sc->next_chan = ctx; + if (chandef) + ctx->chandef = *chandef; + + if (sc->next_chan == &sc->offchannel.chan) { + sc->sched.offchannel_duration = + TU_TO_USEC(sc->offchannel.duration) + + sc->sched.channel_switch_time; + } + spin_unlock_bh(&sc->chan_lock); + ieee80211_queue_work(sc->hw, &sc->chanctx_work); +} + +void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef) +{ + bool cur_chan; + + spin_lock_bh(&sc->chan_lock); + if (chandef) + memcpy(&ctx->chandef, chandef, sizeof(*chandef)); + cur_chan = sc->cur_chan == ctx; + spin_unlock_bh(&sc->chan_lock); + + if (!cur_chan) + return; + + ath_set_channel(sc); +} + +struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active) +{ + struct ath_chanctx *ctx; + + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + if (active && !ctx->active) + continue; + + if (ctx->switch_after_beacon) + return ctx; + } + + return &sc->chanctx[0]; +} + +void ath_chanctx_offchan_switch(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + struct cfg80211_chan_def chandef; + + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + + ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef); +} + +static struct ath_chanctx * +ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx) +{ + int idx = ctx - &sc->chanctx[0]; + + return &sc->chanctx[!idx]; +} + +static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) +{ + struct ath_chanctx *prev, *cur; + struct timespec ts; + u32 cur_tsf, prev_tsf, beacon_int; + s32 offset; + + beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + + cur = sc->cur_chan; + prev = ath_chanctx_get_next(sc, cur); + + getrawmonotonic(&ts); + cur_tsf = (u32) cur->tsf_val + + ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts); + + prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf; + prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts); + + /* Adjust the TSF time of the AP chanctx to keep its beacons + * at half beacon interval offset relative to the STA chanctx. + */ + offset = cur_tsf - prev_tsf; + + /* Ignore stale data or spurious timestamps */ + if (offset < 0 || offset > 3 * beacon_int) + return; + + offset = beacon_int / 2 - (offset % beacon_int); + prev->tsf_val += offset; +} + +void ath_chanctx_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); +} + +/* Configure the TSF based hardware timer for a channel switch. + * Also set up backup software timer, in case the gen timer fails. + * This could be caused by a hardware reset. + */ +static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time) +{ + struct ath_hw *ah = sc->sc_ah; + + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000); + tsf_time -= ath9k_hw_gettsf32(ah); + tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1; + mod_timer(&sc->sched.timer, tsf_time); +} + +void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, + enum ath_chanctx_event ev) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_beacon_config *cur_conf; + struct ath_vif *avp = NULL; + struct ath_chanctx *ctx; + u32 tsf_time; + u32 beacon_int; + bool noa_changed = false; + + if (vif) + avp = (struct ath_vif *) vif->drv_priv; + + spin_lock_bh(&sc->chan_lock); + + switch (ev) { + case ATH_CHANCTX_EVENT_BEACON_PREPARE: + if (avp->offchannel_duration) + avp->offchannel_duration = 0; + + if (avp->chanctx != sc->cur_chan) + break; + + if (sc->sched.offchannel_pending) { + sc->sched.offchannel_pending = false; + sc->next_chan = &sc->offchannel.chan; + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + } + + ctx = ath_chanctx_get_next(sc, sc->cur_chan); + if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) { + sc->next_chan = ctx; + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + } + + /* if the timer missed its window, use the next interval */ + if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) + break; + + sc->sched.beacon_pending = true; + sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); + + cur_conf = &sc->cur_chan->beacon; + beacon_int = TU_TO_USEC(cur_conf->beacon_interval); + + /* defer channel switch by a quarter beacon interval */ + tsf_time = sc->sched.next_tbtt + beacon_int / 4; + sc->sched.switch_start_time = tsf_time; + sc->cur_chan->last_beacon = sc->sched.next_tbtt; + + /* Prevent wrap-around issues */ + if (avp->periodic_noa_duration && + tsf_time - avp->periodic_noa_start > BIT(30)) + avp->periodic_noa_duration = 0; + + if (ctx->active && !avp->periodic_noa_duration) { + avp->periodic_noa_start = tsf_time; + avp->periodic_noa_duration = + TU_TO_USEC(cur_conf->beacon_interval) / 2 - + sc->sched.channel_switch_time; + noa_changed = true; + } else if (!ctx->active && avp->periodic_noa_duration) { + avp->periodic_noa_duration = 0; + noa_changed = true; + } + + /* If at least two consecutive beacons were missed on the STA + * chanctx, stay on the STA channel for one extra beacon period, + * to resync the timer properly. + */ + if (ctx->active && sc->sched.beacon_miss >= 2) + sc->sched.offchannel_duration = 3 * beacon_int / 2; + + if (sc->sched.offchannel_duration) { + noa_changed = true; + avp->offchannel_start = tsf_time; + avp->offchannel_duration = + sc->sched.offchannel_duration; + } + + if (noa_changed) + avp->noa_index++; + break; + case ATH_CHANCTX_EVENT_BEACON_SENT: + if (!sc->sched.beacon_pending) + break; + + sc->sched.beacon_pending = false; + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) + break; + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + ath_chanctx_setup_timer(sc, sc->sched.switch_start_time); + break; + case ATH_CHANCTX_EVENT_TSF_TIMER: + if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER) + break; + + if (!sc->cur_chan->switch_after_beacon && + sc->sched.beacon_pending) + sc->sched.beacon_miss++; + + sc->sched.state = ATH_CHANCTX_STATE_SWITCH; + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; + case ATH_CHANCTX_EVENT_BEACON_RECEIVED: + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || + sc->cur_chan == &sc->offchannel.chan) + break; + + ath_chanctx_adjust_tbtt_delta(sc); + sc->sched.beacon_pending = false; + sc->sched.beacon_miss = 0; + + /* TSF time might have been updated by the incoming beacon, + * need update the channel switch timer to reflect the change. + */ + tsf_time = sc->sched.switch_start_time; + tsf_time -= (u32) sc->cur_chan->tsf_val + + ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); + tsf_time += ath9k_hw_gettsf32(ah); + + + ath_chanctx_setup_timer(sc, tsf_time); + break; + case ATH_CHANCTX_EVENT_ASSOC: + if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || + avp->chanctx != sc->cur_chan) + break; + + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + /* fall through */ + case ATH_CHANCTX_EVENT_SWITCH: + if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || + sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE || + sc->cur_chan->switch_after_beacon || + sc->cur_chan == &sc->offchannel.chan) + break; + + /* If this is a station chanctx, stay active for a half + * beacon period (minus channel switch time) + */ + sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); + cur_conf = &sc->cur_chan->beacon; + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + + tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2; + if (sc->sched.beacon_miss >= 2) { + sc->sched.beacon_miss = 0; + tsf_time *= 3; + } + + tsf_time -= sc->sched.channel_switch_time; + tsf_time += ath9k_hw_gettsf32(sc->sc_ah); + sc->sched.switch_start_time = tsf_time; + + ath_chanctx_setup_timer(sc, tsf_time); + sc->sched.beacon_pending = true; + break; + case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: + if (sc->cur_chan == &sc->offchannel.chan || + sc->cur_chan->switch_after_beacon) + break; + + sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; + case ATH_CHANCTX_EVENT_UNASSIGN: + if (sc->cur_chan->assigned) { + if (sc->next_chan && !sc->next_chan->assigned && + sc->next_chan != &sc->offchannel.chan) + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + break; + } + + ctx = ath_chanctx_get_next(sc, sc->cur_chan); + sc->sched.state = ATH_CHANCTX_STATE_IDLE; + if (!ctx->assigned) + break; + + sc->next_chan = ctx; + ieee80211_queue_work(sc->hw, &sc->chanctx_work); + break; + } + + spin_unlock_bh(&sc->chan_lock); +} diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c index 775d1d20ce0b..733be5178481 100644 --- a/drivers/net/wireless/ath/ath9k/common-beacon.c +++ b/drivers/net/wireless/ath/ath9k/common-beacon.c @@ -57,7 +57,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, struct ath9k_beacon_state *bs) { struct ath_common *common = ath9k_hw_common(ah); - int dtim_intval; + int dtim_intval, sleepduration; u64 tsf; /* No need to configure beacon if we are not associated */ @@ -75,6 +75,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, * last beacon we received (which may be none). */ dtim_intval = conf->intval * conf->dtim_period; + sleepduration = ah->hw->conf.listen_interval * conf->intval; /* * Pull nexttbtt forward to reflect the current @@ -112,7 +113,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah, */ bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100), - conf->intval)); + sleepduration)); if (bs->bs_sleepduration > bs->bs_dtimperiod) bs->bs_sleepduration = bs->bs_dtimperiod; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 6cc42be48d4e..d2279365be6f 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -202,7 +202,7 @@ static ssize_t write_file_ani(struct file *file, if (kstrtoul(buf, 0, &ani)) return -EINVAL; - if (ani < 0 || ani > 1) + if (ani > 1) return -EINVAL; common->disable_ani = !ani; @@ -750,13 +750,13 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, { struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ieee80211_hw *hw = sc->hw; struct ath9k_vif_iter_data iter_data; + struct ath_chanctx *ctx; char buf[512]; unsigned int len = 0; ssize_t retval = 0; unsigned int reg; - u32 rxfilter; + u32 rxfilter, i; len += scnprintf(buf + len, sizeof(buf) - len, "BSSID: %pM\n", common->curbssid); @@ -826,14 +826,20 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, len += scnprintf(buf + len, sizeof(buf) - len, "\n"); - ath9k_calculate_iter_data(hw, NULL, &iter_data); - - len += scnprintf(buf + len, sizeof(buf) - len, - "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i" - " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", - iter_data.naps, iter_data.nstations, iter_data.nmeshes, - iter_data.nwds, iter_data.nadhocs, - sc->nvifs, sc->nbcnvifs); + i = 0; + ath_for_each_chanctx(sc, ctx) { + if (!ctx->assigned || list_empty(&ctx->vifs)) + continue; + ath9k_calculate_iter_data(sc, ctx, &iter_data); + + len += scnprintf(buf + len, sizeof(buf) - len, + "VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i", + i++, iter_data.naps, iter_data.nstations, + iter_data.nmeshes, iter_data.nwds); + len += scnprintf(buf + len, sizeof(buf) - len, + " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", + iter_data.nadhocs, sc->nvifs, sc->nbcnvifs); + } if (len > sizeof(buf)) len = sizeof(buf); @@ -1080,7 +1086,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf, { struct ath_softc *sc = file->private_data; struct ath_hw *ah = sc->sc_ah; - struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist; + struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; u32 len = 0, size = 1500; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 2a8ed8375ec0..69bbea1184d2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -791,7 +791,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, refdiv = 5; } else { pll2_divint = 0x11; - pll2_divfrac = 0x26666; + pll2_divfrac = + AR_SREV_9531(ah) ? 0x26665 : 0x26666; refdiv = 1; } } @@ -1730,11 +1731,27 @@ fail: return -EINVAL; } +u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur) +{ + struct timespec ts; + s64 usec; + + if (!cur) { + getrawmonotonic(&ts); + cur = &ts; + } + + usec = cur->tv_sec * 1000000ULL + cur->tv_nsec / 1000; + usec -= last->tv_sec * 1000000ULL + last->tv_nsec / 1000; + + return (u32) usec; +} +EXPORT_SYMBOL(ath9k_hw_get_tsf_offset); + int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata, bool fastcc) { struct ath_common *common = ath9k_hw_common(ah); - struct timespec ts; u32 saveLedState; u32 saveDefAntenna; u32 macStaId1; @@ -1784,8 +1801,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, /* Save TSF before chip reset, a cold reset clears it */ tsf = ath9k_hw_gettsf64(ah); - getrawmonotonic(&ts); - usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000; + usec = ktime_to_us(ktime_get_raw()); saveLedState = REG_READ(ah, AR_CFG_LED) & (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | @@ -1818,8 +1834,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } /* Restore TSF */ - getrawmonotonic(&ts); - usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec; + usec = ktime_to_us(ktime_get_raw()) - usec; ath9k_hw_settsf64(ah, tsf + usec); if (AR_SREV_9280_20_OR_LATER(ah)) diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 0acd4b5a4892..51b4ebe04c04 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1000,6 +1000,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah); u64 ath9k_hw_gettsf64(struct ath_hw *ah); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah); +u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set); void ath9k_hw_init_global_settings(struct ath_hw *ah); u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 0246b990fe87..39419ea845cc 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -61,7 +61,7 @@ static int ath9k_ps_enable; module_param_named(ps_enable, ath9k_ps_enable, int, 0444); MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); -static int ath9k_use_chanctx; +int ath9k_use_chanctx; module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444); MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency"); @@ -169,9 +169,9 @@ static void ath9k_reg_notifier(struct wiphy *wiphy, /* Set tx power */ if (ah->curchan) { - sc->config.txpowlimit = 2 * ah->curchan->chan->max_power; + sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power; ath9k_ps_wakeup(sc); - ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); + ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit; /* synchronize DFS detector if regulatory domain changed */ if (sc->dfs_detector != NULL) @@ -335,7 +335,6 @@ static void ath9k_init_misc(struct ath_softc *sc) setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); common->last_rssi = ATH_RSSI_DUMMY_MARKER; - sc->config.txpowlimit = ATH_TXPOWER_MAX; memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); sc->beacon.slottime = ATH9K_SLOT_TIME_9; @@ -511,6 +510,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); sc->tx99_power = MAX_RATE_POWER + 1; init_waitqueue_head(&sc->tx_wait); + sc->cur_chan = &sc->chanctx[0]; + if (!ath9k_use_chanctx) + sc->cur_chan->hw_queue_base = 0; if (!pdata || pdata->use_eeprom) { ah->ah_flags |= AH_USE_EEPROM; @@ -556,6 +558,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, spin_lock_init(&common->cc_lock); spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); + spin_lock_init(&sc->chan_lock); mutex_init(&sc->mutex); tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, @@ -564,7 +567,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc); INIT_WORK(&sc->hw_reset_work, ath_reset_work); INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); + INIT_WORK(&sc->chanctx_work, ath_chanctx_work); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); + setup_timer(&sc->offchannel.timer, ath_offchannel_timer, + (unsigned long)sc); + setup_timer(&sc->sched.timer, ath_chanctx_timer, (unsigned long)sc); /* * Cache line size is used to size and align various @@ -599,6 +606,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath9k_cmn_init_crypto(sc->sc_ah); ath9k_init_misc(sc); ath_fill_led_pin(sc); + ath_chanctx_init(sc); if (common->bus_ops->aspm_init) common->bus_ops->aspm_init(common); @@ -664,6 +672,12 @@ static const struct ieee80211_iface_limit wds_limits[] = { { .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) }, }; +static const struct ieee80211_iface_limit if_limits_multi[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) }, +}; + static const struct ieee80211_iface_limit if_dfs_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | #ifdef CONFIG_MAC80211_MESH @@ -672,6 +686,16 @@ static const struct ieee80211_iface_limit if_dfs_limits[] = { BIT(NL80211_IFTYPE_ADHOC) }, }; +static const struct ieee80211_iface_combination if_comb_multi[] = { + { + .limits = if_limits_multi, + .n_limits = ARRAY_SIZE(if_limits_multi), + .max_interfaces = 2, + .num_different_channels = 2, + .beacon_int_infra_match = true, + }, +}; + static const struct ieee80211_iface_combination if_comb[] = { { .limits = if_limits, @@ -712,6 +736,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_RC_TABLE | + IEEE80211_HW_QUEUE_CONTROL | IEEE80211_HW_SUPPORTS_HT_CCK_RATES; if (ath9k_ps_enable) @@ -739,12 +764,21 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); - hw->wiphy->iface_combinations = if_comb; if (!ath9k_use_chanctx) { + hw->wiphy->iface_combinations = if_comb; hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS); - } else - hw->wiphy->n_iface_combinations = 1; + } else { + hw->wiphy->iface_combinations = if_comb_multi; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(if_comb_multi); + hw->wiphy->max_scan_ssids = 255; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->max_remain_on_channel_duration = 10000; + hw->chanctx_data_size = sizeof(void *); + hw->extra_beacon_tailroom = + sizeof(struct ieee80211_p2p_noa_attr) + 9; + } } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -756,9 +790,14 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; - hw->queues = 4; + /* allow 4 queues per channel context + + * 1 cab queue + 1 offchannel tx queue + */ + hw->queues = 10; + /* last queue for offchannel */ + hw->offchannel_tx_hw_queue = hw->queues - 1; hw->max_rates = 4; - hw->max_listen_interval = 1; + hw->max_listen_interval = 10; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 72a715fe8f24..2343f56e6498 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -178,7 +178,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE]; memset(tx_info, 0, sizeof(*tx_info)); - tx_info->band = hw->conf.chandef.chan->band; + tx_info->band = sc->cur_chandef.chan->band; tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; tx_info->control.rates[0].idx = 0; tx_info->control.rates[0].count = 1; @@ -416,7 +416,7 @@ void ath_start_ani(struct ath_softc *sc) if (common->disable_ani || !test_bit(ATH_OP_ANI_RUN, &common->op_flags) || - (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + sc->cur_chan->offchannel) return; common->ani.longcal_timer = timestamp; @@ -440,7 +440,7 @@ void ath_check_ani(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; /* * Check for the various conditions in which ANI has to diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index da7686757535..6c56cafa5ca4 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -346,8 +346,14 @@ struct ar5416_desc { #define AR_FrameLen 0x00000fff #define AR_VirtMoreFrag 0x00001000 #define AR_TxCtlRsvd00 0x0000e000 -#define AR_XmitPower 0x003f0000 -#define AR_XmitPower_S 16 +#define AR_XmitPower0 0x003f0000 +#define AR_XmitPower0_S 16 +#define AR_XmitPower1 0x3f000000 +#define AR_XmitPower1_S 24 +#define AR_XmitPower2 0x3f000000 +#define AR_XmitPower2_S 24 +#define AR_XmitPower3 0x3f000000 +#define AR_XmitPower3_S 24 #define AR_RTSEnable 0x00400000 #define AR_VEOL 0x00800000 #define AR_ClrDestMask 0x01000000 diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 62ac95d6bb9d..e6ac8d2e610c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -19,9 +19,6 @@ #include "ath9k.h" #include "btcoex.h" -static void ath9k_set_assoc_state(struct ath_softc *sc, - struct ieee80211_vif *vif); - u8 ath9k_parse_mpdudensity(u8 mpdudensity) { /* @@ -63,9 +60,16 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq) spin_lock_bh(&txq->axq_lock); - if (txq->axq_depth || !list_empty(&txq->axq_acq)) + if (txq->axq_depth) pending = true; + if (txq->mac80211_qnum >= 0) { + struct list_head *list; + + list = &sc->cur_chan->acq[txq->mac80211_qnum]; + if (!list_empty(list)) + pending = true; + } spin_unlock_bh(&txq->axq_lock); return pending; } @@ -227,13 +231,22 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) } ath9k_cmn_update_txpow(ah, sc->curtxpow, - sc->config.txpowlimit, &sc->curtxpow); + sc->cur_chan->txpower, &sc->curtxpow); clear_bit(ATH_OP_HW_RESET, &common->op_flags); - ath9k_hw_set_interrupts(ah); - ath9k_hw_enable_interrupts(ah); + ath9k_calculate_summary_state(sc, sc->cur_chan); + + if (!sc->cur_chan->offchannel && start) { + /* restore per chanctx TSF timer */ + if (sc->cur_chan->tsf_val) { + u32 offset; + + offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, + NULL); + ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset); + } + - if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) { if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) goto work; @@ -247,26 +260,35 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) } work: ath_restart_work(sc); + ath_txq_schedule_all(sc); + } - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { - if (!ATH_TXQ_SETUP(sc, i)) - continue; + sc->gtt_cnt = 0; - spin_lock_bh(&sc->tx.txq[i].axq_lock); - ath_txq_schedule(sc, &sc->tx.txq[i]); - spin_unlock_bh(&sc->tx.txq[i].axq_lock); + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + + if (!ath9k_use_chanctx) + ieee80211_wake_queues(sc->hw); + else { + if (sc->cur_chan == &sc->offchannel.chan) + ieee80211_wake_queue(sc->hw, + sc->hw->offchannel_tx_hw_queue); + else { + for (i = 0; i < IEEE80211_NUM_ACS; i++) + ieee80211_wake_queue(sc->hw, + sc->cur_chan->hw_queue_base + i); } + if (ah->opmode == NL80211_IFTYPE_AP) + ieee80211_wake_queue(sc->hw, sc->hw->queues - 2); } - sc->gtt_cnt = 0; - ieee80211_wake_queues(sc->hw); - ath9k_p2p_ps_timer(sc); return true; } -static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) +int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); @@ -279,9 +301,9 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) tasklet_disable(&sc->intr_tq); spin_lock_bh(&sc->sc_pcu_lock); - if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { + if (!sc->cur_chan->offchannel) { fastcc = false; - caldata = &sc->caldata; + caldata = &sc->cur_chan->caldata; } if (!hchan) { @@ -292,6 +314,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) if (!ath_prepare_reset(sc)) fastcc = false; + spin_lock_bh(&sc->chan_lock); + sc->cur_chandef = sc->cur_chan->chandef; + spin_unlock_bh(&sc->chan_lock); + ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", hchan->channel, IS_CHAN_HT40(hchan), fastcc); @@ -307,7 +333,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) } if (ath9k_hw_mci_is_enabled(sc->sc_ah) && - (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + sc->cur_chan->offchannel) ath9k_mci_set_txpower(sc, true, false); if (!ath_complete_reset(sc, true)) @@ -320,98 +346,6 @@ out: return r; } - -/* - * Set/change channels. If the channel is really being changed, it's done - * by reseting the chip. To accomplish this we must first cleanup any pending - * DMA, then restart stuff. -*/ -static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_hw *hw = sc->hw; - struct ath9k_channel *hchan; - struct ieee80211_channel *chan = chandef->chan; - bool offchannel; - int pos = chan->hw_value; - int old_pos = -1; - int r; - - if (test_bit(ATH_OP_INVALID, &common->op_flags)) - return -EIO; - - offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); - - if (ah->curchan) - old_pos = ah->curchan - &ah->channels[0]; - - ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", - chan->center_freq, chandef->width); - - /* update survey stats for the old channel before switching */ - spin_lock_bh(&common->cc_lock); - ath_update_survey_stats(sc); - spin_unlock_bh(&common->cc_lock); - - ath9k_cmn_get_channel(hw, ah, chandef); - - /* - * If the operating channel changes, change the survey in-use flags - * along with it. - * Reset the survey data for the new channel, unless we're switching - * back to the operating channel from an off-channel operation. - */ - if (!offchannel && sc->cur_survey != &sc->survey[pos]) { - if (sc->cur_survey) - sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; - - sc->cur_survey = &sc->survey[pos]; - - memset(sc->cur_survey, 0, sizeof(struct survey_info)); - sc->cur_survey->filled |= SURVEY_INFO_IN_USE; - } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { - memset(&sc->survey[pos], 0, sizeof(struct survey_info)); - } - - hchan = &sc->sc_ah->channels[pos]; - r = ath_reset_internal(sc, hchan); - if (r) - return r; - - /* - * The most recent snapshot of channel->noisefloor for the old - * channel is only available after the hardware reset. Copy it to - * the survey stats now. - */ - if (old_pos >= 0) - ath_update_survey_nf(sc, old_pos); - - /* - * Enable radar pulse detection if on a DFS channel. Spectral - * scanning and radar detection can not be used concurrently. - */ - if (hw->conf.radar_enabled) { - u32 rxfilter; - - /* set HW specific DFS configuration */ - ath9k_hw_set_radar_params(ah); - rxfilter = ath9k_hw_getrxfilter(ah); - rxfilter |= ATH9K_RX_FILTER_PHYRADAR | - ATH9K_RX_FILTER_PHYERR; - ath9k_hw_setrxfilter(ah, rxfilter); - ath_dbg(common, DFS, "DFS enabled at freq %d\n", - chan->center_freq); - } else { - /* perform spectral scan if requested. */ - if (test_bit(ATH_OP_SCANNING, &common->op_flags) && - sc->spectral_mode == SPECTRAL_CHANSCAN) - ath9k_spectral_scan_trigger(hw); - } - - return 0; -} - static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { @@ -712,7 +646,8 @@ static int ath9k_start(struct ieee80211_hw *hw) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_channel *curchan = hw->conf.chandef.chan; + struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan; + struct ath_chanctx *ctx = sc->cur_chan; struct ath9k_channel *init_channel; int r; @@ -723,7 +658,8 @@ static int ath9k_start(struct ieee80211_hw *hw) ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); - init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); + init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef); + sc->cur_chandef = hw->conf.chandef; /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, false); @@ -886,6 +822,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) struct ath_common *common = ath9k_hw_common(ah); bool prev_idle; + cancel_work_sync(&sc->chanctx_work); mutex_lock(&sc->mutex); ath_cancel_work(sc); @@ -934,7 +871,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) } if (!ah->curchan) - ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); + ah->curchan = ath9k_cmn_get_channel(hw, ah, + &sc->cur_chan->chandef); ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); ath9k_hw_phy_disable(ah); @@ -979,18 +917,29 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) iter_data->has_hw_macaddr = true; } + if (!vif->bss_conf.use_short_slot) + iter_data->slottime = ATH9K_SLOT_TIME_20; + switch (vif->type) { case NL80211_IFTYPE_AP: iter_data->naps++; + if (vif->bss_conf.enable_beacon) + iter_data->beacons = true; break; case NL80211_IFTYPE_STATION: iter_data->nstations++; + if (vif->bss_conf.assoc && !iter_data->primary_sta) + iter_data->primary_sta = vif; break; case NL80211_IFTYPE_ADHOC: iter_data->nadhocs++; + if (vif->bss_conf.enable_beacon) + iter_data->beacons = true; break; case NL80211_IFTYPE_MESH_POINT: iter_data->nmeshes++; + if (vif->bss_conf.enable_beacon) + iter_data->beacons = true; break; case NL80211_IFTYPE_WDS: iter_data->nwds++; @@ -1000,26 +949,12 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) } } -static void ath9k_sta_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath_softc *sc = data; - struct ath_vif *avp = (void *)vif->drv_priv; - - if (vif->type != NL80211_IFTYPE_STATION) - return; - - if (avp->primary_sta_vif) - ath9k_set_assoc_state(sc, vif); -} - /* Called with sc->mutex held. */ -void ath9k_calculate_iter_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, +void ath9k_calculate_iter_data(struct ath_softc *sc, + struct ath_chanctx *ctx, struct ath9k_vif_iter_data *iter_data) { - struct ath_softc *sc = hw->priv; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); + struct ath_vif *avp; /* * Pick the MAC address of the first interface as the new hardware @@ -1028,29 +963,80 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, */ memset(iter_data, 0, sizeof(*iter_data)); memset(&iter_data->mask, 0xff, ETH_ALEN); + iter_data->slottime = ATH9K_SLOT_TIME_9; + + list_for_each_entry(avp, &ctx->vifs, list) + ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif); + + if (ctx == &sc->offchannel.chan) { + struct ieee80211_vif *vif; + + if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START) + vif = sc->offchannel.scan_vif; + else + vif = sc->offchannel.roc_vif; + + if (vif) + ath9k_vif_iter(iter_data, vif->addr, vif); + iter_data->beacons = false; + } +} + +static void ath9k_set_assoc_state(struct ath_softc *sc, + struct ieee80211_vif *vif, bool changed) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + unsigned long flags; - if (vif) - ath9k_vif_iter(iter_data, vif->addr, vif); + set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); + /* Set the AID, BSSID and do beacon-sync only when + * the HW opmode is STATION. + * + * But the primary bit is set above in any case. + */ + if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) + return; + + ether_addr_copy(common->curbssid, bss_conf->bssid); + common->curaid = bss_conf->aid; + ath9k_hw_write_associd(sc->sc_ah); - /* Get list of all active MAC addresses */ - ieee80211_iterate_active_interfaces_atomic( - sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath9k_vif_iter, iter_data); + if (changed) { + common->last_rssi = ATH_RSSI_DUMMY_MARKER; + sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; - memcpy(common->macaddr, iter_data->hw_macaddr, ETH_ALEN); + spin_lock_irqsave(&sc->sc_pm_lock, flags); + sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + } + + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + ath9k_mci_update_wlan_channels(sc, false); + + ath_dbg(common, CONFIG, + "Primary Station interface: %pM, BSSID: %pM\n", + vif->addr, common->curbssid); } /* Called with sc->mutex held. */ -static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +void ath9k_calculate_summary_state(struct ath_softc *sc, + struct ath_chanctx *ctx) { - struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_vif_iter_data iter_data; - enum nl80211_iftype old_opmode = ah->opmode; - ath9k_calculate_iter_data(hw, vif, &iter_data); + ath_chanctx_check_active(sc, ctx); + + if (ctx != sc->cur_chan) + return; + + ath9k_ps_wakeup(sc); + ath9k_calculate_iter_data(sc, ctx, &iter_data); + + if (iter_data.has_hw_macaddr) + ether_addr_copy(common->macaddr, iter_data.hw_macaddr); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); ath_hw_setbssidmask(common); @@ -1073,24 +1059,57 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, ath9k_hw_setopmode(ah); + ctx->switch_after_beacon = false; if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0) ah->imask |= ATH9K_INT_TSFOOR; - else + else { ah->imask &= ~ATH9K_INT_TSFOOR; + if (iter_data.naps == 1 && iter_data.beacons) + ctx->switch_after_beacon = true; + } + + ah->imask &= ~ATH9K_INT_SWBA; + if (ah->opmode == NL80211_IFTYPE_STATION) { + bool changed = (iter_data.primary_sta != ctx->primary_sta); + iter_data.beacons = true; + if (iter_data.primary_sta) { + ath9k_set_assoc_state(sc, iter_data.primary_sta, + changed); + if (!ctx->primary_sta || + !ctx->primary_sta->bss_conf.assoc) + ctx->primary_sta = iter_data.primary_sta; + } else { + ctx->primary_sta = NULL; + memset(common->curbssid, 0, ETH_ALEN); + common->curaid = 0; + ath9k_hw_write_associd(sc->sc_ah); + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + ath9k_mci_update_wlan_channels(sc, true); + } + } else if (iter_data.beacons) { + ah->imask |= ATH9K_INT_SWBA; + } ath9k_hw_set_interrupts(ah); - /* - * If we are changing the opmode to STATION, - * a beacon sync needs to be done. - */ - if (ah->opmode == NL80211_IFTYPE_STATION && - old_opmode == NL80211_IFTYPE_AP && - test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { - ieee80211_iterate_active_interfaces_atomic( - sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath9k_sta_vif_iter, sc); + if (iter_data.beacons) + set_bit(ATH_OP_BEACONS, &common->op_flags); + else + clear_bit(ATH_OP_BEACONS, &common->op_flags); + + if (ah->slottime != iter_data.slottime) { + ah->slottime = iter_data.slottime; + ath9k_hw_init_global_settings(ah); } + + if (iter_data.primary_sta) + set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); + else + clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); + + ctx->primary_sta = iter_data.primary_sta; + + ath9k_ps_restore(sc); } static int ath9k_add_interface(struct ieee80211_hw *hw, @@ -1101,6 +1120,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_node *an = &avp->mcast_node; + int i; mutex_lock(&sc->mutex); @@ -1115,14 +1135,20 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->nvifs++; - ath9k_ps_wakeup(sc); - ath9k_calculate_summary_state(hw, vif); - ath9k_ps_restore(sc); - if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); avp->vif = vif; + if (!ath9k_use_chanctx) { + avp->chanctx = sc->cur_chan; + list_add_tail(&avp->list, &avp->chanctx->vifs); + } + for (i = 0; i < IEEE80211_NUM_ACS; i++) + vif->hw_queue[i] = i; + if (vif->type == NL80211_IFTYPE_AP) + vif->cab_queue = hw->queues - 2; + else + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; an->sc = sc; an->sta = NULL; @@ -1141,6 +1167,8 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp = (void *)vif->drv_priv; + int i; mutex_lock(&sc->mutex); @@ -1157,13 +1185,19 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, vif->type = new_type; vif->p2p = p2p; - ath9k_ps_wakeup(sc); - ath9k_calculate_summary_state(hw, vif); - ath9k_ps_restore(sc); - if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); + for (i = 0; i < IEEE80211_NUM_ACS; i++) + vif->hw_queue[i] = i; + + if (vif->type == NL80211_IFTYPE_AP) + vif->cab_queue = hw->queues - 2; + else + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; + + ath9k_calculate_summary_state(sc, avp->chanctx); + mutex_unlock(&sc->mutex); return 0; } @@ -1211,14 +1245,12 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, sc->nvifs--; sc->tx99_vif = NULL; + if (!ath9k_use_chanctx) + list_del(&avp->list); if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); - ath9k_ps_wakeup(sc); - ath9k_calculate_summary_state(hw, NULL); - ath9k_ps_restore(sc); - ath_tx_node_cleanup(sc, &avp->mcast_node); mutex_unlock(&sc->mutex); @@ -1345,7 +1377,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &hw->conf; - bool reset_channel = false; + struct ath_chanctx *ctx = sc->cur_chan; ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); @@ -1361,7 +1393,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) * The chip needs a reset to properly wake up from * full sleep */ - reset_channel = ah->chip_fullsleep; + ath_chanctx_set_channel(sc, ctx, &ctx->chandef); } } @@ -1391,20 +1423,16 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } } - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { - if (ath_set_channel(sc, &hw->conf.chandef) < 0) { - ath_err(common, "Unable to set channel\n"); - mutex_unlock(&sc->mutex); - ath9k_ps_restore(sc); - return -EINVAL; - } + if (!ath9k_use_chanctx && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { + ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL); + ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef); } if (changed & IEEE80211_CONF_CHANGE_POWER) { ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level); - sc->config.txpowlimit = 2 * conf->power_level; + sc->cur_chan->txpower = 2 * conf->power_level; ath9k_cmn_update_txpow(ah, sc->curtxpow, - sc->config.txpowlimit, &sc->curtxpow); + sc->cur_chan->txpower, &sc->curtxpow); } mutex_unlock(&sc->mutex); @@ -1659,58 +1687,6 @@ static int ath9k_set_key(struct ieee80211_hw *hw, return ret; } -static void ath9k_set_assoc_state(struct ath_softc *sc, - struct ieee80211_vif *vif) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_vif *avp = (void *)vif->drv_priv; - struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - unsigned long flags; - - set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); - avp->primary_sta_vif = true; - - /* - * Set the AID, BSSID and do beacon-sync only when - * the HW opmode is STATION. - * - * But the primary bit is set above in any case. - */ - if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) - return; - - memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); - common->curaid = bss_conf->aid; - ath9k_hw_write_associd(sc->sc_ah); - - common->last_rssi = ATH_RSSI_DUMMY_MARKER; - sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; - - spin_lock_irqsave(&sc->sc_pm_lock, flags); - sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; - spin_unlock_irqrestore(&sc->sc_pm_lock, flags); - - if (ath9k_hw_mci_is_enabled(sc->sc_ah)) - ath9k_mci_update_wlan_channels(sc, false); - - ath_dbg(common, CONFIG, - "Primary Station interface: %pM, BSSID: %pM\n", - vif->addr, common->curbssid); -} - -static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath_softc *sc = data; - struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - - if (test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) - return; - - if (bss_conf->assoc) - ath9k_set_assoc_state(sc, vif); -} - void ath9k_p2p_ps_timer(void *priv) { struct ath_softc *sc = priv; @@ -1720,7 +1696,11 @@ void ath9k_p2p_ps_timer(void *priv) struct ath_node *an; u32 tsf; - if (!avp) + del_timer_sync(&sc->sched.timer); + ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); + + if (!avp || avp->chanctx != sc->cur_chan) return; tsf = ath9k_hw_gettsf32(sc->sc_ah); @@ -1795,26 +1775,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n", bss_conf->bssid, bss_conf->assoc); - if (avp->primary_sta_vif && !bss_conf->assoc) { - clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); - avp->primary_sta_vif = false; - - if (ah->opmode == NL80211_IFTYPE_STATION) - clear_bit(ATH_OP_BEACONS, &common->op_flags); - } - - ieee80211_iterate_active_interfaces_atomic( - sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - ath9k_bss_assoc_iter, sc); - - if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags) && - ah->opmode == NL80211_IFTYPE_STATION) { - memset(common->curbssid, 0, ETH_ALEN); - common->curaid = 0; - ath9k_hw_write_associd(sc->sc_ah); - if (ath9k_hw_mci_is_enabled(sc->sc_ah)) - ath9k_mci_update_wlan_channels(sc, true); - } + ath9k_calculate_summary_state(sc, avp->chanctx); + if (bss_conf->assoc) + ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_ASSOC); } if (changed & BSS_CHANGED_IBSS) { @@ -1824,10 +1787,15 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } if ((changed & BSS_CHANGED_BEACON_ENABLED) || - (changed & BSS_CHANGED_BEACON_INT)) + (changed & BSS_CHANGED_BEACON_INT) || + (changed & BSS_CHANGED_BEACON_INFO)) { + if (changed & BSS_CHANGED_BEACON_ENABLED) + ath9k_calculate_summary_state(sc, avp->chanctx); ath9k_beacon_config(sc, vif, changed); + } - if (changed & BSS_CHANGED_ERP_SLOT) { + if ((avp->chanctx == sc->cur_chan) && + (changed & BSS_CHANGED_ERP_SLOT)) { if (bss_conf->use_short_slot) slottime = 9; else @@ -2032,23 +2000,30 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) { struct ath_softc *sc = hw->priv; + + mutex_lock(&sc->mutex); + __ath9k_flush(hw, queues, drop); + mutex_unlock(&sc->mutex); +} + +void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +{ + struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); int timeout = HZ / 5; /* 200 ms */ bool drain_txq; + int i; - mutex_lock(&sc->mutex); cancel_delayed_work_sync(&sc->tx_complete_work); if (ah->ah_flags & AH_UNPLUGGED) { ath_dbg(common, ANY, "Device has been unplugged!\n"); - mutex_unlock(&sc->mutex); return; } if (test_bit(ATH_OP_INVALID, &common->op_flags)) { ath_dbg(common, ANY, "Device not present\n"); - mutex_unlock(&sc->mutex); return; } @@ -2066,11 +2041,13 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ath_reset(sc); ath9k_ps_restore(sc); - ieee80211_wake_queues(hw); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + ieee80211_wake_queue(sc->hw, + sc->cur_chan->hw_queue_base + i); + } } ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0); - mutex_unlock(&sc->mutex); } static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw) @@ -2230,6 +2207,403 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) clear_bit(ATH_OP_SCANNING, &common->op_flags); } +static int ath_scan_channel_duration(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + + if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR)) + return (HZ / 9); /* ~110 ms */ + + return (HZ / 16); /* ~60 ms */ +} + +static void +ath_scan_next_channel(struct ath_softc *sc) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + struct ieee80211_channel *chan; + + if (sc->offchannel.scan_idx >= req->n_channels) { + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), + NULL); + return; + } + + chan = req->channels[sc->offchannel.scan_idx++]; + sc->offchannel.duration = ath_scan_channel_duration(sc, chan); + sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND; + ath_chanctx_offchan_switch(sc, chan); +} + +static void ath_offchannel_next(struct ath_softc *sc) +{ + struct ieee80211_vif *vif; + + if (sc->offchannel.scan_req) { + vif = sc->offchannel.scan_vif; + sc->offchannel.chan.txpower = vif->bss_conf.txpower; + ath_scan_next_channel(sc); + } else if (sc->offchannel.roc_vif) { + vif = sc->offchannel.roc_vif; + sc->offchannel.chan.txpower = vif->bss_conf.txpower; + sc->offchannel.duration = sc->offchannel.roc_duration; + sc->offchannel.state = ATH_OFFCHANNEL_ROC_START; + ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan); + } else { + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), + NULL); + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + if (sc->ps_idle) + ath_cancel_work(sc); + } +} + +static void ath_roc_complete(struct ath_softc *sc, bool abort) +{ + sc->offchannel.roc_vif = NULL; + sc->offchannel.roc_chan = NULL; + if (!abort) + ieee80211_remain_on_channel_expired(sc->hw); + ath_offchannel_next(sc); + ath9k_ps_restore(sc); +} + +static void ath_scan_complete(struct ath_softc *sc, bool abort) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + sc->offchannel.scan_req = NULL; + sc->offchannel.scan_vif = NULL; + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; + ieee80211_scan_completed(sc->hw, abort); + clear_bit(ATH_OP_SCANNING, &common->op_flags); + ath_offchannel_next(sc); + ath9k_ps_restore(sc); +} + +static void ath_scan_send_probe(struct ath_softc *sc, + struct cfg80211_ssid *ssid) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + struct ieee80211_vif *vif = sc->offchannel.scan_vif; + struct ath_tx_control txctl = {}; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + int band = sc->offchannel.chan.chandef.chan->band; + + skb = ieee80211_probereq_get(sc->hw, vif, + ssid->ssid, ssid->ssid_len, req->ie_len); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + if (req->no_cck) + info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + + if (req->ie_len) + memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); + + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + + if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL)) + goto error; + + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + txctl.force_channel = true; + if (ath_tx_start(sc->hw, skb, &txctl)) + goto error; + + return; + +error: + ieee80211_free_txskb(sc->hw, skb); +} + +static void ath_scan_channel_start(struct ath_softc *sc) +{ + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + int i; + + if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) && + req->n_ssids) { + for (i = 0; i < req->n_ssids; i++) + ath_scan_send_probe(sc, &req->ssids[i]); + + } + + sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT; + mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration); +} + +void ath_offchannel_channel_change(struct ath_softc *sc) +{ + switch (sc->offchannel.state) { + case ATH_OFFCHANNEL_PROBE_SEND: + if (!sc->offchannel.scan_req) + return; + + if (sc->cur_chan->chandef.chan != + sc->offchannel.chan.chandef.chan) + return; + + ath_scan_channel_start(sc); + break; + case ATH_OFFCHANNEL_IDLE: + if (!sc->offchannel.scan_req) + return; + + ath_scan_complete(sc, false); + break; + case ATH_OFFCHANNEL_ROC_START: + if (sc->cur_chan != &sc->offchannel.chan) + break; + + sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT; + mod_timer(&sc->offchannel.timer, jiffies + + msecs_to_jiffies(sc->offchannel.duration)); + ieee80211_ready_on_channel(sc->hw); + break; + case ATH_OFFCHANNEL_ROC_DONE: + ath_roc_complete(sc, false); + break; + default: + break; + } +} + +void ath_offchannel_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_chanctx *ctx; + + switch (sc->offchannel.state) { + case ATH_OFFCHANNEL_PROBE_WAIT: + if (!sc->offchannel.scan_req) + return; + + /* get first active channel context */ + ctx = ath_chanctx_get_oper_chan(sc, true); + if (ctx->active) { + sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; + ath_chanctx_switch(sc, ctx, NULL); + mod_timer(&sc->offchannel.timer, jiffies + HZ / 10); + break; + } + /* fall through */ + case ATH_OFFCHANNEL_SUSPEND: + if (!sc->offchannel.scan_req) + return; + + ath_scan_next_channel(sc); + break; + case ATH_OFFCHANNEL_ROC_START: + case ATH_OFFCHANNEL_ROC_WAIT: + ctx = ath_chanctx_get_oper_chan(sc, false); + sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; + ath_chanctx_switch(sc, ctx, NULL); + break; + default: + break; + } +} + +static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct cfg80211_scan_request *req = &hw_req->req; + struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int ret = 0; + + mutex_lock(&sc->mutex); + + if (WARN_ON(sc->offchannel.scan_req)) { + ret = -EBUSY; + goto out; + } + + ath9k_ps_wakeup(sc); + set_bit(ATH_OP_SCANNING, &common->op_flags); + sc->offchannel.scan_vif = vif; + sc->offchannel.scan_req = req; + sc->offchannel.scan_idx = 0; + + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_offchannel_next(sc); + +out: + mutex_unlock(&sc->mutex); + + return ret; +} + +static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath_softc *sc = hw->priv; + + mutex_lock(&sc->mutex); + del_timer_sync(&sc->offchannel.timer); + ath_scan_complete(sc, true); + mutex_unlock(&sc->mutex); +} + +static int ath9k_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, int duration, + enum ieee80211_roc_type type) +{ + struct ath_softc *sc = hw->priv; + int ret = 0; + + mutex_lock(&sc->mutex); + + if (WARN_ON(sc->offchannel.roc_vif)) { + ret = -EBUSY; + goto out; + } + + ath9k_ps_wakeup(sc); + sc->offchannel.roc_vif = vif; + sc->offchannel.roc_chan = chan; + sc->offchannel.roc_duration = duration; + + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_offchannel_next(sc); + +out: + mutex_unlock(&sc->mutex); + + return ret; +} + +static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + + mutex_lock(&sc->mutex); + + del_timer_sync(&sc->offchannel.timer); + + if (sc->offchannel.roc_vif) { + if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) + ath_roc_complete(sc, true); + } + + mutex_unlock(&sc->mutex); + + return 0; +} + +static int ath9k_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_chanctx *ctx, **ptr; + int pos; + + mutex_lock(&sc->mutex); + + ath_for_each_chanctx(sc, ctx) { + if (ctx->assigned) + continue; + + ptr = (void *) conf->drv_priv; + *ptr = ctx; + ctx->assigned = true; + pos = ctx - &sc->chanctx[0]; + ctx->hw_queue_base = pos * IEEE80211_NUM_ACS; + ath_chanctx_set_channel(sc, ctx, &conf->def); + mutex_unlock(&sc->mutex); + return 0; + } + mutex_unlock(&sc->mutex); + return -ENOSPC; +} + + +static void ath9k_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_chanctx *ctx = ath_chanctx_get(conf); + + mutex_lock(&sc->mutex); + ctx->assigned = false; + ctx->hw_queue_base = -1; + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN); + mutex_unlock(&sc->mutex); +} + +static void ath9k_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, + u32 changed) +{ + struct ath_softc *sc = hw->priv; + struct ath_chanctx *ctx = ath_chanctx_get(conf); + + mutex_lock(&sc->mutex); + ath_chanctx_set_channel(sc, ctx, &conf->def); + mutex_unlock(&sc->mutex); +} + +static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_chanctx *ctx = ath_chanctx_get(conf); + int i; + + mutex_lock(&sc->mutex); + avp->chanctx = ctx; + list_add_tail(&avp->list, &ctx->vifs); + ath9k_calculate_summary_state(sc, ctx); + for (i = 0; i < IEEE80211_NUM_ACS; i++) + vif->hw_queue[i] = ctx->hw_queue_base + i; + mutex_unlock(&sc->mutex); + + return 0; +} + +static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_vif *avp = (void *)vif->drv_priv; + struct ath_chanctx *ctx = ath_chanctx_get(conf); + int ac; + + mutex_lock(&sc->mutex); + avp->chanctx = NULL; + list_del(&avp->list); + ath9k_calculate_summary_state(sc, ctx); + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE; + mutex_unlock(&sc->mutex); +} + +void ath9k_fill_chanctx_ops(void) +{ + if (!ath9k_use_chanctx) + return; + + ath9k_ops.hw_scan = ath9k_hw_scan; + ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; + ath9k_ops.remain_on_channel = ath9k_remain_on_channel; + ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; + ath9k_ops.add_chanctx = ath9k_add_chanctx; + ath9k_ops.remove_chanctx = ath9k_remove_chanctx; + ath9k_ops.change_chanctx = ath9k_change_chanctx; + ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx; + ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx; + ath9k_ops.mgd_prepare_tx = ath9k_chanctx_force_active; +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index a0dbcc412384..3f7a11edb82a 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -706,7 +706,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, return; if (setchannel) { - struct ath9k_hw_cal_data *caldata = &sc->caldata; + struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata; if (IS_CHAN_HT40PLUS(ah->curchan) && (ah->curchan->channel > caldata->channel) && (ah->curchan->channel <= caldata->channel + 20)) @@ -720,7 +720,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, mci_hw->concur_tx = concur_tx; if (old_concur_tx != mci_hw->concur_tx) - ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); + ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); } static void ath9k_mci_stomp_audio(struct ath_softc *sc) diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 4dec09e565ed..7a2b2c5caced 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -843,6 +843,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENODEV; } + ath9k_fill_chanctx_ops(); hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); if (!hw) { dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 9105a92364f7..74ab1d02013b 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -259,7 +259,7 @@ static void ath_edma_start_recv(struct ath_softc *sc) ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP); ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP); ath_opmode_init(sc); - ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); + ath9k_hw_startpcureceive(sc->sc_ah, sc->cur_chan->offchannel); } static void ath_edma_stop_recv(struct ath_softc *sc) @@ -374,6 +374,7 @@ void ath_rx_cleanup(struct ath_softc *sc) u32 ath_calcrxfilter(struct ath_softc *sc) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); u32 rfilt; if (config_enabled(CONFIG_ATH9K_TX99)) @@ -424,6 +425,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc) if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah)) rfilt |= ATH9K_RX_FILTER_4ADDRESS; + if (ath9k_use_chanctx && + test_bit(ATH_OP_SCANNING, &common->op_flags)) + rfilt |= ATH9K_RX_FILTER_BEACON; + return rfilt; } @@ -457,7 +462,7 @@ int ath_startrecv(struct ath_softc *sc) start_recv: ath_opmode_init(sc); - ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); + ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel); return 0; } @@ -540,7 +545,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) sc->ps_flags &= ~PS_BEACON_SYNC; ath_dbg(common, PS, "Reconfigure beacon timers based on synchronized timestamp\n"); - if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0))) + if (!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0))) ath9k_set_beacon(sc); if (sc->p2p_ps_vif) ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif); @@ -887,6 +892,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, return -EINVAL; } + if (rx_stats->is_mybeacon) { + sc->sched.next_tbtt = rx_stats->rs_tstamp; + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_BEACON_RECEIVED); + } + ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status); rx_status->band = ah->curchan->chan->band; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index f1bbce3f7774..a1499700bcf2 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -813,6 +813,7 @@ #define AR_SREV_VERSION_9531 0x500 #define AR_SREV_REVISION_9531_10 0 #define AR_SREV_REVISION_9531_11 1 +#define AR_SREV_REVISION_9531_20 2 #define AR_SREV_5416(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \ @@ -958,6 +959,9 @@ #define AR_SREV_9531_11(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_11)) +#define AR_SREV_9531_20(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_20)) /* NOTE: When adding chips newer than Peacock, add chip check here */ #define AR_SREV_9580_10_OR_LATER(_ah) \ diff --git a/drivers/net/wireless/ath/ath9k/spectral.c b/drivers/net/wireless/ath/ath9k/spectral.c index 99f4de95c264..5fe29b9f8fa2 100644 --- a/drivers/net/wireless/ath/ath9k/spectral.c +++ b/drivers/net/wireless/ath/ath9k/spectral.c @@ -313,7 +313,7 @@ static ssize_t write_file_spectral_short_repeat(struct file *file, if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val < 0 || val > 1) + if (val > 1) return -EINVAL; sc->spec_config.short_repeat = val; @@ -361,7 +361,7 @@ static ssize_t write_file_spectral_count(struct file *file, if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val < 0 || val > 255) + if (val > 255) return -EINVAL; sc->spec_config.count = val; @@ -409,7 +409,7 @@ static ssize_t write_file_spectral_period(struct file *file, if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val < 0 || val > 255) + if (val > 255) return -EINVAL; sc->spec_config.period = val; @@ -457,7 +457,7 @@ static ssize_t write_file_spectral_fft_period(struct file *file, if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val < 0 || val > 15) + if (val > 15) return -EINVAL; sc->spec_config.fft_period = val; diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index a65cfb91adca..23972924c774 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -76,7 +76,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) tx_info = IEEE80211_SKB_CB(skb); memset(tx_info, 0, sizeof(*tx_info)); rate = &tx_info->control.rates[0]; - tx_info->band = hw->conf.chandef.chan->band; + tx_info->band = sc->cur_chan->chandef.chan->band; tx_info->flags = IEEE80211_TX_CTL_NO_ACK; tx_info->control.vif = sc->tx99_vif; rate->count = 1; diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index 2879887f5691..a4f4f0da81f6 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -193,6 +193,7 @@ int ath9k_suspend(struct ieee80211_hw *hw, u32 wow_triggers_enabled = 0; int ret = 0; + cancel_work_sync(&sc->chanctx_work); mutex_lock(&sc->mutex); ath_cancel_work(sc); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 66acb2cbd9df..704fcbcbe20b 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -103,9 +103,16 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq) ieee80211_tx_status(sc->hw, skb); } -static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) +static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid) { struct ath_atx_ac *ac = tid->ac; + struct list_head *list; + struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv; + struct ath_chanctx *ctx = avp->chanctx; + + if (!ctx) + return; if (tid->sched) return; @@ -117,7 +124,9 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) return; ac->sched = true; - list_add_tail(&ac->list, &txq->axq_acq); + + list = &ctx->acq[TID_TO_WME_AC(tid->tidno)]; + list_add_tail(&ac->list, list); } static struct ath_frame_info *get_frame_info(struct sk_buff *skb) @@ -147,21 +156,22 @@ static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta, static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, struct sk_buff *skb) { - int q; - - q = skb_get_queue_mapping(skb); - if (txq == sc->tx.uapsdq) - txq = sc->tx.txq_map[q]; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ath_frame_info *fi = get_frame_info(skb); + int hw_queue; + int q = fi->txq; - if (txq != sc->tx.txq_map[q]) + if (q < 0) return; + txq = sc->tx.txq_map[q]; if (WARN_ON(--txq->pending_frames < 0)) txq->pending_frames = 0; + hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue; if (txq->stopped && txq->pending_frames < sc->tx.txq_max_pending[q]) { - ieee80211_wake_queue(sc->hw, q); + ieee80211_wake_queue(sc->hw, hw_queue); txq->stopped = false; } } @@ -626,7 +636,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, skb_queue_splice_tail(&bf_pending, &tid->retry_q); if (!an->sleeping) { - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY)) tid->ac->clear_ps_filter = true; @@ -887,6 +897,15 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, tx_info = IEEE80211_SKB_CB(skb); tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT; + + /* + * No aggregation session is running, but there may be frames + * from a previous session or a failed attempt in the queue. + * Send them out as normal data frames + */ + if (!tid->active) + tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU; + if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) { bf->bf_state.bf_type = 0; return bf; @@ -1483,7 +1502,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) ac->clear_ps_filter = true; if (ath_tid_has_buffered(tid)) { - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); ath_txq_schedule(sc, txq); } @@ -1507,7 +1526,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; if (ath_tid_has_buffered(tid)) { - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); ath_txq_schedule(sc, txq); } @@ -1642,7 +1661,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) txq->axq_link = NULL; __skb_queue_head_init(&txq->complete_q); INIT_LIST_HEAD(&txq->axq_q); - INIT_LIST_HEAD(&txq->axq_acq); spin_lock_init(&txq->axq_lock); txq->axq_depth = 0; txq->axq_ampdu_depth = 0; @@ -1686,7 +1704,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum, int ath_cabq_update(struct ath_softc *sc) { struct ath9k_tx_queue_info qi; - struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; int qnum = sc->beacon.cabq->axq_qnum; ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); @@ -1804,7 +1822,7 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) sc->tx.txqsetup &= ~(1<<txq->axq_qnum); } -/* For each axq_acq entry, for each tid, try to schedule packets +/* For each acq entry, for each tid, try to schedule packets * for transmit until ampdu_depth has reached min Q depth. */ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) @@ -1812,19 +1830,31 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_ac *ac, *last_ac; struct ath_atx_tid *tid, *last_tid; + struct list_head *ac_list; bool sent = false; + if (txq->mac80211_qnum < 0) + return; + + spin_lock_bh(&sc->chan_lock); + ac_list = &sc->cur_chan->acq[txq->mac80211_qnum]; + spin_unlock_bh(&sc->chan_lock); + if (test_bit(ATH_OP_HW_RESET, &common->op_flags) || - list_empty(&txq->axq_acq)) + list_empty(ac_list)) return; + spin_lock_bh(&sc->chan_lock); rcu_read_lock(); - last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list); - while (!list_empty(&txq->axq_acq)) { + last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list); + while (!list_empty(ac_list)) { bool stop = false; - ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list); + if (sc->cur_chan->stopped) + break; + + ac = list_first_entry(ac_list, struct ath_atx_ac, list); last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list); list_del(&ac->list); ac->sched = false; @@ -1844,7 +1874,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) * are pending for the tid */ if (ath_tid_has_buffered(tid)) - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); if (stop || tid == last_tid) break; @@ -1852,7 +1882,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) if (!list_empty(&ac->tid_q) && !ac->sched) { ac->sched = true; - list_add_tail(&ac->list, &txq->axq_acq); + list_add_tail(&ac->list, ac_list); } if (stop) @@ -1863,12 +1893,27 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) break; sent = false; - last_ac = list_entry(txq->axq_acq.prev, + last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list); } } rcu_read_unlock(); + spin_unlock_bh(&sc->chan_lock); +} + +void ath_txq_schedule_all(struct ath_softc *sc) +{ + struct ath_txq *txq; + int i; + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + txq = sc->tx.txq_map[i]; + + spin_lock_bh(&txq->axq_lock); + ath_txq_schedule(sc, txq); + spin_unlock_bh(&txq->axq_lock); + } } /***********/ @@ -1999,6 +2044,7 @@ static void setup_frame_info(struct ieee80211_hw *hw, an = (struct ath_node *) sta->drv_priv; memset(fi, 0, sizeof(*fi)); + fi->txq = -1; if (hw_key) fi->keyix = hw_key->hw_key_idx; else if (an && ieee80211_is_data(hdr->frame_control) && an->ps_key > 0) @@ -2150,13 +2196,22 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; + struct ath_frame_info *fi = get_frame_info(skb); + struct ath_vif *avp = NULL; struct ath_softc *sc = hw->priv; struct ath_txq *txq = txctl->txq; struct ath_atx_tid *tid = NULL; struct ath_buf *bf; - int q; + bool queue; + int q, hw_queue; int ret; + if (vif) + avp = (void *)vif->drv_priv; + + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + txctl->force_channel = true; + ret = ath_tx_prepare(hw, skb, txctl); if (ret) return ret; @@ -2168,24 +2223,41 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, */ q = skb_get_queue_mapping(skb); + hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue; ath_txq_lock(sc, txq); - if (txq == sc->tx.txq_map[q] && - ++txq->pending_frames > sc->tx.txq_max_pending[q] && - !txq->stopped) { - ieee80211_stop_queue(sc->hw, q); - txq->stopped = true; + if (txq == sc->tx.txq_map[q]) { + fi->txq = q; + if (++txq->pending_frames > sc->tx.txq_max_pending[q] && + !txq->stopped) { + ieee80211_stop_queue(sc->hw, hw_queue); + txq->stopped = true; + } + } + + queue = ieee80211_is_data_present(hdr->frame_control); + + /* Force queueing of all frames that belong to a virtual interface on + * a different channel context, to ensure that they are sent on the + * correct channel. + */ + if (((avp && avp->chanctx != sc->cur_chan) || + sc->cur_chan->stopped) && !txctl->force_channel) { + if (!txctl->an) + txctl->an = &avp->mcast_node; + info->flags &= ~IEEE80211_TX_CTL_PS_RESPONSE; + queue = true; } - if (txctl->an && ieee80211_is_data_present(hdr->frame_control)) + if (txctl->an && queue) tid = ath_get_skb_tid(sc, txctl->an, skb); - if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) { + if (info->flags & (IEEE80211_TX_CTL_PS_RESPONSE | + IEEE80211_TX_CTL_TX_OFFCHAN)) { ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); - } else if (txctl->an && - ieee80211_is_data_present(hdr->frame_control)) { + } else if (txctl->an && queue) { WARN_ON(tid->ac->txq != txctl->txq); if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) @@ -2198,7 +2270,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, TX_STAT_INC(txq->axq_qnum, a_queued_sw); __skb_queue_tail(&tid->buf_q, skb); if (!txctl->an->sleeping) - ath_tx_queue_tid(txq, tid); + ath_tx_queue_tid(sc, txq, tid); ath_txq_schedule(sc, txq); goto out; @@ -2244,8 +2316,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int max_duration; max_duration = - sc->cur_beacon_conf.beacon_interval * 1000 * - sc->cur_beacon_conf.dtim_period / ATH_BCBUF; + sc->cur_chan->beacon.beacon_interval * 1000 * + sc->cur_chan->beacon.dtim_period / ATH_BCBUF; do { struct ath_frame_info *fi = get_frame_info(skb); @@ -2560,6 +2632,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) sc->beacon.tx_processed = true; sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); + ath_chanctx_event(sc, NULL, + ATH_CHANCTX_EVENT_BEACON_SENT); ath9k_csa_update(sc); continue; } |