diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k')
26 files changed, 2283 insertions, 982 deletions
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 8fcc029a76a6..896e63281b3b 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -92,6 +92,15 @@ config ATH9K_DFS_CERTIFIED developed. At this point enabling this option won't do anything except increase code size. +config ATH9K_DYNACK + bool "Atheros ath9k ACK timeout estimation algorithm (EXPERIMENTAL)" + depends on ATH9K + default n + ---help--- + This option enables ath9k dynamic ACK timeout estimation algorithm + based on ACK frame RX timestamp, TX frame timestamp and frame + duration + config ATH9K_TX99 bool "Atheros ath9k TX99 testing support" depends on ATH9K_DEBUGFS && CFG80211_CERTIFICATION_ONUS @@ -130,6 +139,15 @@ config ATH9K_RFKILL seconds. Turn off to save power, but enable it if you have a platform that can toggle the RF-Kill GPIO. +config ATH9K_CHANNEL_CONTEXT + bool "Channel Context support" + depends on ATH9K + default n + ---help--- + This option enables channel context support in ath9k, which is needed + for multi-channel concurrency. Enable this if P2P PowerSave support + is required. + config ATH9K_HTC tristate "Atheros HTC based wireless cards support" depends on USB && MAC80211 diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 6b4020a57984..73704c1be736 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -49,6 +49,9 @@ ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \ ar9003_mci.o + +ath9k_hw-$(CONFIG_ATH9K_DYNACK) += dynack.o + obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 00fb8badbacc..b72d0be716db 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -1004,9 +1004,11 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, case ATH9K_ANI_FIRSTEP_LEVEL:{ u32 level = param; - value = level; + value = level * 2; REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, value); + REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW, + AR_PHY_FIND_SIG_FIRSTEP_LOW, value); if (level != aniState->firstepLevel) { ath_dbg(common, ANI, @@ -1040,9 +1042,8 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, value); - if (IS_CHAN_HT40(ah->curchan)) - REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, - AR_PHY_EXT_TIMING5_CYCPWR_THR1, value); + REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, + AR_PHY_EXT_TIMING5_CYCPWR_THR1, value - 1); if (level != aniState->spurImmunityLevel) { ath_dbg(common, ANI, diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index 59af9f9712da..2a93519f4bdf 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -384,6 +384,24 @@ static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds, return 0; } +static int ar9002_hw_get_duration(struct ath_hw *ah, const void *ds, int index) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + switch (index) { + case 0: + return MS(ACCESS_ONCE(ads->ds_ctl4), AR_PacketDur0); + case 1: + return MS(ACCESS_ONCE(ads->ds_ctl4), AR_PacketDur1); + case 2: + return MS(ACCESS_ONCE(ads->ds_ctl5), AR_PacketDur2); + case 3: + return MS(ACCESS_ONCE(ads->ds_ctl5), AR_PacketDur3); + default: + return -1; + } +} + void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags) { @@ -406,4 +424,5 @@ void ar9002_hw_attach_mac_ops(struct ath_hw *ah) ops->get_isr = ar9002_hw_get_isr; ops->set_txdesc = ar9002_set_txdesc; ops->proc_txdesc = ar9002_hw_proc_txdesc; + ops->get_duration = ar9002_hw_get_duration; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 71e38e85aa99..057b1657c428 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -431,6 +431,24 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds, return 0; } +static int ar9003_hw_get_duration(struct ath_hw *ah, const void *ds, int index) +{ + const struct ar9003_txc *adc = ds; + + switch (index) { + case 0: + return MS(ACCESS_ONCE(adc->ctl15), AR_PacketDur0); + case 1: + return MS(ACCESS_ONCE(adc->ctl15), AR_PacketDur1); + case 2: + return MS(ACCESS_ONCE(adc->ctl16), AR_PacketDur2); + case 3: + return MS(ACCESS_ONCE(adc->ctl16), AR_PacketDur3); + default: + return 0; + } +} + void ar9003_hw_attach_mac_ops(struct ath_hw *hw) { struct ath_hw_ops *ops = ath9k_hw_ops(hw); @@ -440,6 +458,7 @@ void ar9003_hw_attach_mac_ops(struct ath_hw *hw) ops->get_isr = ar9003_hw_get_isr; ops->set_txdesc = ar9003_set_txdesc; ops->proc_txdesc = ar9003_hw_proc_txdesc; + ops->get_duration = ar9003_hw_get_duration; } void ath9k_hw_set_rx_bufsize(struct ath_hw *ah, u16 buf_size) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 542a8d51d3b0..697c4ae90af0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -517,6 +517,23 @@ static void ar9003_hw_spur_mitigate(struct ath_hw *ah, ar9003_hw_spur_mitigate_ofdm(ah, chan); } +static u32 ar9003_hw_compute_pll_control_soc(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + u32 pll; + + pll = SM(0x5, AR_RTC_9300_SOC_PLL_REFDIV); + + if (chan && IS_CHAN_HALF_RATE(chan)) + pll |= SM(0x1, AR_RTC_9300_SOC_PLL_CLKSEL); + else if (chan && IS_CHAN_QUARTER_RATE(chan)) + pll |= SM(0x2, AR_RTC_9300_SOC_PLL_CLKSEL); + + pll |= SM(0x2c, AR_RTC_9300_SOC_PLL_DIV_INT); + + return pll; +} + static u32 ar9003_hw_compute_pll_control(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1781,7 +1798,12 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) priv_ops->rf_set_freq = ar9003_hw_set_channel; priv_ops->spur_mitigate_freq = ar9003_hw_spur_mitigate; - priv_ops->compute_pll_control = ar9003_hw_compute_pll_control; + + if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) + priv_ops->compute_pll_control = ar9003_hw_compute_pll_control_soc; + else + priv_ops->compute_pll_control = ar9003_hw_compute_pll_control; + priv_ops->set_channel_regs = ar9003_hw_set_channel_regs; priv_ops->init_bb = ar9003_hw_init_bb; priv_ops->process_ini = ar9003_hw_process_ini; diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 7fc13a8da675..bfa0b1518da1 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -31,6 +31,7 @@ #include "spectral.h" struct ath_node; +struct ath_vif; extern struct ieee80211_ops ath9k_ops; extern int ath9k_modparam_nohwcrypt; @@ -273,6 +274,9 @@ struct ath_node { struct ath_rx_rate_stats rx_rate_stats; #endif u8 key_idx[4]; + + u32 ackto; + struct list_head list; }; struct ath_tx_control { @@ -313,7 +317,6 @@ struct ath_rx { bool discard_next; u32 *rxlink; u32 num_pkts; - unsigned int rxfilter; struct list_head rxbuf; struct ath_descdma rxdma; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; @@ -324,6 +327,10 @@ struct ath_rx { u32 ampdu_ref; }; +/*******************/ +/* Channel Context */ +/*******************/ + struct ath_chanctx { struct cfg80211_chan_def chandef; struct list_head vifs; @@ -345,6 +352,10 @@ struct ath_chanctx { bool active; bool assigned; bool switch_after_beacon; + + short nvifs; + short nvifs_assigned; + unsigned int rxfilter; }; enum ath_chanctx_event { @@ -354,7 +365,9 @@ enum ath_chanctx_event { ATH_CHANCTX_EVENT_BEACON_RECEIVED, ATH_CHANCTX_EVENT_ASSOC, ATH_CHANCTX_EVENT_SWITCH, + ATH_CHANCTX_EVENT_ASSIGN, ATH_CHANCTX_EVENT_UNASSIGN, + ATH_CHANCTX_EVENT_CHANGE, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL, }; @@ -369,6 +382,9 @@ enum ath_chanctx_state { struct ath_chanctx_sched { bool beacon_pending; bool offchannel_pending; + bool wait_switch; + bool force_noa_update; + bool extend_absence; enum ath_chanctx_state state; u8 beacon_miss; @@ -403,38 +419,130 @@ struct ath_offchannel { int roc_duration; int duration; }; + +#define case_rtn_string(val) case val: return #val + #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); +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); + +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + 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); + +bool ath9k_is_chanctx_enabled(void); +void ath9k_fill_chanctx_ops(void); +void ath9k_init_channel_context(struct ath_softc *sc); +void ath9k_offchannel_init(struct ath_softc *sc); +void ath9k_deinit_channel_context(struct ath_softc *sc); +int ath9k_init_p2p(struct ath_softc *sc); +void ath9k_deinit_p2p(struct ath_softc *sc); +void ath9k_p2p_remove_vif(struct ath_softc *sc, + struct ieee80211_vif *vif); +void ath9k_p2p_beacon_sync(struct ath_softc *sc); +void ath9k_p2p_bss_info_changed(struct ath_softc *sc, + struct ieee80211_vif *vif); +void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, + struct sk_buff *skb); +void ath9k_p2p_ps_timer(void *priv); +void ath9k_chanctx_wake_queues(struct ath_softc *sc, struct ath_chanctx *ctx); +void ath9k_chanctx_stop_queues(struct ath_softc *sc, struct ath_chanctx *ctx); 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_beacon_recv_ev(struct ath_softc *sc, + enum ath_chanctx_event ev); +void ath_chanctx_beacon_sent_ev(struct ath_softc *sc, + enum ath_chanctx_event ev); void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, enum ath_chanctx_event ev); -void ath_chanctx_timer(unsigned long data); +void ath_chanctx_set_next(struct ath_softc *sc, bool force); +void ath_offchannel_next(struct ath_softc *sc); +void ath_scan_complete(struct ath_softc *sc, bool abort); +void ath_roc_complete(struct ath_softc *sc, bool abort); + +#else + +static inline bool ath9k_is_chanctx_enabled(void) +{ + return false; +} +static inline void ath9k_fill_chanctx_ops(void) +{ +} +static inline void ath9k_init_channel_context(struct ath_softc *sc) +{ +} +static inline void ath9k_offchannel_init(struct ath_softc *sc) +{ +} +static inline void ath9k_deinit_channel_context(struct ath_softc *sc) +{ +} +static inline void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, + enum ath_chanctx_event ev) +{ +} +static inline void ath_chanctx_beacon_sent_ev(struct ath_softc *sc, + enum ath_chanctx_event ev) +{ +} +static inline void ath_chanctx_event(struct ath_softc *sc, + struct ieee80211_vif *vif, + enum ath_chanctx_event ev) +{ +} +static inline int ath9k_init_p2p(struct ath_softc *sc) +{ + return 0; +} +static inline void ath9k_deinit_p2p(struct ath_softc *sc) +{ +} +static inline void ath9k_p2p_remove_vif(struct ath_softc *sc, + struct ieee80211_vif *vif) +{ +} +static inline void ath9k_p2p_beacon_sync(struct ath_softc *sc) +{ +} +static inline void ath9k_p2p_bss_info_changed(struct ath_softc *sc, + struct ieee80211_vif *vif) +{ +} +static inline void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, + struct sk_buff *skb) +{ +} +static inline void ath9k_p2p_ps_timer(struct ath_softc *sc) +{ +} +static inline void ath9k_chanctx_wake_queues(struct ath_softc *sc, + struct ath_chanctx *ctx) +{ +} +static inline void ath9k_chanctx_stop_queues(struct ath_softc *sc, + struct ath_chanctx *ctx) +{ +} +static inline void ath_chanctx_check_active(struct ath_softc *sc, + struct ath_chanctx *ctx) +{ +} + +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); -int ath_startrecv(struct ath_softc *sc); +void ath_startrecv(struct ath_softc *sc); bool ath_stoprecv(struct ath_softc *sc); u32 ath_calcrxfilter(struct ath_softc *sc); int ath_rx_init(struct ath_softc *sc, int nbufs); @@ -479,9 +587,16 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, /* VIFs */ /********/ +#define P2P_DEFAULT_CTWIN 10 + struct ath_vif { struct list_head list; + /* BSS info */ + u8 bssid[ETH_ALEN]; + u16 aid; + bool assoc; + struct ieee80211_vif *vif; struct ath_node mcast_node; int av_bslot; @@ -497,8 +612,10 @@ struct ath_vif { u32 offchannel_start; u32 offchannel_duration; - u32 periodic_noa_start; - u32 periodic_noa_duration; + /* These are used for both periodic and one-shot */ + u32 noa_start; + u32 noa_duration; + bool periodic_noa; }; struct ath9k_vif_iter_data { @@ -583,7 +700,6 @@ 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); @@ -597,8 +713,6 @@ int ath_update_survey_stats(struct ath_softc *sc); void ath_update_survey_nf(struct ath_softc *sc, int channel); 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); /**********/ @@ -849,12 +963,17 @@ 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; +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + struct work_struct chanctx_work; struct ath_gen_timer *p2p_ps_timer; struct ath_vif *p2p_ps_vif; + struct ath_chanctx_sched sched; + struct ath_offchannel offchannel; + struct ath_chanctx *next_chan; +#endif unsigned long driver_data; @@ -865,7 +984,6 @@ struct ath_softc { bool ps_enabled; bool ps_idle; short nbcnvifs; - short nvifs; unsigned long ps_usecount; struct ath_rx rx; @@ -875,10 +993,7 @@ struct ath_softc { 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; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index eaf8f058c151..a6af855ef6ed 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -108,55 +108,6 @@ 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) { @@ -232,7 +183,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, spin_unlock_bh(&cabq->axq_lock); if (skb && cabq_depth) { - if (sc->nvifs > 1) { + if (sc->cur_chan->nvifs > 1) { ath_dbg(common, BEACON, "Flushing previous cabq traffic\n"); ath_draintxq(sc, cabq); @@ -427,9 +378,10 @@ void ath9k_beacon_tasklet(unsigned long data) /* EDMA devices check that in the tx completion function. */ if (!edma) { - if (sc->sched.beacon_pending) - ath_chanctx_event(sc, NULL, + if (ath9k_is_chanctx_enabled()) { + ath_chanctx_beacon_sent_ev(sc, ATH_CHANCTX_EVENT_BEACON_SENT); + } if (ath9k_csa_is_finished(sc, vif)) return; @@ -438,7 +390,10 @@ void ath9k_beacon_tasklet(unsigned long data) if (!vif || !vif->bss_conf.enable_beacon) return; - ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE); + if (ath9k_is_chanctx_enabled()) { + ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE); + } + bf = ath9k_beacon_generate(sc->hw, vif); if (sc->beacon.bmisscnt != 0) { @@ -559,6 +514,18 @@ 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 (ath9k_is_chanctx_enabled()) { + /* + * If the VIF is not present in the current channel context, + * then we can't do the usual opmode checks. Allow the + * beacon config for the VIF to be updated in this case and + * return immediately. + */ + if (sc->cur_chan != avp->chanctx) + return true; + } if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { if ((vif->type != NL80211_IFTYPE_AP) || diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index ba214ebdcd16..945c89826b14 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -83,8 +83,6 @@ static int ath_set_channel(struct ath_softc *sc) 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; @@ -101,202 +99,6 @@ static int ath_set_channel(struct ath_softc *sc) 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; @@ -318,115 +120,124 @@ void ath_chanctx_init(struct ath_softc *sc) 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) +void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, + struct cfg80211_chan_def *chandef) { - 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); + bool cur_chan; 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; + if (chandef) + memcpy(&ctx->chandef, chandef, sizeof(*chandef)); + cur_chan = sc->cur_chan == ctx; spin_unlock_bh(&sc->chan_lock); - if (changed) - ath_chanctx_set_next(sc, true); + if (!cur_chan) { + ath_dbg(common, CHAN_CTX, + "Current context differs from the new context\n"); + return; + } - mutex_unlock(&sc->mutex); + ath_set_channel(sc); } -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); +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT - spin_lock_bh(&sc->chan_lock); +/**********************************************************/ +/* Functions to handle the channel context state machine. */ +/**********************************************************/ - 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; +static const char *offchannel_state_string(enum ath_offchannel_state state) +{ + switch (state) { + case_rtn_string(ATH_OFFCHANNEL_IDLE); + case_rtn_string(ATH_OFFCHANNEL_PROBE_SEND); + case_rtn_string(ATH_OFFCHANNEL_PROBE_WAIT); + case_rtn_string(ATH_OFFCHANNEL_SUSPEND); + case_rtn_string(ATH_OFFCHANNEL_ROC_START); + case_rtn_string(ATH_OFFCHANNEL_ROC_WAIT); + case_rtn_string(ATH_OFFCHANNEL_ROC_DONE); + default: + return "unknown"; } +} - 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; +static const char *chanctx_event_string(enum ath_chanctx_event ev) +{ + switch (ev) { + case_rtn_string(ATH_CHANCTX_EVENT_BEACON_PREPARE); + case_rtn_string(ATH_CHANCTX_EVENT_BEACON_SENT); + case_rtn_string(ATH_CHANCTX_EVENT_TSF_TIMER); + case_rtn_string(ATH_CHANCTX_EVENT_BEACON_RECEIVED); + case_rtn_string(ATH_CHANCTX_EVENT_ASSOC); + case_rtn_string(ATH_CHANCTX_EVENT_SWITCH); + case_rtn_string(ATH_CHANCTX_EVENT_ASSIGN); + case_rtn_string(ATH_CHANCTX_EVENT_UNASSIGN); + case_rtn_string(ATH_CHANCTX_EVENT_CHANGE); + case_rtn_string(ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); + default: + return "unknown"; } - 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) +static const char *chanctx_state_string(enum ath_chanctx_state state) { - bool cur_chan; + switch (state) { + case_rtn_string(ATH_CHANCTX_STATE_IDLE); + case_rtn_string(ATH_CHANCTX_STATE_WAIT_FOR_BEACON); + case_rtn_string(ATH_CHANCTX_STATE_WAIT_FOR_TIMER); + case_rtn_string(ATH_CHANCTX_STATE_SWITCH); + case_rtn_string(ATH_CHANCTX_STATE_FORCE_ACTIVE); + default: + return "unknown"; + } +} - 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); +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 (!cur_chan) + if (!ctx) return; - ath_set_channel(sc); -} + list_for_each_entry(avp, &ctx->vifs, list) { + struct ieee80211_vif *vif = avp->vif; -struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active) -{ - struct ath_chanctx *ctx; + switch (vif->type) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + if (avp->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; - if (active && !ctx->active) - continue; - - if (ctx->switch_after_beacon) - return ctx; + n_active++; } - 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); + 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_switch(sc, &sc->offchannel.chan, &chandef); + if (ath9k_is_chanctx_enabled()) { + ath_chanctx_event(sc, NULL, + ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); + } } static struct ath_chanctx * @@ -449,6 +260,9 @@ static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) cur = sc->cur_chan; prev = ath_chanctx_get_next(sc, cur); + if (!prev->switch_after_beacon) + return; + getrawmonotonic(&ts); cur_tsf = (u32) cur->tsf_val + ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts); @@ -469,25 +283,22 @@ static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) 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_common *common = ath9k_hw_common(sc->sc_ah); 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); + mod_timer(&sc->sched.timer, jiffies + tsf_time); + + ath_dbg(common, CHAN_CTX, + "Setup chanctx timer with timeout: %d ms\n", jiffies_to_msecs(tsf_time)); } void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, @@ -500,40 +311,56 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, 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); + ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s\n", + sc->cur_chan->chandef.center_freq1, + chanctx_event_string(ev), + chanctx_state_string(sc->sched.state)); + switch (ev) { case ATH_CHANCTX_EVENT_BEACON_PREPARE: if (avp->offchannel_duration) avp->offchannel_duration = 0; - if (avp->chanctx != sc->cur_chan) + if (avp->chanctx != sc->cur_chan) { + ath_dbg(common, CHAN_CTX, + "Contexts differ, not preparing beacon\n"); break; + } - if (sc->sched.offchannel_pending) { + if (sc->sched.offchannel_pending && !sc->sched.wait_switch) { sc->sched.offchannel_pending = false; sc->next_chan = &sc->offchannel.chan; sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + ath_dbg(common, CHAN_CTX, + "Setting offchannel_pending to false\n"); } 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; + ath_dbg(common, CHAN_CTX, + "Set next context, move chanctx state to WAIT_FOR_BEACON\n"); } /* if the timer missed its window, use the next interval */ - if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) + if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) { sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + ath_dbg(common, CHAN_CTX, + "Move chanctx state from WAIT_FOR_TIMER to WAIT_FOR_BEACON\n"); + } if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) break; + ath_dbg(common, CHAN_CTX, "Preparing beacon for vif: %pM\n", vif->addr); + sc->sched.beacon_pending = true; sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); @@ -545,47 +372,107 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, 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 an offchannel switch is scheduled to happen after + * a beacon transmission, update the NoA with one-shot + * values and increment the index. + */ + if (sc->next_chan == &sc->offchannel.chan) { + avp->noa_index++; + avp->offchannel_start = tsf_time; + avp->offchannel_duration = sc->sched.offchannel_duration; + + ath_dbg(common, CHAN_CTX, + "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n", + avp->offchannel_duration, + avp->offchannel_start, + avp->noa_index); + + /* + * When multiple contexts are active, the NoA + * has to be recalculated and advertised after + * an offchannel operation. + */ + if (ctx->active && avp->noa_duration) + avp->noa_duration = 0; + + break; + } + + /* + * Clear the extend_absence flag if it had been + * set during the previous beacon transmission, + * since we need to revert to the normal NoA + * schedule. + */ + if (ctx->active && sc->sched.extend_absence) { + avp->noa_duration = 0; + sc->sched.extend_absence = false; } /* 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 (ctx->active && sc->sched.beacon_miss >= 2) { + avp->noa_duration = 0; + sc->sched.extend_absence = true; } - if (noa_changed) + /* Prevent wrap-around issues */ + if (avp->noa_duration && tsf_time - avp->noa_start > BIT(30)) + avp->noa_duration = 0; + + /* + * If multiple contexts are active, start periodic + * NoA and increment the index for the first + * announcement. + */ + if (ctx->active && + (!avp->noa_duration || sc->sched.force_noa_update)) { avp->noa_index++; + avp->noa_start = tsf_time; + + if (sc->sched.extend_absence) + avp->noa_duration = (3 * beacon_int / 2) + + sc->sched.channel_switch_time; + else + avp->noa_duration = + TU_TO_USEC(cur_conf->beacon_interval) / 2 + + sc->sched.channel_switch_time; + + if (test_bit(ATH_OP_SCANNING, &common->op_flags) || + sc->sched.extend_absence) + avp->periodic_noa = false; + else + avp->periodic_noa = true; + + ath_dbg(common, CHAN_CTX, + "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n", + avp->noa_duration, + avp->noa_start, + avp->noa_index, + avp->periodic_noa); + } + + if (ctx->active && sc->sched.force_noa_update) + sc->sched.force_noa_update = false; + break; case ATH_CHANCTX_EVENT_BEACON_SENT: - if (!sc->sched.beacon_pending) + if (!sc->sched.beacon_pending) { + ath_dbg(common, CHAN_CTX, + "No pending beacon\n"); break; + } sc->sched.beacon_pending = false; if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) break; + ath_dbg(common, CHAN_CTX, + "Move chanctx state to WAIT_FOR_TIMER\n"); + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; ath_chanctx_setup_timer(sc, sc->sched.switch_start_time); break; @@ -597,6 +484,9 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, sc->sched.beacon_pending) sc->sched.beacon_miss++; + ath_dbg(common, CHAN_CTX, + "Move chanctx state to SWITCH\n"); + sc->sched.state = ATH_CHANCTX_STATE_SWITCH; ieee80211_queue_work(sc->hw, &sc->chanctx_work); break; @@ -625,6 +515,9 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, avp->chanctx != sc->cur_chan) break; + ath_dbg(common, CHAN_CTX, + "Move chanctx state from FORCE_ACTIVE to IDLE\n"); + sc->sched.state = ATH_CHANCTX_STATE_IDLE; /* fall through */ case ATH_CHANCTX_EVENT_SWITCH: @@ -640,10 +533,15 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); cur_conf = &sc->cur_chan->beacon; + ath_dbg(common, CHAN_CTX, + "Move chanctx state to WAIT_FOR_TIMER (event SWITCH)\n"); + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; + sc->sched.wait_switch = false; tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2; - if (sc->sched.beacon_miss >= 2) { + + if (sc->sched.extend_absence) { sc->sched.beacon_miss = 0; tsf_time *= 3; } @@ -679,7 +577,874 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, sc->next_chan = ctx; ieee80211_queue_work(sc->hw, &sc->chanctx_work); break; + case ATH_CHANCTX_EVENT_ASSIGN: + /* + * When adding a new channel context, check if a scan + * is in progress and abort it since the addition of + * a new channel context is usually followed by VIF + * assignment, in which case we have to start multi-channel + * operation. + */ + if (test_bit(ATH_OP_SCANNING, &common->op_flags)) { + ath_dbg(common, CHAN_CTX, + "Aborting HW scan to add new context\n"); + + spin_unlock_bh(&sc->chan_lock); + del_timer_sync(&sc->offchannel.timer); + ath_scan_complete(sc, true); + spin_lock_bh(&sc->chan_lock); + } + break; + case ATH_CHANCTX_EVENT_CHANGE: + break; + } + + spin_unlock_bh(&sc->chan_lock); +} + +void ath_chanctx_beacon_sent_ev(struct ath_softc *sc, + enum ath_chanctx_event ev) +{ + if (sc->sched.beacon_pending) + ath_chanctx_event(sc, NULL, ev); +} + +void ath_chanctx_beacon_recv_ev(struct ath_softc *sc, + enum ath_chanctx_event ev) +{ + ath_chanctx_event(sc, NULL, ev); +} + +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_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)) { + if (chandef) + ctx->chandef = *chandef; + + sc->sched.offchannel_pending = true; + sc->sched.wait_switch = true; + sc->sched.offchannel_duration = + jiffies_to_usecs(sc->offchannel.duration) + + sc->sched.channel_switch_time; + + spin_unlock_bh(&sc->chan_lock); + ath_dbg(common, CHAN_CTX, + "Set offchannel_pending to true\n"); + return; + } + + sc->next_chan = ctx; + if (chandef) { + ctx->chandef = *chandef; + ath_dbg(common, CHAN_CTX, + "Assigned next_chan to %d MHz\n", chandef->center_freq1); + } + + if (sc->next_chan == &sc->offchannel.chan) { + sc->sched.offchannel_duration = + jiffies_to_usecs(sc->offchannel.duration) + + sc->sched.channel_switch_time; + + if (chandef) { + ath_dbg(common, CHAN_CTX, + "Offchannel duration for chan %d MHz : %u\n", + chandef->center_freq1, + sc->sched.offchannel_duration); + } + } + spin_unlock_bh(&sc->chan_lock); + ieee80211_queue_work(sc->hw, &sc->chanctx_work); +} + +static void ath_chanctx_offchan_switch(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct cfg80211_chan_def chandef; + + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + ath_dbg(common, CHAN_CTX, + "Channel definition created: %d MHz\n", chandef.center_freq1); + + ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef); +} + +static 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]; +} + +static void +ath_scan_next_channel(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct cfg80211_scan_request *req = sc->offchannel.scan_req; + struct ieee80211_channel *chan; + + if (sc->offchannel.scan_idx >= req->n_channels) { + ath_dbg(common, CHAN_CTX, + "Moving offchannel state to ATH_OFFCHANNEL_IDLE, " + "scan_idx: %d, n_channels: %d\n", + 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; + } + + ath_dbg(common, CHAN_CTX, + "Moving offchannel state to ATH_OFFCHANNEL_PROBE_SEND, scan_idx: %d\n", + sc->offchannel.scan_idx); + + 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); +} + +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 = + msecs_to_jiffies(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); + } +} + +void ath_roc_complete(struct ath_softc *sc, bool abort) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + if (abort) + ath_dbg(common, CHAN_CTX, "RoC aborted\n"); + else + ath_dbg(common, CHAN_CTX, "RoC expired\n"); + + 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); +} + +void ath_scan_complete(struct ath_softc *sc, bool abort) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + if (abort) + ath_dbg(common, CHAN_CTX, "HW scan aborted\n"); + else + ath_dbg(common, CHAN_CTX, "HW scan complete\n"); + + 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); + spin_lock_bh(&sc->chan_lock); + if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) + sc->sched.force_noa_update = true; + spin_unlock_bh(&sc->chan_lock); + 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 ath_common *common = ath9k_hw_common(sc->sc_ah); + 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]); + + } + + ath_dbg(common, CHAN_CTX, + "Moving offchannel state to ATH_OFFCHANNEL_PROBE_WAIT\n"); + + sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT; + mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration); +} + +static void ath_chanctx_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_dbg(common, CHAN_CTX, + "Channel context timer invoked\n"); + + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); +} + +static void ath_offchannel_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_chanctx *ctx; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_dbg(common, CHAN_CTX, "%s: offchannel state: %s\n", + __func__, offchannel_state_string(sc->offchannel.state)); + + 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) { + ath_dbg(common, CHAN_CTX, + "Switch to oper/active context, " + "move offchannel state to ATH_OFFCHANNEL_SUSPEND\n"); + + 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 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 (!avp->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; +} + +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) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + 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; + + ath_dbg(common, CHAN_CTX, + "Defer switch, set chanctx state to WAIT_FOR_BEACON\n"); + + sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; + break; + default: + break; + } + + return true; +} + +static void ath_offchannel_channel_change(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_dbg(common, CHAN_CTX, "%s: offchannel state: %s\n", + __func__, offchannel_state_string(sc->offchannel.state)); + + 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 + 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_chanctx_set_next(struct ath_softc *sc, bool force) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_chanctx *old_ctx; + struct timespec ts; + bool measure_time = false; + bool send_ps = false; + bool queues_stopped = 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; + } + + ath_dbg(common, CHAN_CTX, + "%s: current: %d MHz, next: %d MHz\n", + __func__, + sc->cur_chan->chandef.center_freq1, + sc->next_chan->chandef.center_freq1); + + if (sc->cur_chan != sc->next_chan) { + ath_dbg(common, CHAN_CTX, + "Stopping current chanctx: %d\n", + sc->cur_chan->chandef.center_freq1); + sc->cur_chan->stopped = true; + spin_unlock_bh(&sc->chan_lock); + + if (sc->next_chan == &sc->offchannel.chan) { + getrawmonotonic(&ts); + measure_time = true; + } + + ath9k_chanctx_stop_queues(sc, sc->cur_chan); + queues_stopped = 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); + } } + old_ctx = sc->cur_chan; + sc->cur_chan = sc->next_chan; + sc->cur_chan->stopped = false; + sc->next_chan = NULL; + + if (!sc->sched.offchannel_pending) + 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_dbg(common, CHAN_CTX, + "%s: Set channel %d MHz\n", + __func__, sc->cur_chan->chandef.center_freq1); + ath_set_channel(sc); + if (measure_time) + sc->sched.channel_switch_time = + ath9k_hw_get_tsf_offset(&ts, NULL); + /* + * A reset will ensure that all queues are woken up, + * so there is no need to awaken them again. + */ + goto out; + } + + if (queues_stopped) + ath9k_chanctx_wake_queues(sc, old_ctx); +out: + if (send_ps) + ath_chanctx_send_ps_frame(sc, false); + + ath_offchannel_channel_change(sc); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH); } + +static 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 ath9k_offchannel_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; + + sband = &common->sbands[IEEE80211_BAND_2GHZ]; + if (!sband->n_channels) + sband = &common->sbands[IEEE80211_BAND_5GHZ]; + + chan = &sband->channels[0]; + + ctx = &sc->offchannel.chan; + INIT_LIST_HEAD(&ctx->vifs); + ctx->txpower = ATH_TXPOWER_MAX; + cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); + + for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) + INIT_LIST_HEAD(&ctx->acq[i]); + + sc->offchannel.chan.offchannel = true; +} + +void ath9k_init_channel_context(struct ath_softc *sc) +{ + INIT_WORK(&sc->chanctx_work, ath_chanctx_work); + + setup_timer(&sc->offchannel.timer, ath_offchannel_timer, + (unsigned long)sc); + setup_timer(&sc->sched.timer, ath_chanctx_timer, + (unsigned long)sc); +} + +void ath9k_deinit_channel_context(struct ath_softc *sc) +{ + cancel_work_sync(&sc->chanctx_work); +} + +bool ath9k_is_chanctx_enabled(void) +{ + return (ath9k_use_chanctx == 1); +} + +/********************/ +/* Queue management */ +/********************/ + +void ath9k_chanctx_stop_queues(struct ath_softc *sc, struct ath_chanctx *ctx) +{ + struct ath_hw *ah = sc->sc_ah; + int i; + + if (ctx == &sc->offchannel.chan) { + ieee80211_stop_queue(sc->hw, + sc->hw->offchannel_tx_hw_queue); + } else { + for (i = 0; i < IEEE80211_NUM_ACS; i++) + ieee80211_stop_queue(sc->hw, + ctx->hw_queue_base + i); + } + + if (ah->opmode == NL80211_IFTYPE_AP) + ieee80211_stop_queue(sc->hw, sc->hw->queues - 2); +} + + +void ath9k_chanctx_wake_queues(struct ath_softc *sc, struct ath_chanctx *ctx) +{ + struct ath_hw *ah = sc->sc_ah; + int i; + + if (ctx == &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, + ctx->hw_queue_base + i); + } + + if (ah->opmode == NL80211_IFTYPE_AP) + ieee80211_wake_queue(sc->hw, sc->hw->queues - 2); +} + +/*****************/ +/* P2P Powersave */ +/*****************/ + +static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) +{ + struct ath_hw *ah = sc->sc_ah; + s32 tsf, target_tsf; + + if (!avp || !avp->noa.has_next_tsf) + return; + + ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer); + + tsf = ath9k_hw_gettsf32(sc->sc_ah); + + target_tsf = avp->noa.next_tsf; + if (!avp->noa.absent) + target_tsf -= ATH_P2P_PS_STOP_TIME; + + if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) + target_tsf = tsf + ATH_P2P_PS_STOP_TIME; + + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); +} + +static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) +{ + struct ath_vif *avp = (void *)vif->drv_priv; + u32 tsf; + + if (!sc->p2p_ps_timer) + return; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p) + return; + + sc->p2p_ps_vif = avp; + tsf = ath9k_hw_gettsf32(sc->sc_ah); + ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); + ath9k_update_p2p_ps_timer(sc, avp); +} + +static u8 ath9k_get_ctwin(struct ath_softc *sc, struct ath_vif *avp) +{ + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; + u8 switch_time, ctwin; + + /* + * Channel switch in multi-channel mode is deferred + * by a quarter beacon interval when handling + * ATH_CHANCTX_EVENT_BEACON_PREPARE, so the P2P-GO + * interface is guaranteed to be discoverable + * for that duration after a TBTT. + */ + switch_time = cur_conf->beacon_interval / 4; + + ctwin = avp->vif->bss_conf.p2p_noa_attr.oppps_ctwindow; + if (ctwin && (ctwin < switch_time)) + return ctwin; + + if (switch_time < P2P_DEFAULT_CTWIN) + return 0; + + return P2P_DEFAULT_CTWIN; +} + +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->noa_duration) + return; + + noa_desc = !!avp->offchannel_duration + !!avp->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; + noa->oppps_ctwindow = ath9k_get_ctwin(sc, avp); + + if (avp->noa_duration) { + if (avp->periodic_noa) { + u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); + noa->desc[i].count = 255; + noa->desc[i].interval = cpu_to_le32(interval); + } else { + noa->desc[i].count = 1; + } + + noa->desc[i].start_time = cpu_to_le32(avp->noa_start); + noa->desc[i].duration = cpu_to_le32(avp->noa_duration); + 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); + } +} + +void ath9k_p2p_ps_timer(void *priv) +{ + struct ath_softc *sc = priv; + struct ath_vif *avp = sc->p2p_ps_vif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + struct ath_node *an; + u32 tsf; + + 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); + if (!avp->noa.absent) + tsf += ATH_P2P_PS_STOP_TIME; + + if (!avp->noa.has_next_tsf || + avp->noa.next_tsf - tsf > BIT(31)) + ieee80211_update_p2p_noa(&avp->noa, tsf); + + ath9k_update_p2p_ps_timer(sc, avp); + + rcu_read_lock(); + + vif = avp->vif; + sta = ieee80211_find_sta(vif, avp->bssid); + if (!sta) + goto out; + + an = (void *) sta->drv_priv; + if (an->sleeping == !!avp->noa.absent) + goto out; + + an->sleeping = avp->noa.absent; + if (an->sleeping) + ath_tx_aggr_sleep(sta, sc, an); + else + ath_tx_aggr_wakeup(sc, an); + +out: + rcu_read_unlock(); +} + +void ath9k_p2p_bss_info_changed(struct ath_softc *sc, + struct ieee80211_vif *vif) +{ + unsigned long flags; + + spin_lock_bh(&sc->sc_pcu_lock); + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (!(sc->ps_flags & PS_BEACON_SYNC)) + ath9k_update_p2p_ps(sc, vif); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + spin_unlock_bh(&sc->sc_pcu_lock); +} + +void ath9k_p2p_beacon_sync(struct ath_softc *sc) +{ + if (sc->p2p_ps_vif) + ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif); +} + +void ath9k_p2p_remove_vif(struct ath_softc *sc, + struct ieee80211_vif *vif) +{ + struct ath_vif *avp = (void *)vif->drv_priv; + + spin_lock_bh(&sc->sc_pcu_lock); + if (avp == sc->p2p_ps_vif) { + sc->p2p_ps_vif = NULL; + ath9k_update_p2p_ps_timer(sc, NULL); + } + spin_unlock_bh(&sc->sc_pcu_lock); +} + +int ath9k_init_p2p(struct ath_softc *sc) +{ + sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer, + NULL, sc, AR_FIRST_NDP_TIMER); + if (!sc->p2p_ps_timer) + return -ENOMEM; + + return 0; +} + +void ath9k_deinit_p2p(struct ath_softc *sc) +{ + if (sc->p2p_ps_timer) + ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer); +} + +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index d2279365be6f..46f20a309b5f 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -838,7 +838,7 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, 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); + iter_data.nadhocs, sc->cur_chan->nvifs, sc->nbcnvifs); } if (len > sizeof(buf)) @@ -1169,6 +1169,29 @@ static const struct file_operations fops_btcoex = { }; #endif +#ifdef CONFIG_ATH9K_DYNACK +static ssize_t read_file_ackto(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%u %c\n", ah->dynack.ackto, + (ah->dynack.enabled) ? 'A' : 'S'); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_ackto = { + .read = read_file_ackto, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; +#endif + /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" @@ -1374,5 +1397,10 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_btcoex); #endif +#ifdef CONFIG_ATH9K_DYNACK + debugfs_create_file("ack_to", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, + sc, &fops_ackto); +#endif + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/dynack.c b/drivers/net/wireless/ath/ath9k/dynack.c new file mode 100644 index 000000000000..22b3cc4c27cd --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/dynack.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2014, Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * 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" +#include "hw.h" +#include "dynack.h" + +#define COMPUTE_TO (5 * HZ) +#define LATEACK_DELAY (10 * HZ) +#define LATEACK_TO 256 +#define MAX_DELAY 300 +#define EWMA_LEVEL 96 +#define EWMA_DIV 128 + +/** + * ath_dynack_ewma - EWMA (Exponentially Weighted Moving Average) calculation + * + */ +static inline u32 ath_dynack_ewma(u32 old, u32 new) +{ + return (new * (EWMA_DIV - EWMA_LEVEL) + old * EWMA_LEVEL) / EWMA_DIV; +} + +/** + * ath_dynack_get_sifs - get sifs time based on phy used + * @ah: ath hw + * @phy: phy used + * + */ +static inline u32 ath_dynack_get_sifs(struct ath_hw *ah, int phy) +{ + u32 sifs = CCK_SIFS_TIME; + + if (phy == WLAN_RC_PHY_OFDM) { + if (IS_CHAN_QUARTER_RATE(ah->curchan)) + sifs = OFDM_SIFS_TIME_QUARTER; + else if (IS_CHAN_HALF_RATE(ah->curchan)) + sifs = OFDM_SIFS_TIME_HALF; + else + sifs = OFDM_SIFS_TIME; + } + return sifs; +} + +/** + * ath_dynack_bssidmask - filter out ACK frames based on BSSID mask + * @ah: ath hw + * @mac: receiver address + */ +static inline bool ath_dynack_bssidmask(struct ath_hw *ah, const u8 *mac) +{ + int i; + struct ath_common *common = ath9k_hw_common(ah); + + for (i = 0; i < ETH_ALEN; i++) { + if ((common->macaddr[i] & common->bssidmask[i]) != + (mac[i] & common->bssidmask[i])) + return false; + } + + return true; +} + +/** + * ath_dynack_compute_ackto - compute ACK timeout as the maximum STA timeout + * @ah: ath hw + * + * should be called while holding qlock + */ +static void ath_dynack_compute_ackto(struct ath_hw *ah) +{ + struct ath_node *an; + u32 to = 0; + struct ath_dynack *da = &ah->dynack; + struct ath_common *common = ath9k_hw_common(ah); + + list_for_each_entry(an, &da->nodes, list) + if (an->ackto > to) + to = an->ackto; + + if (to && da->ackto != to) { + u32 slottime; + + slottime = (to - 3) / 2; + da->ackto = to; + ath_dbg(common, DYNACK, "ACK timeout %u slottime %u\n", + da->ackto, slottime); + ath9k_hw_setslottime(ah, slottime); + ath9k_hw_set_ack_timeout(ah, da->ackto); + ath9k_hw_set_cts_timeout(ah, da->ackto); + } +} + +/** + * ath_dynack_compute_to - compute STA ACK timeout + * @ah: ath hw + * + * should be called while holding qlock + */ +static void ath_dynack_compute_to(struct ath_hw *ah) +{ + u32 ackto, ack_ts; + u8 *dst, *src; + struct ieee80211_sta *sta; + struct ath_node *an; + struct ts_info *st_ts; + struct ath_dynack *da = &ah->dynack; + + rcu_read_lock(); + + while (da->st_rbf.h_rb != da->st_rbf.t_rb && + da->ack_rbf.h_rb != da->ack_rbf.t_rb) { + ack_ts = da->ack_rbf.tstamp[da->ack_rbf.h_rb]; + st_ts = &da->st_rbf.ts[da->st_rbf.h_rb]; + dst = da->st_rbf.addr[da->st_rbf.h_rb].h_dest; + src = da->st_rbf.addr[da->st_rbf.h_rb].h_src; + + ath_dbg(ath9k_hw_common(ah), DYNACK, + "ack_ts %u st_ts %u st_dur %u [%u-%u]\n", + ack_ts, st_ts->tstamp, st_ts->dur, + da->ack_rbf.h_rb, da->st_rbf.h_rb); + + if (ack_ts > st_ts->tstamp + st_ts->dur) { + ackto = ack_ts - st_ts->tstamp - st_ts->dur; + + if (ackto < MAX_DELAY) { + sta = ieee80211_find_sta_by_ifaddr(ah->hw, dst, + src); + if (sta) { + an = (struct ath_node *)sta->drv_priv; + an->ackto = ath_dynack_ewma(an->ackto, + ackto); + ath_dbg(ath9k_hw_common(ah), DYNACK, + "%pM to %u\n", dst, an->ackto); + if (time_is_before_jiffies(da->lto)) { + ath_dynack_compute_ackto(ah); + da->lto = jiffies + COMPUTE_TO; + } + } + INCR(da->ack_rbf.h_rb, ATH_DYN_BUF); + } + INCR(da->st_rbf.h_rb, ATH_DYN_BUF); + } else { + INCR(da->ack_rbf.h_rb, ATH_DYN_BUF); + } + } + + rcu_read_unlock(); +} + +/** + * ath_dynack_sample_tx_ts - status timestamp sampling method + * @ah: ath hw + * @skb: socket buffer + * @ts: tx status info + * + */ +void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb, + struct ath_tx_status *ts) +{ + u8 ridx; + struct ieee80211_hdr *hdr; + struct ath_dynack *da = &ah->dynack; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !da->enabled) + return; + + spin_lock_bh(&da->qlock); + + hdr = (struct ieee80211_hdr *)skb->data; + + /* late ACK */ + if (ts->ts_status & ATH9K_TXERR_XRETRY) { + if (ieee80211_is_assoc_req(hdr->frame_control) || + ieee80211_is_assoc_resp(hdr->frame_control)) { + ath_dbg(common, DYNACK, "late ack\n"); + ath9k_hw_setslottime(ah, (LATEACK_TO - 3) / 2); + ath9k_hw_set_ack_timeout(ah, LATEACK_TO); + ath9k_hw_set_cts_timeout(ah, LATEACK_TO); + da->lto = jiffies + LATEACK_DELAY; + } + + spin_unlock_bh(&da->qlock); + return; + } + + ridx = ts->ts_rateindex; + + da->st_rbf.ts[da->st_rbf.t_rb].tstamp = ts->ts_tstamp; + da->st_rbf.ts[da->st_rbf.t_rb].dur = ts->duration; + ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_dest, hdr->addr1); + ether_addr_copy(da->st_rbf.addr[da->st_rbf.t_rb].h_src, hdr->addr2); + + if (!(info->status.rates[ridx].flags & IEEE80211_TX_RC_MCS)) { + u32 phy, sifs; + const struct ieee80211_rate *rate; + struct ieee80211_tx_rate *rates = info->status.rates; + + rate = &common->sbands[info->band].bitrates[rates[ridx].idx]; + if (info->band == IEEE80211_BAND_2GHZ && + !(rate->flags & IEEE80211_RATE_ERP_G)) + phy = WLAN_RC_PHY_CCK; + else + phy = WLAN_RC_PHY_OFDM; + + sifs = ath_dynack_get_sifs(ah, phy); + da->st_rbf.ts[da->st_rbf.t_rb].dur -= sifs; + } + + ath_dbg(common, DYNACK, "{%pM} tx sample %u [dur %u][h %u-t %u]\n", + hdr->addr1, da->st_rbf.ts[da->st_rbf.t_rb].tstamp, + da->st_rbf.ts[da->st_rbf.t_rb].dur, da->st_rbf.h_rb, + (da->st_rbf.t_rb + 1) % ATH_DYN_BUF); + + INCR(da->st_rbf.t_rb, ATH_DYN_BUF); + if (da->st_rbf.t_rb == da->st_rbf.h_rb) + INCR(da->st_rbf.h_rb, ATH_DYN_BUF); + + ath_dynack_compute_to(ah); + + spin_unlock_bh(&da->qlock); +} +EXPORT_SYMBOL(ath_dynack_sample_tx_ts); + +/** + * ath_dynack_sample_ack_ts - ACK timestamp sampling method + * @ah: ath hw + * @skb: socket buffer + * @ts: rx timestamp + * + */ +void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb, + u32 ts) +{ + struct ath_dynack *da = &ah->dynack; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (!ath_dynack_bssidmask(ah, hdr->addr1) || !da->enabled) + return; + + spin_lock_bh(&da->qlock); + da->ack_rbf.tstamp[da->ack_rbf.t_rb] = ts; + + ath_dbg(common, DYNACK, "rx sample %u [h %u-t %u]\n", + da->ack_rbf.tstamp[da->ack_rbf.t_rb], + da->ack_rbf.h_rb, (da->ack_rbf.t_rb + 1) % ATH_DYN_BUF); + + INCR(da->ack_rbf.t_rb, ATH_DYN_BUF); + if (da->ack_rbf.t_rb == da->ack_rbf.h_rb) + INCR(da->ack_rbf.h_rb, ATH_DYN_BUF); + + ath_dynack_compute_to(ah); + + spin_unlock_bh(&da->qlock); +} +EXPORT_SYMBOL(ath_dynack_sample_ack_ts); + +/** + * ath_dynack_node_init - init ath_node related info + * @ah: ath hw + * @an: ath node + * + */ +void ath_dynack_node_init(struct ath_hw *ah, struct ath_node *an) +{ + /* ackto = slottime + sifs + air delay */ + u32 ackto = ATH9K_SLOT_TIME_9 + 16 + 64; + struct ath_dynack *da = &ah->dynack; + + an->ackto = ackto; + + spin_lock(&da->qlock); + list_add_tail(&an->list, &da->nodes); + spin_unlock(&da->qlock); +} +EXPORT_SYMBOL(ath_dynack_node_init); + +/** + * ath_dynack_node_deinit - deinit ath_node related info + * @ah: ath hw + * @an: ath node + * + */ +void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an) +{ + struct ath_dynack *da = &ah->dynack; + + spin_lock(&da->qlock); + list_del(&an->list); + spin_unlock(&da->qlock); +} +EXPORT_SYMBOL(ath_dynack_node_deinit); + +/** + * ath_dynack_reset - reset dynack processing + * @ah: ath hw + * + */ +void ath_dynack_reset(struct ath_hw *ah) +{ + /* ackto = slottime + sifs + air delay */ + u32 ackto = ATH9K_SLOT_TIME_9 + 16 + 64; + struct ath_dynack *da = &ah->dynack; + + da->lto = jiffies; + da->ackto = ackto; + + da->st_rbf.t_rb = 0; + da->st_rbf.h_rb = 0; + da->ack_rbf.t_rb = 0; + da->ack_rbf.h_rb = 0; + + /* init acktimeout */ + ath9k_hw_setslottime(ah, (ackto - 3) / 2); + ath9k_hw_set_ack_timeout(ah, ackto); + ath9k_hw_set_cts_timeout(ah, ackto); +} +EXPORT_SYMBOL(ath_dynack_reset); + +/** + * ath_dynack_init - init dynack data structure + * @ah: ath hw + * + */ +void ath_dynack_init(struct ath_hw *ah) +{ + struct ath_dynack *da = &ah->dynack; + + memset(da, 0, sizeof(struct ath_dynack)); + + spin_lock_init(&da->qlock); + INIT_LIST_HEAD(&da->nodes); + + ah->hw->wiphy->features |= NL80211_FEATURE_ACKTO_ESTIMATION; +} diff --git a/drivers/net/wireless/ath/ath9k/dynack.h b/drivers/net/wireless/ath/ath9k/dynack.h new file mode 100644 index 000000000000..6d7bef976742 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/dynack.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014, Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DYNACK_H +#define DYNACK_H + +#define ATH_DYN_BUF 64 + +struct ath_hw; +struct ath_node; + +/** + * struct ath_dyn_rxbuf - ACK frame ring buffer + * @h_rb: ring buffer head + * @t_rb: ring buffer tail + * @tstamp: ACK RX timestamp buffer + */ +struct ath_dyn_rxbuf { + u16 h_rb, t_rb; + u32 tstamp[ATH_DYN_BUF]; +}; + +struct ts_info { + u32 tstamp; + u32 dur; +}; + +struct haddr_pair { + u8 h_dest[ETH_ALEN]; + u8 h_src[ETH_ALEN]; +}; + +/** + * struct ath_dyn_txbuf - tx frame ring buffer + * @h_rb: ring buffer head + * @t_rb: ring buffer tail + * @addr: dest/src address pair for a given TX frame + * @ts: TX frame timestamp buffer + */ +struct ath_dyn_txbuf { + u16 h_rb, t_rb; + struct haddr_pair addr[ATH_DYN_BUF]; + struct ts_info ts[ATH_DYN_BUF]; +}; + +/** + * struct ath_dynack - dynack processing info + * @enabled: enable dyn ack processing + * @ackto: current ACK timeout + * @lto: last ACK timeout computation + * @nodes: ath_node linked list + * @qlock: ts queue spinlock + * @ack_rbf: ACK ts ring buffer + * @st_rbf: status ts ring buffer + */ +struct ath_dynack { + bool enabled; + int ackto; + unsigned long lto; + + struct list_head nodes; + + /* protect timestamp queue access */ + spinlock_t qlock; + struct ath_dyn_rxbuf ack_rbf; + struct ath_dyn_txbuf st_rbf; +}; + +#if defined(CONFIG_ATH9K_DYNACK) +void ath_dynack_reset(struct ath_hw *ah); +void ath_dynack_node_init(struct ath_hw *ah, struct ath_node *an); +void ath_dynack_node_deinit(struct ath_hw *ah, struct ath_node *an); +void ath_dynack_init(struct ath_hw *ah); +void ath_dynack_sample_ack_ts(struct ath_hw *ah, struct sk_buff *skb, u32 ts); +void ath_dynack_sample_tx_ts(struct ath_hw *ah, struct sk_buff *skb, + struct ath_tx_status *ts); +#else +static inline void ath_dynack_init(struct ath_hw *ah) {} +static inline void ath_dynack_node_init(struct ath_hw *ah, + struct ath_node *an) {} +static inline void ath_dynack_node_deinit(struct ath_hw *ah, + struct ath_node *an) {} +static inline void ath_dynack_sample_ack_ts(struct ath_hw *ah, + struct sk_buff *skb, u32 ts) {} +static inline void ath_dynack_sample_tx_ts(struct ath_hw *ah, + struct sk_buff *skb, + struct ath_tx_status *ts) {} +#endif + +#endif /* DYNACK_H */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 8a3bd5fe3a54..d779f4fa50e3 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -592,6 +592,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + hw->queues = 4; hw->max_listen_interval = 1; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 5627917c5ff7..994fff1ff519 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1722,7 +1722,7 @@ static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value) } static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw, - u8 coverage_class) + s16 coverage_class) { struct ath9k_htc_priv *priv = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index a47ea8423f1e..8e85efeaeffc 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -67,6 +67,12 @@ static inline int ath9k_hw_txprocdesc(struct ath_hw *ah, void *ds, return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts); } +static inline int ath9k_hw_get_duration(struct ath_hw *ah, const void *ds, + int index) +{ + return ath9k_hw_ops(ah)->get_duration(ah, ds, index); +} + static inline void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf) { diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 69bbea1184d2..8be4b1453394 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -222,31 +222,28 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah) { u32 val; + if (ah->get_mac_revision) + ah->hw_version.macRev = ah->get_mac_revision(); + switch (ah->hw_version.devid) { case AR5416_AR9100_DEVID: ah->hw_version.macVersion = AR_SREV_VERSION_9100; break; case AR9300_DEVID_AR9330: ah->hw_version.macVersion = AR_SREV_VERSION_9330; - if (ah->get_mac_revision) { - ah->hw_version.macRev = ah->get_mac_revision(); - } else { + if (!ah->get_mac_revision) { val = REG_READ(ah, AR_SREV); ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); } return; case AR9300_DEVID_AR9340: ah->hw_version.macVersion = AR_SREV_VERSION_9340; - val = REG_READ(ah, AR_SREV); - ah->hw_version.macRev = MS(val, AR_SREV_REVISION2); return; case AR9300_DEVID_QCA955X: ah->hw_version.macVersion = AR_SREV_VERSION_9550; return; case AR9300_DEVID_AR953X: ah->hw_version.macVersion = AR_SREV_VERSION_9531; - if (ah->get_mac_revision) - ah->hw_version.macRev = ah->get_mac_revision(); return; } @@ -647,6 +644,8 @@ int ath9k_hw_init(struct ath_hw *ah) return ret; } + ath_dynack_init(ah); + return 0; } EXPORT_SYMBOL(ath9k_hw_init); @@ -702,6 +701,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, { u32 pll; + pll = ath9k_hw_compute_pll_control(ah, chan); + if (AR_SREV_9485(ah) || AR_SREV_9565(ah)) { /* program BB PLL ki and kd value, ki=0x4, kd=0x40 */ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL2, @@ -752,7 +753,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_CH0_DDR_DPLL3, AR_CH0_DPLL3_PHASE_SHIFT, 0x1); - REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); + REG_WRITE(ah, AR_RTC_PLL_CONTROL, + pll | AR_RTC_9300_PLL_BYPASS); udelay(1000); /* program refdiv, nint, frac to RTC register */ @@ -768,7 +770,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) { u32 regval, pll2_divint, pll2_divfrac, refdiv; - REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); + REG_WRITE(ah, AR_RTC_PLL_CONTROL, + pll | AR_RTC_9300_SOC_PLL_BYPASS); udelay(1000); REG_SET_BIT(ah, AR_PHY_PLL_MODE, 0x1 << 16); @@ -841,7 +844,6 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, udelay(1000); } - pll = ath9k_hw_compute_pll_control(ah, chan); if (AR_SREV_9565(ah)) pll |= 0x40000; REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); @@ -935,21 +937,21 @@ static void ath9k_hw_set_sifs_time(struct ath_hw *ah, u32 us) REG_WRITE(ah, AR_D_GBL_IFS_SIFS, val); } -static void ath9k_hw_setslottime(struct ath_hw *ah, u32 us) +void ath9k_hw_setslottime(struct ath_hw *ah, u32 us) { u32 val = ath9k_hw_mac_to_clks(ah, us); val = min(val, (u32) 0xFFFF); REG_WRITE(ah, AR_D_GBL_IFS_SLOT, val); } -static void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) +void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) { u32 val = ath9k_hw_mac_to_clks(ah, us); val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_ACK)); REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_ACK, val); } -static void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) +void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) { u32 val = ath9k_hw_mac_to_clks(ah, us); val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_CTS)); @@ -1053,6 +1055,14 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) ctstimeout += 48 - sifstime - ah->slottime; } + if (ah->dynack.enabled) { + acktimeout = ah->dynack.ackto; + ctstimeout = acktimeout; + slottime = (acktimeout - 3) / 2; + } else { + ah->dynack.ackto = acktimeout; + } + ath9k_hw_set_sifs_time(ah, sifstime); ath9k_hw_setslottime(ah, slottime); ath9k_hw_set_ack_timeout(ah, acktimeout); @@ -1182,9 +1192,12 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) switch (opmode) { case NL80211_IFTYPE_ADHOC: - set |= AR_STA_ID1_ADHOC; - REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); - break; + if (!AR_SREV_9340_13(ah)) { + set |= AR_STA_ID1_ADHOC; + REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); + break; + } + /* fall through */ case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: set |= AR_STA_ID1_STA_AP; @@ -1954,6 +1967,12 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (AR_SREV_9565(ah) && common->bt_ant_diversity) REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); + if (ah->hw->conf.radar_enabled) { + /* set HW specific DFS configuration */ + ah->radar_conf.ext_channel = IS_CHAN_HT40(chan); + ath9k_hw_set_radar_params(ah); + } + return 0; } EXPORT_SYMBOL(ath9k_hw_reset); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 51b4ebe04c04..975074fc11bc 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -29,6 +29,7 @@ #include "reg.h" #include "phy.h" #include "btcoex.h" +#include "dynack.h" #include "../regd.h" @@ -690,6 +691,7 @@ struct ath_hw_ops { struct ath_tx_info *i); int (*proc_txdesc)(struct ath_hw *ah, void *ds, struct ath_tx_status *ts); + int (*get_duration)(struct ath_hw *ah, const void *ds, int index); void (*antdiv_comb_conf_get)(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf); void (*antdiv_comb_conf_set)(struct ath_hw *ah, @@ -924,6 +926,8 @@ struct ath_hw { int (*external_reset)(void); const struct firmware *eeprom_blob; + + struct ath_dynack dynack; }; struct ath_bus_ops { @@ -1080,6 +1084,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning); void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan); +void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us); +void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us); +void ath9k_hw_setslottime(struct ath_hw *ah, u32 us); + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 39419ea845cc..156a944134dc 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -61,10 +61,14 @@ static int ath9k_ps_enable; module_param_named(ps_enable, ath9k_ps_enable, int, 0444); MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + int ath9k_use_chanctx; module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444); MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency"); +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ + bool is_ath9k_unloaded; #ifdef CONFIG_MAC80211_LEDS @@ -511,7 +515,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->tx99_power = MAX_RATE_POWER + 1; init_waitqueue_head(&sc->tx_wait); sc->cur_chan = &sc->chanctx[0]; - if (!ath9k_use_chanctx) + if (!ath9k_is_chanctx_enabled()) sc->cur_chan->hw_queue_base = 0; if (!pdata || pdata->use_eeprom) { @@ -567,11 +571,9 @@ 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); + + ath9k_init_channel_context(sc); /* * Cache line size is used to size and align various @@ -600,13 +602,15 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, if (ret) goto err_btcoex; - sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer, - NULL, sc, AR_FIRST_NDP_TIMER); + ret = ath9k_init_p2p(sc); + if (ret) + goto err_btcoex; ath9k_cmn_init_crypto(sc->sc_ah); ath9k_init_misc(sc); ath_fill_led_pin(sc); ath_chanctx_init(sc); + ath9k_offchannel_init(sc); if (common->bus_ops->aspm_init) common->bus_ops->aspm_init(common); @@ -672,18 +676,14 @@ static const struct ieee80211_iface_limit wds_limits[] = { { .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) }, }; +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + static const struct ieee80211_iface_limit if_limits_multi[] = { - { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, - { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + 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 - BIT(NL80211_IFTYPE_MESH_POINT) | -#endif - BIT(NL80211_IFTYPE_ADHOC) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, }; static const struct ieee80211_iface_combination if_comb_multi[] = { @@ -696,6 +696,16 @@ static const struct ieee80211_iface_combination if_comb_multi[] = { }, }; +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ + +static const struct ieee80211_iface_limit if_dfs_limits[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_ADHOC) }, +}; + static const struct ieee80211_iface_combination if_comb[] = { { .limits = if_limits, @@ -753,8 +763,9 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt) hw->flags |= IEEE80211_HW_MFP_CAPABLE; - hw->wiphy->features |= (NL80211_FEATURE_ACTIVE_MONITOR | - NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE); + hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR | + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | + NL80211_FEATURE_P2P_GO_CTWIN; if (!config_enabled(CONFIG_ATH9K_TX99)) { hw->wiphy->interface_modes = @@ -763,24 +774,31 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); - if (!ath9k_use_chanctx) { + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_WDS); + 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->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; - } } +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + + if (ath9k_is_chanctx_enabled()) { + hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS); + 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; + + ath_dbg(common, CHAN_CTX, "Use channel contexts\n"); + } + +#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; @@ -793,7 +811,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) /* allow 4 queues per channel context + * 1 cab queue + 1 offchannel tx queue */ - hw->queues = 10; + hw->queues = ATH9K_NUM_TX_QUEUES; /* last queue for offchannel */ hw->offchannel_tx_hw_queue = hw->queues - 1; hw->max_rates = 4; @@ -915,9 +933,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc) { int i = 0; - if (sc->p2p_ps_timer) - ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer); - + ath9k_deinit_p2p(sc); ath9k_deinit_btcoex(sc); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 6c56cafa5ca4..aa69ceaad0be 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -121,6 +121,7 @@ struct ath_tx_status { u32 evm0; u32 evm1; u32 evm2; + u32 duration; }; struct ath_rx_status { diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 4b148bbb2bf6..205162449b72 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -60,8 +60,10 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq) spin_lock_bh(&txq->axq_lock); - if (txq->axq_depth) + if (txq->axq_depth) { pending = true; + goto out; + } if (txq->mac80211_qnum >= 0) { struct list_head *list; @@ -70,6 +72,7 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq) if (!list_empty(list)) pending = true; } +out: spin_unlock_bh(&txq->axq_lock); return pending; } @@ -223,18 +226,12 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); unsigned long flags; - int i; - - if (ath_startrecv(sc) != 0) { - ath_err(common, "Unable to restart recv logic\n"); - return false; - } + ath9k_calculate_summary_state(sc, sc->cur_chan); + ath_startrecv(sc); ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->cur_chan->txpower, &sc->curtxpow); - clear_bit(ATH_OP_HW_RESET, &common->op_flags); - ath9k_calculate_summary_state(sc, sc->cur_chan); if (!sc->cur_chan->offchannel && start) { /* restore per chanctx TSF timer */ @@ -267,22 +264,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) 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); - } - + ieee80211_wake_queues(sc->hw); ath9k_p2p_ps_timer(sc); return true; @@ -314,6 +296,9 @@ int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) if (!ath_prepare_reset(sc)) fastcc = false; + if (ath9k_is_chanctx_enabled()) + fastcc = false; + spin_lock_bh(&sc->chan_lock); sc->cur_chandef = sc->cur_chan->chandef; spin_unlock_bh(&sc->chan_lock); @@ -358,12 +343,16 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, memset(&an->key_idx, 0, sizeof(an->key_idx)); ath_tx_node_init(sc, an); + + ath_dynack_node_init(sc->sc_ah, an); } static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) { struct ath_node *an = (struct ath_node *)sta->drv_priv; ath_tx_node_cleanup(sc, an); + + ath_dynack_node_deinit(sc->sc_ah, an); } void ath9k_tasklet(unsigned long data) @@ -822,7 +811,8 @@ 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); + ath9k_deinit_channel_context(sc); + mutex_lock(&sc->mutex); ath_cancel_work(sc); @@ -903,9 +893,10 @@ static bool ath9k_uses_beacons(int type) } } -static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data, + u8 *mac, struct ieee80211_vif *vif) { - struct ath9k_vif_iter_data *iter_data = data; + struct ath_vif *avp = (struct ath_vif *)vif->drv_priv; int i; if (iter_data->has_hw_macaddr) { @@ -923,12 +914,10 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) 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) + if (avp->assoc && !iter_data->primary_sta) iter_data->primary_sta = vif; break; case NL80211_IFTYPE_ADHOC: @@ -949,6 +938,34 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) } } +static void ath9k_update_bssid_mask(struct ath_softc *sc, + struct ath_chanctx *ctx, + struct ath9k_vif_iter_data *iter_data) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_vif *avp; + int i; + + if (!ath9k_is_chanctx_enabled()) + return; + + list_for_each_entry(avp, &ctx->vifs, list) { + if (ctx->nvifs_assigned != 1) + continue; + + if (!avp->vif->p2p || !iter_data->has_hw_macaddr) + continue; + + ether_addr_copy(common->curbssid, avp->bssid); + + /* perm_addr will be used as the p2p device address. */ + for (i = 0; i < ETH_ALEN; i++) + iter_data->mask[i] &= + ~(iter_data->hw_macaddr[i] ^ + sc->hw->wiphy->perm_addr[i]); + } +} + /* Called with sc->mutex held. */ void ath9k_calculate_iter_data(struct ath_softc *sc, struct ath_chanctx *ctx, @@ -968,38 +985,20 @@ void ath9k_calculate_iter_data(struct ath_softc *sc, 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; - } + ath9k_update_bssid_mask(sc, ctx, iter_data); } 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; + struct ath_vif *avp = (struct ath_vif *)vif->drv_priv; unsigned long flags; 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; + ether_addr_copy(common->curbssid, avp->bssid); + common->curaid = avp->aid; ath9k_hw_write_associd(sc->sc_ah); if (changed) { @@ -1019,6 +1018,43 @@ static void ath9k_set_assoc_state(struct ath_softc *sc, vif->addr, common->curbssid); } +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT +static void ath9k_set_offchannel_state(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_vif *vif = NULL; + + ath9k_ps_wakeup(sc); + + if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START) + vif = sc->offchannel.scan_vif; + else + vif = sc->offchannel.roc_vif; + + if (WARN_ON(!vif)) + goto exit; + + eth_zero_addr(common->curbssid); + eth_broadcast_addr(common->bssidmask); + ether_addr_copy(common->macaddr, vif->addr); + common->curaid = 0; + ah->opmode = vif->type; + ah->imask &= ~ATH9K_INT_SWBA; + ah->imask &= ~ATH9K_INT_TSFOOR; + ah->slottime = ATH9K_SLOT_TIME_9; + + ath_hw_setbssidmask(common); + ath9k_hw_setopmode(ah); + ath9k_hw_write_associd(sc->sc_ah); + ath9k_hw_set_interrupts(ah); + ath9k_hw_init_global_settings(ah); + +exit: + ath9k_ps_restore(sc); +} +#endif + /* Called with sc->mutex held. */ void ath9k_calculate_summary_state(struct ath_softc *sc, struct ath_chanctx *ctx) @@ -1026,12 +1062,18 @@ void ath9k_calculate_summary_state(struct ath_softc *sc, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_vif_iter_data iter_data; + struct ath_beacon_config *cur_conf; ath_chanctx_check_active(sc, ctx); if (ctx != sc->cur_chan) return; +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + if (ctx == &sc->offchannel.chan) + return ath9k_set_offchannel_state(sc); +#endif + ath9k_ps_wakeup(sc); ath9k_calculate_iter_data(sc, ctx, &iter_data); @@ -1042,8 +1084,11 @@ void ath9k_calculate_summary_state(struct ath_softc *sc, ath_hw_setbssidmask(common); if (iter_data.naps > 0) { + cur_conf = &ctx->beacon; ath9k_hw_set_tsfadjust(ah, true); ah->opmode = NL80211_IFTYPE_AP; + if (cur_conf->enable_beacon) + iter_data.beacons = true; } else { ath9k_hw_set_tsfadjust(ah, false); @@ -1072,13 +1117,11 @@ void ath9k_calculate_summary_state(struct ath_softc *sc, if (ah->opmode == NL80211_IFTYPE_STATION) { bool changed = (iter_data.primary_sta != ctx->primary_sta); - iter_data.beacons = true; if (iter_data.primary_sta) { + iter_data.beacons = true; 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; + ctx->primary_sta = iter_data.primary_sta; } else { ctx->primary_sta = NULL; memset(common->curbssid, 0, ETH_ALEN); @@ -1107,11 +1150,27 @@ void ath9k_calculate_summary_state(struct ath_softc *sc, else clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); - ctx->primary_sta = iter_data.primary_sta; + ath_dbg(common, CONFIG, + "macaddr: %pM, bssid: %pM, bssidmask: %pM\n", + common->macaddr, common->curbssid, common->bssidmask); ath9k_ps_restore(sc); } +static void ath9k_assign_hw_queues(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + int i; + + 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; +} + static int ath9k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -1120,12 +1179,11 @@ 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); if (config_enabled(CONFIG_ATH9K_TX99)) { - if (sc->nvifs >= 1) { + if (sc->cur_chan->nvifs >= 1) { mutex_unlock(&sc->mutex); return -EOPNOTSUPP; } @@ -1133,22 +1191,18 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, } ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); - sc->nvifs++; + sc->cur_chan->nvifs++; if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); avp->vif = vif; - if (!ath9k_use_chanctx) { + if (!ath9k_is_chanctx_enabled()) { 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; + + ath9k_assign_hw_queues(hw, vif); an->sc = sc; an->sta = NULL; @@ -1168,7 +1222,6 @@ 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); @@ -1188,43 +1241,13 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, 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_assign_hw_queues(hw, vif); ath9k_calculate_summary_state(sc, avp->chanctx); mutex_unlock(&sc->mutex); return 0; } -static void -ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) -{ - struct ath_hw *ah = sc->sc_ah; - s32 tsf, target_tsf; - - if (!avp || !avp->noa.has_next_tsf) - return; - - ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer); - - tsf = ath9k_hw_gettsf32(sc->sc_ah); - - target_tsf = avp->noa.next_tsf; - if (!avp->noa.absent) - target_tsf -= ATH_P2P_PS_STOP_TIME; - - if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) - target_tsf = tsf + ATH_P2P_PS_STOP_TIME; - - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); -} - static void ath9k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -1236,16 +1259,11 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); - spin_lock_bh(&sc->sc_pcu_lock); - if (avp == sc->p2p_ps_vif) { - sc->p2p_ps_vif = NULL; - ath9k_update_p2p_ps_timer(sc, NULL); - } - spin_unlock_bh(&sc->sc_pcu_lock); + ath9k_p2p_remove_vif(sc, vif); - sc->nvifs--; + sc->cur_chan->nvifs--; sc->tx99_vif = NULL; - if (!ath9k_use_chanctx) + if (!ath9k_is_chanctx_enabled()) list_del(&avp->list); if (ath9k_uses_beacons(vif->type)) @@ -1423,7 +1441,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } } - if (!ath9k_use_chanctx && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { + if (!ath9k_is_chanctx_enabled() && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL); ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef); } @@ -1463,7 +1481,10 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw, changed_flags &= SUPPORTED_FILTERS; *total_flags &= SUPPORTED_FILTERS; - sc->rx.rxfilter = *total_flags; + spin_lock_bh(&sc->chan_lock); + sc->cur_chan->rxfilter = *total_flags; + spin_unlock_bh(&sc->chan_lock); + ath9k_ps_wakeup(sc); rfilt = ath_calcrxfilter(sc); ath9k_hw_setrxfilter(sc->sc_ah, rfilt); @@ -1687,70 +1708,6 @@ static int ath9k_set_key(struct ieee80211_hw *hw, return ret; } -void ath9k_p2p_ps_timer(void *priv) -{ - struct ath_softc *sc = priv; - struct ath_vif *avp = sc->p2p_ps_vif; - struct ieee80211_vif *vif; - struct ieee80211_sta *sta; - struct ath_node *an; - u32 tsf; - - 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); - if (!avp->noa.absent) - tsf += ATH_P2P_PS_STOP_TIME; - - if (!avp->noa.has_next_tsf || - avp->noa.next_tsf - tsf > BIT(31)) - ieee80211_update_p2p_noa(&avp->noa, tsf); - - ath9k_update_p2p_ps_timer(sc, avp); - - rcu_read_lock(); - - vif = avp->vif; - sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); - if (!sta) - goto out; - - an = (void *) sta->drv_priv; - if (an->sleeping == !!avp->noa.absent) - goto out; - - an->sleeping = avp->noa.absent; - if (an->sleeping) - ath_tx_aggr_sleep(sta, sc, an); - else - ath_tx_aggr_wakeup(sc, an); - -out: - rcu_read_unlock(); -} - -void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) -{ - struct ath_vif *avp = (void *)vif->drv_priv; - u32 tsf; - - if (!sc->p2p_ps_timer) - return; - - if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p) - return; - - sc->p2p_ps_vif = avp; - tsf = ath9k_hw_gettsf32(sc->sc_ah); - ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); - ath9k_update_p2p_ps_timer(sc, avp); -} - static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -1765,7 +1722,6 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; - unsigned long flags; int slottime; ath9k_ps_wakeup(sc); @@ -1775,9 +1731,17 @@ 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); + ether_addr_copy(avp->bssid, bss_conf->bssid); + avp->aid = bss_conf->aid; + avp->assoc = bss_conf->assoc; + ath9k_calculate_summary_state(sc, avp->chanctx); - if (bss_conf->assoc) - ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_ASSOC); + + if (ath9k_is_chanctx_enabled()) { + if (bss_conf->assoc) + ath_chanctx_event(sc, vif, + ATH_CHANCTX_EVENT_ASSOC); + } } if (changed & BSS_CHANGED_IBSS) { @@ -1789,9 +1753,9 @@ 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_INFO)) { + ath9k_beacon_config(sc, vif, changed); if (changed & BSS_CHANGED_BEACON_ENABLED) ath9k_calculate_summary_state(sc, avp->chanctx); - ath9k_beacon_config(sc, vif, changed); } if ((avp->chanctx == sc->cur_chan) && @@ -1814,14 +1778,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } } - if (changed & BSS_CHANGED_P2P_PS) { - spin_lock_bh(&sc->sc_pcu_lock); - spin_lock_irqsave(&sc->sc_pm_lock, flags); - if (!(sc->ps_flags & PS_BEACON_SYNC)) - ath9k_update_p2p_ps(sc, vif); - spin_unlock_irqrestore(&sc->sc_pm_lock, flags); - spin_unlock_bh(&sc->sc_pcu_lock); - } + if (changed & BSS_CHANGED_P2P_PS) + ath9k_p2p_bss_info_changed(sc, vif); if (changed & CHECK_ANI) ath_check_ani(sc); @@ -1959,7 +1917,22 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, return 0; } -static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) +static void ath9k_enable_dynack(struct ath_softc *sc) +{ +#ifdef CONFIG_ATH9K_DYNACK + u32 rfilt; + struct ath_hw *ah = sc->sc_ah; + + ath_dynack_reset(ah); + + ah->dynack.enabled = true; + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); +#endif +} + +static void ath9k_set_coverage_class(struct ieee80211_hw *hw, + s16 coverage_class) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; @@ -1968,11 +1941,22 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) return; mutex_lock(&sc->mutex); - ah->coverage_class = coverage_class; - ath9k_ps_wakeup(sc); - ath9k_hw_init_global_settings(ah); - ath9k_ps_restore(sc); + if (coverage_class >= 0) { + ah->coverage_class = coverage_class; + if (ah->dynack.enabled) { + u32 rfilt; + + ah->dynack.enabled = false; + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + } + ath9k_ps_wakeup(sc); + ath9k_hw_init_global_settings(ah); + ath9k_ps_restore(sc); + } else if (!ah->dynack.enabled) { + ath9k_enable_dynack(sc); + } mutex_unlock(&sc->mutex); } @@ -1985,9 +1969,6 @@ static bool ath9k_has_tx_pending(struct ath_softc *sc) if (!ATH_TXQ_SETUP(sc, i)) continue; - if (!sc->tx.txq[i].axq_depth) - continue; - npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i]); if (npend) break; @@ -2013,7 +1994,6 @@ void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) struct ath_common *common = ath9k_hw_common(ah); int timeout = HZ / 5; /* 200 ms */ bool drain_txq; - int i; cancel_delayed_work_sync(&sc->tx_complete_work); @@ -2041,10 +2021,6 @@ void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) ath_reset(sc); ath9k_ps_restore(sc); - 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); @@ -2053,16 +2029,8 @@ void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; - int i; - - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { - if (!ATH_TXQ_SETUP(sc, i)) - continue; - if (ath9k_has_pending_frames(sc, &sc->tx.txq[i])) - return true; - } - return false; + return ath9k_has_tx_pending(sc); } static int ath9k_tx_last_beacon(struct ieee80211_hw *hw) @@ -2207,207 +2175,7 @@ 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; - } -} +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *hw_req) @@ -2430,8 +2198,13 @@ static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, sc->offchannel.scan_req = req; sc->offchannel.scan_idx = 0; - if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_dbg(common, CHAN_CTX, "HW scan request received on vif: %pM\n", + vif->addr); + + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) { + ath_dbg(common, CHAN_CTX, "Starting HW scan\n"); ath_offchannel_next(sc); + } out: mutex_unlock(&sc->mutex); @@ -2443,6 +2216,9 @@ static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ath_dbg(common, CHAN_CTX, "Cancel HW scan on vif: %pM\n", vif->addr); mutex_lock(&sc->mutex); del_timer_sync(&sc->offchannel.timer); @@ -2456,6 +2232,7 @@ static int ath9k_remain_on_channel(struct ieee80211_hw *hw, enum ieee80211_roc_type type) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); int ret = 0; mutex_lock(&sc->mutex); @@ -2470,8 +2247,14 @@ static int ath9k_remain_on_channel(struct ieee80211_hw *hw, sc->offchannel.roc_chan = chan; sc->offchannel.roc_duration = duration; - if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) + ath_dbg(common, CHAN_CTX, + "RoC request on vif: %pM, type: %d duration: %d\n", + vif->addr, type, duration); + + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) { + ath_dbg(common, CHAN_CTX, "Starting RoC period\n"); ath_offchannel_next(sc); + } out: mutex_unlock(&sc->mutex); @@ -2482,9 +2265,11 @@ out: static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); mutex_lock(&sc->mutex); + ath_dbg(common, CHAN_CTX, "Cancel RoC\n"); del_timer_sync(&sc->offchannel.timer); if (sc->offchannel.roc_vif) { @@ -2501,6 +2286,7 @@ static int ath9k_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_chanctx *ctx, **ptr; int pos; @@ -2515,10 +2301,18 @@ static int ath9k_add_chanctx(struct ieee80211_hw *hw, ctx->assigned = true; pos = ctx - &sc->chanctx[0]; ctx->hw_queue_base = pos * IEEE80211_NUM_ACS; + + ath_dbg(common, CHAN_CTX, + "Add channel context: %d MHz\n", + conf->def.chan->center_freq); + ath_chanctx_set_channel(sc, ctx, &conf->def); + ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ASSIGN); + mutex_unlock(&sc->mutex); return 0; } + mutex_unlock(&sc->mutex); return -ENOSPC; } @@ -2528,12 +2322,19 @@ static void ath9k_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_chanctx *ctx = ath_chanctx_get(conf); mutex_lock(&sc->mutex); + + ath_dbg(common, CHAN_CTX, + "Remove channel context: %d MHz\n", + conf->def.chan->center_freq); + ctx->assigned = false; ctx->hw_queue_base = -1; ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN); + mutex_unlock(&sc->mutex); } @@ -2542,9 +2343,13 @@ static void ath9k_change_chanctx(struct ieee80211_hw *hw, u32 changed) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_chanctx *ctx = ath_chanctx_get(conf); mutex_lock(&sc->mutex); + ath_dbg(common, CHAN_CTX, + "Change channel context: %d MHz\n", + conf->def.chan->center_freq); ath_chanctx_set_channel(sc, ctx, &conf->def); mutex_unlock(&sc->mutex); } @@ -2554,16 +2359,25 @@ static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_chanctx *ctx = ath_chanctx_get(conf); int i; mutex_lock(&sc->mutex); + + ath_dbg(common, CHAN_CTX, + "Assign VIF (addr: %pM, type: %d, p2p: %d) to channel context: %d MHz\n", + vif->addr, vif->type, vif->p2p, + conf->def.chan->center_freq); + avp->chanctx = ctx; + ctx->nvifs_assigned++; 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; @@ -2574,36 +2388,80 @@ static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_chanctx *ctx = ath_chanctx_get(conf); int ac; mutex_lock(&sc->mutex); + + ath_dbg(common, CHAN_CTX, + "Remove VIF (addr: %pM, type: %d, p2p: %d) from channel context: %d MHz\n", + vif->addr, vif->type, vif->p2p, + conf->def.chan->center_freq); + avp->chanctx = NULL; + ctx->nvifs_assigned--; 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); +} + +static void ath9k_mgd_prepare_tx(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; + } + ath_dbg(common, CHAN_CTX, + "%s: Set chanctx state to FORCE_ACTIVE, changed: %d\n", + __func__, changed); + 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 ath9k_fill_chanctx_ops(void) { - if (!ath9k_use_chanctx) + if (!ath9k_is_chanctx_enabled()) 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.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; + 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_mgd_prepare_tx; } +#endif + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 74ab1d02013b..6914e21816e4 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -387,7 +387,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc) if (sc->hw->conf.radar_enabled) rfilt |= ATH9K_RX_FILTER_PHYRADAR | ATH9K_RX_FILTER_PHYERR; - if (sc->rx.rxfilter & FIF_PROBE_REQ) + spin_lock_bh(&sc->chan_lock); + + if (sc->cur_chan->rxfilter & FIF_PROBE_REQ) rfilt |= ATH9K_RX_FILTER_PROBEREQ; /* @@ -398,24 +400,25 @@ u32 ath_calcrxfilter(struct ath_softc *sc) if (sc->sc_ah->is_monitoring) rfilt |= ATH9K_RX_FILTER_PROM; - if (sc->rx.rxfilter & FIF_CONTROL) + if ((sc->cur_chan->rxfilter & FIF_CONTROL) || + sc->sc_ah->dynack.enabled) rfilt |= ATH9K_RX_FILTER_CONTROL; if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) && - (sc->nvifs <= 1) && - !(sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC)) + (sc->cur_chan->nvifs <= 1) && + !(sc->cur_chan->rxfilter & FIF_BCN_PRBRESP_PROMISC)) rfilt |= ATH9K_RX_FILTER_MYBEACON; else rfilt |= ATH9K_RX_FILTER_BEACON; if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || - (sc->rx.rxfilter & FIF_PSPOLL)) + (sc->cur_chan->rxfilter & FIF_PSPOLL)) rfilt |= ATH9K_RX_FILTER_PSPOLL; - if (conf_is_ht(&sc->hw->conf)) + if (sc->cur_chandef.width != NL80211_CHAN_WIDTH_20_NOHT) rfilt |= ATH9K_RX_FILTER_COMP_BAR; - if (sc->nvifs > 1 || (sc->rx.rxfilter & FIF_OTHER_BSS)) { + if (sc->cur_chan->nvifs > 1 || (sc->cur_chan->rxfilter & FIF_OTHER_BSS)) { /* This is needed for older chips */ if (sc->sc_ah->hw_version.macVersion <= AR_SREV_VERSION_9160) rfilt |= ATH9K_RX_FILTER_PROM; @@ -425,22 +428,24 @@ 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 && + if (ath9k_is_chanctx_enabled() && test_bit(ATH_OP_SCANNING, &common->op_flags)) rfilt |= ATH9K_RX_FILTER_BEACON; + spin_unlock_bh(&sc->chan_lock); + return rfilt; } -int ath_startrecv(struct ath_softc *sc) +void ath_startrecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_rxbuf *bf, *tbf; if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { ath_edma_start_recv(sc); - return 0; + return; } if (list_empty(&sc->rx.rxbuf)) @@ -463,8 +468,6 @@ int ath_startrecv(struct ath_softc *sc) start_recv: ath_opmode_init(sc); ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel); - - return 0; } static void ath_flushrecv(struct ath_softc *sc) @@ -535,6 +538,7 @@ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); + bool skip_beacon = false; if (skb->len < 24 + 8 + 2 + 2) return; @@ -545,10 +549,19 @@ 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_chan->beacon.beacon_interval == 0))) + +#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT + if (ath9k_is_chanctx_enabled()) { + if (sc->cur_chan == &sc->offchannel.chan) + skip_beacon = true; + } +#endif + + if (!skip_beacon && + !(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); + + ath9k_p2p_beacon_sync(sc); } if (ath_beacon_dtim_pending_cab(skb)) { @@ -867,8 +880,13 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, * everything but the rate is checked here, the rate check is done * separately to avoid doing two lookups for a rate for each frame. */ - if (!ath9k_cmn_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error, sc->rx.rxfilter)) + spin_lock_bh(&sc->chan_lock); + if (!ath9k_cmn_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error, + sc->cur_chan->rxfilter)) { + spin_unlock_bh(&sc->chan_lock); return -EINVAL; + } + spin_unlock_bh(&sc->chan_lock); if (ath_is_mybeacon(common, hdr)) { RX_STAT_INC(rx_beacons); @@ -892,9 +910,10 @@ 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); + if (ath9k_is_chanctx_enabled()) { + if (rx_stats->is_mybeacon) + ath_chanctx_beacon_recv_ev(sc, + ATH_CHANCTX_EVENT_BEACON_RECEIVED); } ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status); @@ -991,6 +1010,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) unsigned long flags; dma_addr_t new_buf_addr; unsigned int budget = 512; + struct ieee80211_hdr *hdr; if (edma) dma_type = DMA_BIDIRECTIONAL; @@ -1120,6 +1140,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ath9k_apply_ampdu_details(sc, &rs, rxs); ath_debug_rate_stats(sc, &rs, skb); + hdr = (struct ieee80211_hdr *)skb->data; + if (ieee80211_is_ack(hdr->frame_control)) + ath_dynack_sample_ack_ts(sc->sc_ah, skb, rs.rs_tstamp); + ieee80211_rx(hw, skb); requeue_drop_frag: diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index a1499700bcf2..2a938f4feac5 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -903,6 +903,10 @@ #define AR_SREV_9340(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9340)) +#define AR_SREV_9340_13(_ah) \ + (AR_SREV_9340((_ah)) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9340_13)) + #define AR_SREV_9340_13_OR_LATER(_ah) \ (AR_SREV_9340((_ah)) && \ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9340_13)) @@ -1240,12 +1244,23 @@ enum { #define AR_CH0_DPLL3_PHASE_SHIFT_S 23 #define AR_PHY_CCA_NOM_VAL_2GHZ -118 +#define AR_RTC_9300_SOC_PLL_DIV_INT 0x0000003f +#define AR_RTC_9300_SOC_PLL_DIV_INT_S 0 +#define AR_RTC_9300_SOC_PLL_DIV_FRAC 0x000fffc0 +#define AR_RTC_9300_SOC_PLL_DIV_FRAC_S 6 +#define AR_RTC_9300_SOC_PLL_REFDIV 0x01f00000 +#define AR_RTC_9300_SOC_PLL_REFDIV_S 20 +#define AR_RTC_9300_SOC_PLL_CLKSEL 0x06000000 +#define AR_RTC_9300_SOC_PLL_CLKSEL_S 25 +#define AR_RTC_9300_SOC_PLL_BYPASS 0x08000000 + #define AR_RTC_9300_PLL_DIV 0x000003ff #define AR_RTC_9300_PLL_DIV_S 0 #define AR_RTC_9300_PLL_REFDIV 0x00003C00 #define AR_RTC_9300_PLL_REFDIV_S 10 #define AR_RTC_9300_PLL_CLKSEL 0x0000C000 #define AR_RTC_9300_PLL_CLKSEL_S 14 +#define AR_RTC_9300_PLL_BYPASS 0x00010000 #define AR_RTC_9160_PLL_DIV 0x000003ff #define AR_RTC_9160_PLL_DIV_S 0 diff --git a/drivers/net/wireless/ath/ath9k/spectral.h b/drivers/net/wireless/ath/ath9k/spectral.h index ead63412ee1a..7b410c6858b0 100644 --- a/drivers/net/wireless/ath/ath9k/spectral.h +++ b/drivers/net/wireless/ath/ath9k/spectral.h @@ -17,6 +17,8 @@ #ifndef SPECTRAL_H #define SPECTRAL_H +#include "../spectral_common.h" + /* enum spectral_mode: * * @SPECTRAL_DISABLED: spectral mode is disabled @@ -54,8 +56,6 @@ struct ath_ht20_mag_info { u8 max_exp; } __packed; -#define SPECTRAL_HT20_NUM_BINS 56 - /* WARNING: don't actually use this struct! MAC may vary the amount of * data by -1/+2. This struct is for reference only. */ @@ -83,8 +83,6 @@ struct ath_ht20_40_mag_info { u8 max_exp; } __packed; -#define SPECTRAL_HT20_40_NUM_BINS 128 - /* WARNING: don't actually use this struct! MAC may vary the amount of * data. This struct is for reference only. */ @@ -125,71 +123,6 @@ static inline u8 spectral_bitmap_weight(u8 *bins) return bins[0] & 0x3f; } -/* FFT sample format given to userspace via debugfs. - * - * Please keep the type/length at the front position and change - * other fields after adding another sample type - * - * TODO: this might need rework when switching to nl80211-based - * interface. - */ -enum ath_fft_sample_type { - ATH_FFT_SAMPLE_HT20 = 1, - ATH_FFT_SAMPLE_HT20_40, -}; - -struct fft_sample_tlv { - u8 type; /* see ath_fft_sample */ - __be16 length; - /* type dependent data follows */ -} __packed; - -struct fft_sample_ht20 { - struct fft_sample_tlv tlv; - - u8 max_exp; - - __be16 freq; - s8 rssi; - s8 noise; - - __be16 max_magnitude; - u8 max_index; - u8 bitmap_weight; - - __be64 tsf; - - u8 data[SPECTRAL_HT20_NUM_BINS]; -} __packed; - -struct fft_sample_ht20_40 { - struct fft_sample_tlv tlv; - - u8 channel_type; - __be16 freq; - - s8 lower_rssi; - s8 upper_rssi; - - __be64 tsf; - - s8 lower_noise; - s8 upper_noise; - - __be16 lower_max_magnitude; - __be16 upper_max_magnitude; - - u8 lower_max_index; - u8 upper_max_index; - - u8 lower_bitmap_weight; - u8 upper_bitmap_weight; - - u8 max_exp; - - u8 data[SPECTRAL_HT20_40_NUM_BINS]; -} __packed; - void ath9k_spectral_init_debug(struct ath_softc *sc); void ath9k_spectral_deinit_debug(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index 23972924c774..8a69d08ec55c 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -174,7 +174,7 @@ static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, ssize_t len; int r; - if (sc->nvifs > 1) + if (sc->cur_chan->nvifs > 1) return -EOPNOTSUPP; len = min(count, sizeof(buf) - 1); diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index a4f4f0da81f6..5f30e580d942 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -193,7 +193,8 @@ int ath9k_suspend(struct ieee80211_hw *hw, u32 wow_triggers_enabled = 0; int ret = 0; - cancel_work_sync(&sc->chanctx_work); + ath9k_deinit_channel_context(sc); + mutex_lock(&sc->mutex); ath_cancel_work(sc); @@ -231,7 +232,7 @@ int ath9k_suspend(struct ieee80211_hw *hw, goto fail_wow; } - if (sc->nvifs > 1) { + if (sc->cur_chan->nvifs > 1) { ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); ret = 1; goto fail_wow; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 704fcbcbe20b..151ae49fa57e 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -158,7 +158,6 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, { 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 (q < 0) @@ -168,10 +167,9 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, 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, hw_queue); + ieee80211_wake_queue(sc->hw, info->hw_queue); txq->stopped = false; } } @@ -587,6 +585,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, memcpy(tx_info->control.rates, rates, sizeof(rates)); ath_tx_rc_status(sc, bf, ts, nframes, nbad, txok); rc_update = false; + if (bf == bf->bf_lastbf) + ath_dynack_sample_tx_ts(sc->sc_ah, + bf->bf_mpdu, + ts); } ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, @@ -681,12 +683,15 @@ static void ath_tx_process_buffer(struct ath_softc *sc, struct ath_txq *txq, if (bf_is_ampdu_not_probing(bf)) txq->axq_ampdu_depth--; + ts->duration = ath9k_hw_get_duration(sc->sc_ah, bf->bf_desc, + ts->ts_rateindex); if (!bf_isampdu(bf)) { if (!flush) { info = IEEE80211_SKB_CB(bf->bf_mpdu); memcpy(info->control.rates, bf->rates, sizeof(info->control.rates)); ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok); + ath_dynack_sample_tx_ts(sc->sc_ah, bf->bf_mpdu, ts); } ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok); } else @@ -1836,15 +1841,17 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) if (txq->mac80211_qnum < 0) return; + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) + 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(ac_list)) + if (list_empty(ac_list)) { + spin_unlock_bh(&sc->chan_lock); return; + } - spin_lock_bh(&sc->chan_lock); rcu_read_lock(); last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list); @@ -2202,9 +2209,8 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_txq *txq = txctl->txq; struct ath_atx_tid *tid = NULL; struct ath_buf *bf; - bool queue; - int q, hw_queue; - int ret; + bool queue, skip_uapsd = false; + int q, ret; if (vif) avp = (void *)vif->drv_priv; @@ -2223,14 +2229,13 @@ 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]) { fi->txq = q; if (++txq->pending_frames > sc->tx.txq_max_pending[q] && !txq->stopped) { - ieee80211_stop_queue(sc->hw, hw_queue); + ieee80211_stop_queue(sc->hw, info->hw_queue); txq->stopped = true; } } @@ -2245,15 +2250,14 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, sc->cur_chan->stopped) && !txctl->force_channel) { if (!txctl->an) txctl->an = &avp->mcast_node; - info->flags &= ~IEEE80211_TX_CTL_PS_RESPONSE; queue = true; + skip_uapsd = true; } if (txctl->an && queue) tid = ath_get_skb_tid(sc, txctl->an, skb); - if (info->flags & (IEEE80211_TX_CTL_PS_RESPONSE | - IEEE80211_TX_CTL_TX_OFFCHAN)) { + if (!skip_uapsd && (info->flags & IEEE80211_TX_CTL_PS_RESPONSE)) { ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); @@ -2632,8 +2636,11 @@ 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); + if (ath9k_is_chanctx_enabled()) { + ath_chanctx_event(sc, NULL, + ATH_CHANCTX_EVENT_BEACON_SENT); + } + ath9k_csa_update(sc); continue; } |