18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/nl80211.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include "ath9k.h" 208c2ecf20Sopenharmony_ci#include "btcoex.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 238c2ecf20Sopenharmony_ci u32 queues, bool drop); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciu8 ath9k_parse_mpdudensity(u8 mpdudensity) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci /* 288c2ecf20Sopenharmony_ci * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": 298c2ecf20Sopenharmony_ci * 0 for no restriction 308c2ecf20Sopenharmony_ci * 1 for 1/4 us 318c2ecf20Sopenharmony_ci * 2 for 1/2 us 328c2ecf20Sopenharmony_ci * 3 for 1 us 338c2ecf20Sopenharmony_ci * 4 for 2 us 348c2ecf20Sopenharmony_ci * 5 for 4 us 358c2ecf20Sopenharmony_ci * 6 for 8 us 368c2ecf20Sopenharmony_ci * 7 for 16 us 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci switch (mpdudensity) { 398c2ecf20Sopenharmony_ci case 0: 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci case 1: 428c2ecf20Sopenharmony_ci case 2: 438c2ecf20Sopenharmony_ci case 3: 448c2ecf20Sopenharmony_ci /* Our lower layer calculations limit our precision to 458c2ecf20Sopenharmony_ci 1 microsecond */ 468c2ecf20Sopenharmony_ci return 1; 478c2ecf20Sopenharmony_ci case 4: 488c2ecf20Sopenharmony_ci return 2; 498c2ecf20Sopenharmony_ci case 5: 508c2ecf20Sopenharmony_ci return 4; 518c2ecf20Sopenharmony_ci case 6: 528c2ecf20Sopenharmony_ci return 8; 538c2ecf20Sopenharmony_ci case 7: 548c2ecf20Sopenharmony_ci return 16; 558c2ecf20Sopenharmony_ci default: 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq, 618c2ecf20Sopenharmony_ci bool sw_pending) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci bool pending = false; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci spin_lock_bh(&txq->axq_lock); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (txq->axq_depth) { 688c2ecf20Sopenharmony_ci pending = true; 698c2ecf20Sopenharmony_ci goto out; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!sw_pending) 738c2ecf20Sopenharmony_ci goto out; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (txq->mac80211_qnum >= 0) { 768c2ecf20Sopenharmony_ci struct ath_acq *acq; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci acq = &sc->cur_chan->acq[txq->mac80211_qnum]; 798c2ecf20Sopenharmony_ci if (!list_empty(&acq->acq_new) || !list_empty(&acq->acq_old)) 808c2ecf20Sopenharmony_ci pending = true; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ciout: 838c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->axq_lock); 848c2ecf20Sopenharmony_ci return pending; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci unsigned long flags; 908c2ecf20Sopenharmony_ci bool ret; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 938c2ecf20Sopenharmony_ci ret = ath9k_hw_setpower(sc->sc_ah, mode); 948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_civoid ath_ps_full_sleep(struct timer_list *t) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct ath_softc *sc = from_timer(sc, t, sleep_timer); 1028c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 1038c2ecf20Sopenharmony_ci unsigned long flags; 1048c2ecf20Sopenharmony_ci bool reset; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci spin_lock_irqsave(&common->cc_lock, flags); 1078c2ecf20Sopenharmony_ci ath_hw_cycle_counters_update(common); 1088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&common->cc_lock, flags); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ath9k_hw_setrxabort(sc->sc_ah, 1); 1118c2ecf20Sopenharmony_ci ath9k_hw_stopdmarecv(sc->sc_ah, &reset); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_civoid ath9k_ps_wakeup(struct ath_softc *sc) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 1198c2ecf20Sopenharmony_ci unsigned long flags; 1208c2ecf20Sopenharmony_ci enum ath9k_power_mode power_mode; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 1238c2ecf20Sopenharmony_ci if (++sc->ps_usecount != 1) 1248c2ecf20Sopenharmony_ci goto unlock; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci del_timer_sync(&sc->sleep_timer); 1278c2ecf20Sopenharmony_ci power_mode = sc->sc_ah->power_mode; 1288c2ecf20Sopenharmony_ci ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * While the hardware is asleep, the cycle counters contain no 1328c2ecf20Sopenharmony_ci * useful data. Better clear them now so that they don't mess up 1338c2ecf20Sopenharmony_ci * survey data results. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci if (power_mode != ATH9K_PM_AWAKE) { 1368c2ecf20Sopenharmony_ci spin_lock(&common->cc_lock); 1378c2ecf20Sopenharmony_ci ath_hw_cycle_counters_update(common); 1388c2ecf20Sopenharmony_ci memset(&common->cc_survey, 0, sizeof(common->cc_survey)); 1398c2ecf20Sopenharmony_ci memset(&common->cc_ani, 0, sizeof(common->cc_ani)); 1408c2ecf20Sopenharmony_ci spin_unlock(&common->cc_lock); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci unlock: 1448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_civoid ath9k_ps_restore(struct ath_softc *sc) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 1508c2ecf20Sopenharmony_ci enum ath9k_power_mode mode; 1518c2ecf20Sopenharmony_ci unsigned long flags; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 1548c2ecf20Sopenharmony_ci if (--sc->ps_usecount != 0) 1558c2ecf20Sopenharmony_ci goto unlock; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (sc->ps_idle) { 1588c2ecf20Sopenharmony_ci mod_timer(&sc->sleep_timer, jiffies + HZ / 10); 1598c2ecf20Sopenharmony_ci goto unlock; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (sc->ps_enabled && 1638c2ecf20Sopenharmony_ci !(sc->ps_flags & (PS_WAIT_FOR_BEACON | 1648c2ecf20Sopenharmony_ci PS_WAIT_FOR_CAB | 1658c2ecf20Sopenharmony_ci PS_WAIT_FOR_PSPOLL_DATA | 1668c2ecf20Sopenharmony_ci PS_WAIT_FOR_TX_ACK | 1678c2ecf20Sopenharmony_ci PS_WAIT_FOR_ANI))) { 1688c2ecf20Sopenharmony_ci mode = ATH9K_PM_NETWORK_SLEEP; 1698c2ecf20Sopenharmony_ci if (ath9k_hw_btcoex_is_enabled(sc->sc_ah)) 1708c2ecf20Sopenharmony_ci ath9k_btcoex_stop_gen_timer(sc); 1718c2ecf20Sopenharmony_ci } else { 1728c2ecf20Sopenharmony_ci goto unlock; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci spin_lock(&common->cc_lock); 1768c2ecf20Sopenharmony_ci ath_hw_cycle_counters_update(common); 1778c2ecf20Sopenharmony_ci spin_unlock(&common->cc_lock); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ath9k_hw_setpower(sc->sc_ah, mode); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci unlock: 1828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void __ath_cancel_work(struct ath_softc *sc) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci cancel_work_sync(&sc->paprd_work); 1888c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&sc->hw_check_work); 1898c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&sc->hw_pll_work); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT 1928c2ecf20Sopenharmony_ci if (ath9k_hw_mci_is_enabled(sc->sc_ah)) 1938c2ecf20Sopenharmony_ci cancel_work_sync(&sc->mci_work); 1948c2ecf20Sopenharmony_ci#endif 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_civoid ath_cancel_work(struct ath_softc *sc) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci __ath_cancel_work(sc); 2008c2ecf20Sopenharmony_ci cancel_work_sync(&sc->hw_reset_work); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_civoid ath_restart_work(struct ath_softc *sc) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work, 2068c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH_HW_CHECK_POLL_INT)); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9330(sc->sc_ah)) 2098c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, 2108c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ath_start_ani(sc); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic bool ath_prepare_reset(struct ath_softc *sc) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 2188c2ecf20Sopenharmony_ci bool ret = true; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ieee80211_stop_queues(sc->hw); 2218c2ecf20Sopenharmony_ci ath_stop_ani(sc); 2228c2ecf20Sopenharmony_ci ath9k_hw_disable_interrupts(ah); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (AR_SREV_9300_20_OR_LATER(ah)) { 2258c2ecf20Sopenharmony_ci ret &= ath_stoprecv(sc); 2268c2ecf20Sopenharmony_ci ret &= ath_drain_all_txq(sc); 2278c2ecf20Sopenharmony_ci } else { 2288c2ecf20Sopenharmony_ci ret &= ath_drain_all_txq(sc); 2298c2ecf20Sopenharmony_ci ret &= ath_stoprecv(sc); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic bool ath_complete_reset(struct ath_softc *sc, bool start) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 2388c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 2398c2ecf20Sopenharmony_ci unsigned long flags; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ath9k_calculate_summary_state(sc, sc->cur_chan); 2428c2ecf20Sopenharmony_ci ath_startrecv(sc); 2438c2ecf20Sopenharmony_ci ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower, 2448c2ecf20Sopenharmony_ci sc->cur_chan->txpower, 2458c2ecf20Sopenharmony_ci &sc->cur_chan->cur_txpower); 2468c2ecf20Sopenharmony_ci clear_bit(ATH_OP_HW_RESET, &common->op_flags); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!sc->cur_chan->offchannel && start) { 2498c2ecf20Sopenharmony_ci /* restore per chanctx TSF timer */ 2508c2ecf20Sopenharmony_ci if (sc->cur_chan->tsf_val) { 2518c2ecf20Sopenharmony_ci u32 offset; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, 2548c2ecf20Sopenharmony_ci NULL); 2558c2ecf20Sopenharmony_ci ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!test_bit(ATH_OP_BEACONS, &common->op_flags)) 2608c2ecf20Sopenharmony_ci goto work; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_STATION && 2638c2ecf20Sopenharmony_ci test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { 2648c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 2658c2ecf20Sopenharmony_ci sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; 2668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci ath9k_set_beacon(sc); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci work: 2718c2ecf20Sopenharmony_ci ath_restart_work(sc); 2728c2ecf20Sopenharmony_ci ath_txq_schedule_all(sc); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci sc->gtt_cnt = 0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 2788c2ecf20Sopenharmony_ci ath9k_hw_enable_interrupts(ah); 2798c2ecf20Sopenharmony_ci ieee80211_wake_queues(sc->hw); 2808c2ecf20Sopenharmony_ci ath9k_p2p_ps_timer(sc); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return true; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 2888c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 2898c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = NULL; 2908c2ecf20Sopenharmony_ci bool fastcc = true; 2918c2ecf20Sopenharmony_ci int r; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci __ath_cancel_work(sc); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci disable_irq(sc->irq); 2968c2ecf20Sopenharmony_ci tasklet_disable(&sc->intr_tq); 2978c2ecf20Sopenharmony_ci tasklet_disable(&sc->bcon_tasklet); 2988c2ecf20Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!sc->cur_chan->offchannel) { 3018c2ecf20Sopenharmony_ci fastcc = false; 3028c2ecf20Sopenharmony_ci caldata = &sc->cur_chan->caldata; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (!hchan) { 3068c2ecf20Sopenharmony_ci fastcc = false; 3078c2ecf20Sopenharmony_ci hchan = ah->curchan; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (!hchan) { 3118c2ecf20Sopenharmony_ci fastcc = false; 3128c2ecf20Sopenharmony_ci hchan = ath9k_cmn_get_channel(sc->hw, ah, &sc->cur_chan->chandef); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (!ath_prepare_reset(sc)) 3168c2ecf20Sopenharmony_ci fastcc = false; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (ath9k_is_chanctx_enabled()) 3198c2ecf20Sopenharmony_ci fastcc = false; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 3228c2ecf20Sopenharmony_ci sc->cur_chandef = sc->cur_chan->chandef; 3238c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n", 3268c2ecf20Sopenharmony_ci hchan->channel, IS_CHAN_HT40(hchan), fastcc); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci r = ath9k_hw_reset(ah, hchan, caldata, fastcc); 3298c2ecf20Sopenharmony_ci if (r) { 3308c2ecf20Sopenharmony_ci ath_err(common, 3318c2ecf20Sopenharmony_ci "Unable to reset channel, reset status %d\n", r); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ath9k_hw_enable_interrupts(ah); 3348c2ecf20Sopenharmony_ci ath9k_queue_reset(sc, RESET_TYPE_BB_HANG); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci goto out; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (ath9k_hw_mci_is_enabled(sc->sc_ah) && 3408c2ecf20Sopenharmony_ci sc->cur_chan->offchannel) 3418c2ecf20Sopenharmony_ci ath9k_mci_set_txpower(sc, true, false); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!ath_complete_reset(sc, true)) 3448c2ecf20Sopenharmony_ci r = -EIO; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ciout: 3478c2ecf20Sopenharmony_ci enable_irq(sc->irq); 3488c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 3498c2ecf20Sopenharmony_ci tasklet_enable(&sc->bcon_tasklet); 3508c2ecf20Sopenharmony_ci tasklet_enable(&sc->intr_tq); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return r; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, 3568c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct ath_node *an; 3598c2ecf20Sopenharmony_ci an = (struct ath_node *)sta->drv_priv; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci an->sc = sc; 3628c2ecf20Sopenharmony_ci an->sta = sta; 3638c2ecf20Sopenharmony_ci an->vif = vif; 3648c2ecf20Sopenharmony_ci memset(&an->key_idx, 0, sizeof(an->key_idx)); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ath_tx_node_init(sc, an); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ath_dynack_node_init(sc->sc_ah, an); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct ath_node *an = (struct ath_node *)sta->drv_priv; 3748c2ecf20Sopenharmony_ci ath_tx_node_cleanup(sc, an); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ath_dynack_node_deinit(sc->sc_ah, an); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_civoid ath9k_tasklet(struct tasklet_struct *t) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct ath_softc *sc = from_tasklet(sc, t, intr_tq); 3828c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 3838c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 3848c2ecf20Sopenharmony_ci enum ath_reset_type type; 3858c2ecf20Sopenharmony_ci unsigned long flags; 3868c2ecf20Sopenharmony_ci u32 status; 3878c2ecf20Sopenharmony_ci u32 rxmask; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->intr_lock, flags); 3908c2ecf20Sopenharmony_ci status = sc->intrstatus; 3918c2ecf20Sopenharmony_ci sc->intrstatus = 0; 3928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->intr_lock, flags); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 3958c2ecf20Sopenharmony_ci spin_lock(&sc->sc_pcu_lock); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (status & ATH9K_INT_FATAL) { 3988c2ecf20Sopenharmony_ci type = RESET_TYPE_FATAL_INT; 3998c2ecf20Sopenharmony_ci ath9k_queue_reset(sc, type); 4008c2ecf20Sopenharmony_ci ath_dbg(common, RESET, "FATAL: Skipping interrupts\n"); 4018c2ecf20Sopenharmony_ci goto out; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) && 4058c2ecf20Sopenharmony_ci (status & ATH9K_INT_BB_WATCHDOG)) { 4068c2ecf20Sopenharmony_ci spin_lock_irqsave(&common->cc_lock, flags); 4078c2ecf20Sopenharmony_ci ath_hw_cycle_counters_update(common); 4088c2ecf20Sopenharmony_ci ar9003_hw_bb_watchdog_dbg_info(ah); 4098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&common->cc_lock, flags); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (ar9003_hw_bb_watchdog_check(ah)) { 4128c2ecf20Sopenharmony_ci type = RESET_TYPE_BB_WATCHDOG; 4138c2ecf20Sopenharmony_ci ath9k_queue_reset(sc, type); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci ath_dbg(common, RESET, 4168c2ecf20Sopenharmony_ci "BB_WATCHDOG: Skipping interrupts\n"); 4178c2ecf20Sopenharmony_ci goto out; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (status & ATH9K_INT_GTT) { 4228c2ecf20Sopenharmony_ci sc->gtt_cnt++; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) { 4258c2ecf20Sopenharmony_ci type = RESET_TYPE_TX_GTT; 4268c2ecf20Sopenharmony_ci ath9k_queue_reset(sc, type); 4278c2ecf20Sopenharmony_ci ath_dbg(common, RESET, 4288c2ecf20Sopenharmony_ci "GTT: Skipping interrupts\n"); 4298c2ecf20Sopenharmony_ci goto out; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 4348c2ecf20Sopenharmony_ci if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) { 4358c2ecf20Sopenharmony_ci /* 4368c2ecf20Sopenharmony_ci * TSF sync does not look correct; remain awake to sync with 4378c2ecf20Sopenharmony_ci * the next Beacon. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci ath_dbg(common, PS, "TSFOOR - Sync with next Beacon\n"); 4408c2ecf20Sopenharmony_ci sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) 4458c2ecf20Sopenharmony_ci rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL | 4468c2ecf20Sopenharmony_ci ATH9K_INT_RXORN); 4478c2ecf20Sopenharmony_ci else 4488c2ecf20Sopenharmony_ci rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (status & rxmask) { 4518c2ecf20Sopenharmony_ci /* Check for high priority Rx first */ 4528c2ecf20Sopenharmony_ci if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && 4538c2ecf20Sopenharmony_ci (status & ATH9K_INT_RXHP)) 4548c2ecf20Sopenharmony_ci ath_rx_tasklet(sc, 0, true); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci ath_rx_tasklet(sc, 0, false); 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (status & ATH9K_INT_TX) { 4608c2ecf20Sopenharmony_ci if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { 4618c2ecf20Sopenharmony_ci /* 4628c2ecf20Sopenharmony_ci * For EDMA chips, TX completion is enabled for the 4638c2ecf20Sopenharmony_ci * beacon queue, so if a beacon has been transmitted 4648c2ecf20Sopenharmony_ci * successfully after a GTT interrupt, the GTT counter 4658c2ecf20Sopenharmony_ci * gets reset to zero here. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci sc->gtt_cnt = 0; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ath_tx_edma_tasklet(sc); 4708c2ecf20Sopenharmony_ci } else { 4718c2ecf20Sopenharmony_ci ath_tx_tasklet(sc); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci wake_up(&sc->tx_wait); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (status & ATH9K_INT_GENTIMER) 4788c2ecf20Sopenharmony_ci ath_gen_timer_isr(sc->sc_ah); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ath9k_btcoex_handle_interrupt(sc, status); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* re-enable hardware interrupt */ 4838c2ecf20Sopenharmony_ci ath9k_hw_resume_interrupts(ah); 4848c2ecf20Sopenharmony_ciout: 4858c2ecf20Sopenharmony_ci spin_unlock(&sc->sc_pcu_lock); 4868c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ciirqreturn_t ath_isr(int irq, void *dev) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci#define SCHED_INTR ( \ 4928c2ecf20Sopenharmony_ci ATH9K_INT_FATAL | \ 4938c2ecf20Sopenharmony_ci ATH9K_INT_BB_WATCHDOG | \ 4948c2ecf20Sopenharmony_ci ATH9K_INT_RXORN | \ 4958c2ecf20Sopenharmony_ci ATH9K_INT_RXEOL | \ 4968c2ecf20Sopenharmony_ci ATH9K_INT_RX | \ 4978c2ecf20Sopenharmony_ci ATH9K_INT_RXLP | \ 4988c2ecf20Sopenharmony_ci ATH9K_INT_RXHP | \ 4998c2ecf20Sopenharmony_ci ATH9K_INT_TX | \ 5008c2ecf20Sopenharmony_ci ATH9K_INT_BMISS | \ 5018c2ecf20Sopenharmony_ci ATH9K_INT_CST | \ 5028c2ecf20Sopenharmony_ci ATH9K_INT_GTT | \ 5038c2ecf20Sopenharmony_ci ATH9K_INT_TSFOOR | \ 5048c2ecf20Sopenharmony_ci ATH9K_INT_GENTIMER | \ 5058c2ecf20Sopenharmony_ci ATH9K_INT_MCI) 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci struct ath_softc *sc = dev; 5088c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 5098c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 5108c2ecf20Sopenharmony_ci enum ath9k_int status; 5118c2ecf20Sopenharmony_ci u32 sync_cause = 0; 5128c2ecf20Sopenharmony_ci bool sched = false; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* 5158c2ecf20Sopenharmony_ci * The hardware is not ready/present, don't 5168c2ecf20Sopenharmony_ci * touch anything. Note this can happen early 5178c2ecf20Sopenharmony_ci * on if the IRQ is shared. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci if (!ah || test_bit(ATH_OP_INVALID, &common->op_flags)) 5208c2ecf20Sopenharmony_ci return IRQ_NONE; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* shared irq, not for us */ 5238c2ecf20Sopenharmony_ci if (!ath9k_hw_intrpend(ah)) 5248c2ecf20Sopenharmony_ci return IRQ_NONE; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * Figure out the reason(s) for the interrupt. Note 5288c2ecf20Sopenharmony_ci * that the hal returns a pseudo-ISR that may include 5298c2ecf20Sopenharmony_ci * bits we haven't explicitly enabled so we mask the 5308c2ecf20Sopenharmony_ci * value to insure we only process bits we requested. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci ath9k_hw_getisr(ah, &status, &sync_cause); /* NB: clears ISR too */ 5338c2ecf20Sopenharmony_ci ath9k_debug_sync_cause(sc, sync_cause); 5348c2ecf20Sopenharmony_ci status &= ah->imask; /* discard unasked-for bits */ 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) { 5378c2ecf20Sopenharmony_ci ath9k_hw_kill_interrupts(sc->sc_ah); 5388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* 5428c2ecf20Sopenharmony_ci * If there are no status bits set, then this interrupt was not 5438c2ecf20Sopenharmony_ci * for me (should have been caught above). 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_ci if (!status) 5468c2ecf20Sopenharmony_ci return IRQ_NONE; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* Cache the status */ 5498c2ecf20Sopenharmony_ci spin_lock(&sc->intr_lock); 5508c2ecf20Sopenharmony_ci sc->intrstatus |= status; 5518c2ecf20Sopenharmony_ci spin_unlock(&sc->intr_lock); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (status & SCHED_INTR) 5548c2ecf20Sopenharmony_ci sched = true; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* 5578c2ecf20Sopenharmony_ci * If a FATAL interrupt is received, we have to reset the chip 5588c2ecf20Sopenharmony_ci * immediately. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ci if (status & ATH9K_INT_FATAL) 5618c2ecf20Sopenharmony_ci goto chip_reset; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) && 5648c2ecf20Sopenharmony_ci (status & ATH9K_INT_BB_WATCHDOG)) 5658c2ecf20Sopenharmony_ci goto chip_reset; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (status & ATH9K_INT_SWBA) 5688c2ecf20Sopenharmony_ci tasklet_schedule(&sc->bcon_tasklet); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (status & ATH9K_INT_TXURN) 5718c2ecf20Sopenharmony_ci ath9k_hw_updatetxtriglevel(ah, true); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (status & ATH9K_INT_RXEOL) { 5748c2ecf20Sopenharmony_ci ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN); 5758c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) 5798c2ecf20Sopenharmony_ci if (status & ATH9K_INT_TIM_TIMER) { 5808c2ecf20Sopenharmony_ci if (ATH_DBG_WARN_ON_ONCE(sc->ps_idle)) 5818c2ecf20Sopenharmony_ci goto chip_reset; 5828c2ecf20Sopenharmony_ci /* Clear RxAbort bit so that we can 5838c2ecf20Sopenharmony_ci * receive frames */ 5848c2ecf20Sopenharmony_ci ath9k_setpower(sc, ATH9K_PM_AWAKE); 5858c2ecf20Sopenharmony_ci spin_lock(&sc->sc_pm_lock); 5868c2ecf20Sopenharmony_ci ath9k_hw_setrxabort(sc->sc_ah, 0); 5878c2ecf20Sopenharmony_ci sc->ps_flags |= PS_WAIT_FOR_BEACON; 5888c2ecf20Sopenharmony_ci spin_unlock(&sc->sc_pm_lock); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cichip_reset: 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ath_debug_stat_interrupt(sc, status); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (sched) { 5968c2ecf20Sopenharmony_ci /* turn off every interrupt */ 5978c2ecf20Sopenharmony_ci ath9k_hw_kill_interrupts(ah); 5988c2ecf20Sopenharmony_ci tasklet_schedule(&sc->intr_tq); 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci#undef SCHED_INTR 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci/* 6078c2ecf20Sopenharmony_ci * This function is called when a HW reset cannot be deferred 6088c2ecf20Sopenharmony_ci * and has to be immediate. 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ciint ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 6138c2ecf20Sopenharmony_ci int r; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci ath9k_hw_kill_interrupts(sc->sc_ah); 6168c2ecf20Sopenharmony_ci set_bit(ATH_OP_HW_RESET, &common->op_flags); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 6198c2ecf20Sopenharmony_ci r = ath_reset_internal(sc, hchan); 6208c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return r; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci/* 6268c2ecf20Sopenharmony_ci * When a HW reset can be deferred, it is added to the 6278c2ecf20Sopenharmony_ci * hw_reset_work workqueue, but we set ATH_OP_HW_RESET before 6288c2ecf20Sopenharmony_ci * queueing. 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_civoid ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 6338c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_DEBUGFS 6348c2ecf20Sopenharmony_ci RESET_STAT_INC(sc, type); 6358c2ecf20Sopenharmony_ci#endif 6368c2ecf20Sopenharmony_ci ath9k_hw_kill_interrupts(sc->sc_ah); 6378c2ecf20Sopenharmony_ci set_bit(ATH_OP_HW_RESET, &common->op_flags); 6388c2ecf20Sopenharmony_ci ieee80211_queue_work(sc->hw, &sc->hw_reset_work); 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_civoid ath_reset_work(struct work_struct *work) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 6468c2ecf20Sopenharmony_ci ath_reset_internal(sc, NULL); 6478c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci/**********************/ 6518c2ecf20Sopenharmony_ci/* mac80211 callbacks */ 6528c2ecf20Sopenharmony_ci/**********************/ 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic int ath9k_start(struct ieee80211_hw *hw) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 6578c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 6588c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 6598c2ecf20Sopenharmony_ci struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan; 6608c2ecf20Sopenharmony_ci struct ath_chanctx *ctx = sc->cur_chan; 6618c2ecf20Sopenharmony_ci struct ath9k_channel *init_channel; 6628c2ecf20Sopenharmony_ci int r; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, 6658c2ecf20Sopenharmony_ci "Starting driver with initial channel: %d MHz\n", 6668c2ecf20Sopenharmony_ci curchan->center_freq); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 6698c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef); 6728c2ecf20Sopenharmony_ci sc->cur_chandef = hw->conf.chandef; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* Reset SERDES registers */ 6758c2ecf20Sopenharmony_ci ath9k_hw_configpcipowersave(ah, false); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* 6788c2ecf20Sopenharmony_ci * The basic interface to setting the hardware in a good 6798c2ecf20Sopenharmony_ci * state is ``reset''. On return the hardware is known to 6808c2ecf20Sopenharmony_ci * be powered up and with interrupts disabled. This must 6818c2ecf20Sopenharmony_ci * be followed by initialization of the appropriate bits 6828c2ecf20Sopenharmony_ci * and then setup of the interrupt mask. 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci atomic_set(&ah->intr_ref_cnt, -1); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci r = ath9k_hw_reset(ah, init_channel, ah->caldata, false); 6898c2ecf20Sopenharmony_ci if (r) { 6908c2ecf20Sopenharmony_ci ath_err(common, 6918c2ecf20Sopenharmony_ci "Unable to reset hardware; reset status %d (freq %u MHz)\n", 6928c2ecf20Sopenharmony_ci r, curchan->center_freq); 6938c2ecf20Sopenharmony_ci ah->reset_power_on = false; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* Setup our intr mask. */ 6978c2ecf20Sopenharmony_ci ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL | 6988c2ecf20Sopenharmony_ci ATH9K_INT_RXORN | ATH9K_INT_FATAL | 6998c2ecf20Sopenharmony_ci ATH9K_INT_GLOBAL; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) 7028c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_RXHP | 7038c2ecf20Sopenharmony_ci ATH9K_INT_RXLP; 7048c2ecf20Sopenharmony_ci else 7058c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_RX; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (ah->config.hw_hang_checks & HW_BB_WATCHDOG) 7088c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_BB_WATCHDOG; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* 7118c2ecf20Sopenharmony_ci * Enable GTT interrupts only for AR9003/AR9004 chips 7128c2ecf20Sopenharmony_ci * for now. 7138c2ecf20Sopenharmony_ci */ 7148c2ecf20Sopenharmony_ci if (AR_SREV_9300_20_OR_LATER(ah)) 7158c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_GTT; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) 7188c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_CST; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci ath_mci_enable(sc); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci clear_bit(ATH_OP_INVALID, &common->op_flags); 7238c2ecf20Sopenharmony_ci sc->sc_ah->is_monitoring = false; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (!ath_complete_reset(sc, false)) 7268c2ecf20Sopenharmony_ci ah->reset_power_on = false; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (ah->led_pin >= 0) { 7298c2ecf20Sopenharmony_ci ath9k_hw_set_gpio(ah, ah->led_pin, 7308c2ecf20Sopenharmony_ci (ah->config.led_active_high) ? 1 : 0); 7318c2ecf20Sopenharmony_ci ath9k_hw_gpio_request_out(ah, ah->led_pin, NULL, 7328c2ecf20Sopenharmony_ci AR_GPIO_OUTPUT_MUX_AS_OUTPUT); 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* 7368c2ecf20Sopenharmony_ci * Reset key cache to sane defaults (all entries cleared) instead of 7378c2ecf20Sopenharmony_ci * semi-random values after suspend/resume. 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_ci ath9k_cmn_init_crypto(sc->sc_ah); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci ath9k_hw_reset_tsf(ah); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci ath9k_rng_start(sc); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return 0; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic void ath9k_tx(struct ieee80211_hw *hw, 7558c2ecf20Sopenharmony_ci struct ieee80211_tx_control *control, 7568c2ecf20Sopenharmony_ci struct sk_buff *skb) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 7598c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 7608c2ecf20Sopenharmony_ci struct ath_tx_control txctl; 7618c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 7628c2ecf20Sopenharmony_ci unsigned long flags; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (sc->ps_enabled) { 7658c2ecf20Sopenharmony_ci /* 7668c2ecf20Sopenharmony_ci * mac80211 does not set PM field for normal data frames, so we 7678c2ecf20Sopenharmony_ci * need to update that based on the current PS mode. 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_ci if (ieee80211_is_data(hdr->frame_control) && 7708c2ecf20Sopenharmony_ci !ieee80211_is_nullfunc(hdr->frame_control) && 7718c2ecf20Sopenharmony_ci !ieee80211_has_pm(hdr->frame_control)) { 7728c2ecf20Sopenharmony_ci ath_dbg(common, PS, 7738c2ecf20Sopenharmony_ci "Add PM=1 for a TX frame while in PS mode\n"); 7748c2ecf20Sopenharmony_ci hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP)) { 7798c2ecf20Sopenharmony_ci /* 7808c2ecf20Sopenharmony_ci * We are using PS-Poll and mac80211 can request TX while in 7818c2ecf20Sopenharmony_ci * power save mode. Need to wake up hardware for the TX to be 7828c2ecf20Sopenharmony_ci * completed and if needed, also for RX of buffered frames. 7838c2ecf20Sopenharmony_ci */ 7848c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 7858c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 7868c2ecf20Sopenharmony_ci if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) 7878c2ecf20Sopenharmony_ci ath9k_hw_setrxabort(sc->sc_ah, 0); 7888c2ecf20Sopenharmony_ci if (ieee80211_is_pspoll(hdr->frame_control)) { 7898c2ecf20Sopenharmony_ci ath_dbg(common, PS, 7908c2ecf20Sopenharmony_ci "Sending PS-Poll to pick a buffered frame\n"); 7918c2ecf20Sopenharmony_ci sc->ps_flags |= PS_WAIT_FOR_PSPOLL_DATA; 7928c2ecf20Sopenharmony_ci } else { 7938c2ecf20Sopenharmony_ci ath_dbg(common, PS, "Wake up to complete TX\n"); 7948c2ecf20Sopenharmony_ci sc->ps_flags |= PS_WAIT_FOR_TX_ACK; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci /* 7978c2ecf20Sopenharmony_ci * The actual restore operation will happen only after 7988c2ecf20Sopenharmony_ci * the ps_flags bit is cleared. We are just dropping 7998c2ecf20Sopenharmony_ci * the ps_usecount here. 8008c2ecf20Sopenharmony_ci */ 8018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 8028c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* 8068c2ecf20Sopenharmony_ci * Cannot tx while the hardware is in full sleep, it first needs a full 8078c2ecf20Sopenharmony_ci * chip reset to recover from that 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_ci if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_FULL_SLEEP)) { 8108c2ecf20Sopenharmony_ci ath_err(common, "TX while HW is in FULL_SLEEP mode\n"); 8118c2ecf20Sopenharmony_ci goto exit; 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci memset(&txctl, 0, sizeof(struct ath_tx_control)); 8158c2ecf20Sopenharmony_ci txctl.txq = sc->tx.txq_map[skb_get_queue_mapping(skb)]; 8168c2ecf20Sopenharmony_ci txctl.sta = control->sta; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci ath_dbg(common, XMIT, "transmitting packet, skb: %p\n", skb); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (ath_tx_start(hw, skb, &txctl) != 0) { 8218c2ecf20Sopenharmony_ci ath_dbg(common, XMIT, "TX failed\n"); 8228c2ecf20Sopenharmony_ci TX_STAT_INC(sc, txctl.txq->axq_qnum, txfailed); 8238c2ecf20Sopenharmony_ci goto exit; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return; 8278c2ecf20Sopenharmony_ciexit: 8288c2ecf20Sopenharmony_ci ieee80211_free_txskb(hw, skb); 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic bool ath9k_txq_list_has_key(struct list_head *txq_list, u32 keyix) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci struct ath_buf *bf; 8348c2ecf20Sopenharmony_ci struct ieee80211_tx_info *txinfo; 8358c2ecf20Sopenharmony_ci struct ath_frame_info *fi; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci list_for_each_entry(bf, txq_list, list) { 8388c2ecf20Sopenharmony_ci if (bf->bf_state.stale || !bf->bf_mpdu) 8398c2ecf20Sopenharmony_ci continue; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci txinfo = IEEE80211_SKB_CB(bf->bf_mpdu); 8428c2ecf20Sopenharmony_ci fi = (struct ath_frame_info *)&txinfo->status.status_driver_data[0]; 8438c2ecf20Sopenharmony_ci if (fi->keyix == keyix) 8448c2ecf20Sopenharmony_ci return true; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return false; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic bool ath9k_txq_has_key(struct ath_softc *sc, u32 keyix) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 8538c2ecf20Sopenharmony_ci int i, j; 8548c2ecf20Sopenharmony_ci struct ath_txq *txq; 8558c2ecf20Sopenharmony_ci bool key_in_use = false; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci for (i = 0; !key_in_use && i < ATH9K_NUM_TX_QUEUES; i++) { 8588c2ecf20Sopenharmony_ci if (!ATH_TXQ_SETUP(sc, i)) 8598c2ecf20Sopenharmony_ci continue; 8608c2ecf20Sopenharmony_ci txq = &sc->tx.txq[i]; 8618c2ecf20Sopenharmony_ci if (!txq->axq_depth) 8628c2ecf20Sopenharmony_ci continue; 8638c2ecf20Sopenharmony_ci if (!ath9k_hw_numtxpending(ah, txq->axq_qnum)) 8648c2ecf20Sopenharmony_ci continue; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci ath_txq_lock(sc, txq); 8678c2ecf20Sopenharmony_ci key_in_use = ath9k_txq_list_has_key(&txq->axq_q, keyix); 8688c2ecf20Sopenharmony_ci if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { 8698c2ecf20Sopenharmony_ci int idx = txq->txq_tailidx; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci for (j = 0; !key_in_use && 8728c2ecf20Sopenharmony_ci !list_empty(&txq->txq_fifo[idx]) && 8738c2ecf20Sopenharmony_ci j < ATH_TXFIFO_DEPTH; j++) { 8748c2ecf20Sopenharmony_ci key_in_use = ath9k_txq_list_has_key( 8758c2ecf20Sopenharmony_ci &txq->txq_fifo[idx], keyix); 8768c2ecf20Sopenharmony_ci INCR(idx, ATH_TXFIFO_DEPTH); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci ath_txq_unlock(sc, txq); 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci return key_in_use; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic void ath9k_pending_key_del(struct ath_softc *sc, u8 keyix) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 8888c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (!test_bit(keyix, ah->pending_del_keymap) || 8918c2ecf20Sopenharmony_ci ath9k_txq_has_key(sc, keyix)) 8928c2ecf20Sopenharmony_ci return; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* No more TXQ frames point to this key cache entry, so delete it. */ 8958c2ecf20Sopenharmony_ci clear_bit(keyix, ah->pending_del_keymap); 8968c2ecf20Sopenharmony_ci ath_key_delete(common, keyix); 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic void ath9k_stop(struct ieee80211_hw *hw) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 9028c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 9038c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 9048c2ecf20Sopenharmony_ci bool prev_idle; 9058c2ecf20Sopenharmony_ci int i; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci ath9k_deinit_channel_context(sc); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci ath9k_rng_stop(sc); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci ath_cancel_work(sc); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (test_bit(ATH_OP_INVALID, &common->op_flags)) { 9168c2ecf20Sopenharmony_ci ath_dbg(common, ANY, "Device not present\n"); 9178c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 9188c2ecf20Sopenharmony_ci return; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* Ensure HW is awake when we try to shut it down. */ 9228c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* prevent tasklets to enable interrupts once we disable them */ 9278c2ecf20Sopenharmony_ci ah->imask &= ~ATH9K_INT_GLOBAL; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* make sure h/w will not generate any interrupt 9308c2ecf20Sopenharmony_ci * before setting the invalid flag. */ 9318c2ecf20Sopenharmony_ci ath9k_hw_disable_interrupts(ah); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* we can now sync irq and kill any running tasklets, since we already 9368c2ecf20Sopenharmony_ci * disabled interrupts and not holding a spin lock */ 9378c2ecf20Sopenharmony_ci synchronize_irq(sc->irq); 9388c2ecf20Sopenharmony_ci tasklet_kill(&sc->intr_tq); 9398c2ecf20Sopenharmony_ci tasklet_kill(&sc->bcon_tasklet); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci prev_idle = sc->ps_idle; 9428c2ecf20Sopenharmony_ci sc->ps_idle = true; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (ah->led_pin >= 0) { 9478c2ecf20Sopenharmony_ci ath9k_hw_set_gpio(ah, ah->led_pin, 9488c2ecf20Sopenharmony_ci (ah->config.led_active_high) ? 0 : 1); 9498c2ecf20Sopenharmony_ci ath9k_hw_gpio_request_in(ah, ah->led_pin, NULL); 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci ath_prepare_reset(sc); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (sc->rx.frag) { 9558c2ecf20Sopenharmony_ci dev_kfree_skb_any(sc->rx.frag); 9568c2ecf20Sopenharmony_ci sc->rx.frag = NULL; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (!ah->curchan) 9608c2ecf20Sopenharmony_ci ah->curchan = ath9k_cmn_get_channel(hw, ah, 9618c2ecf20Sopenharmony_ci &sc->cur_chan->chandef); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci set_bit(ATH_OP_INVALID, &common->op_flags); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci ath9k_hw_phy_disable(ah); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci ath9k_hw_configpcipowersave(ah, true); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci for (i = 0; i < ATH_KEYMAX; i++) 9748c2ecf20Sopenharmony_ci ath9k_pending_key_del(sc, i); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* Clear key cache entries explicitly to get rid of any potentially 9778c2ecf20Sopenharmony_ci * remaining keys. 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_ci ath9k_cmn_init_crypto(sc->sc_ah); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci sc->ps_idle = prev_idle; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Driver halt\n"); 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic bool ath9k_uses_beacons(int type) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci switch (type) { 9938c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 9948c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 9958c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 9968c2ecf20Sopenharmony_ci return true; 9978c2ecf20Sopenharmony_ci default: 9988c2ecf20Sopenharmony_ci return false; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic void ath9k_vif_iter_set_beacon(struct ath9k_vif_iter_data *iter_data, 10038c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci /* Use the first (configured) interface, but prefering AP interfaces. */ 10068c2ecf20Sopenharmony_ci if (!iter_data->primary_beacon_vif) { 10078c2ecf20Sopenharmony_ci iter_data->primary_beacon_vif = vif; 10088c2ecf20Sopenharmony_ci } else { 10098c2ecf20Sopenharmony_ci if (iter_data->primary_beacon_vif->type != NL80211_IFTYPE_AP && 10108c2ecf20Sopenharmony_ci vif->type == NL80211_IFTYPE_AP) 10118c2ecf20Sopenharmony_ci iter_data->primary_beacon_vif = vif; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci iter_data->beacons = true; 10158c2ecf20Sopenharmony_ci iter_data->nbcnvifs += 1; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data, 10198c2ecf20Sopenharmony_ci u8 *mac, struct ieee80211_vif *vif) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct ath_vif *avp = (struct ath_vif *)vif->drv_priv; 10228c2ecf20Sopenharmony_ci int i; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (iter_data->has_hw_macaddr) { 10258c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 10268c2ecf20Sopenharmony_ci iter_data->mask[i] &= 10278c2ecf20Sopenharmony_ci ~(iter_data->hw_macaddr[i] ^ mac[i]); 10288c2ecf20Sopenharmony_ci } else { 10298c2ecf20Sopenharmony_ci memcpy(iter_data->hw_macaddr, mac, ETH_ALEN); 10308c2ecf20Sopenharmony_ci iter_data->has_hw_macaddr = true; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (!vif->bss_conf.use_short_slot) 10348c2ecf20Sopenharmony_ci iter_data->slottime = 20; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci switch (vif->type) { 10378c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 10388c2ecf20Sopenharmony_ci iter_data->naps++; 10398c2ecf20Sopenharmony_ci if (vif->bss_conf.enable_beacon) 10408c2ecf20Sopenharmony_ci ath9k_vif_iter_set_beacon(iter_data, vif); 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 10438c2ecf20Sopenharmony_ci iter_data->nstations++; 10448c2ecf20Sopenharmony_ci if (avp->assoc && !iter_data->primary_sta) 10458c2ecf20Sopenharmony_ci iter_data->primary_sta = vif; 10468c2ecf20Sopenharmony_ci break; 10478c2ecf20Sopenharmony_ci case NL80211_IFTYPE_OCB: 10488c2ecf20Sopenharmony_ci iter_data->nocbs++; 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 10518c2ecf20Sopenharmony_ci iter_data->nadhocs++; 10528c2ecf20Sopenharmony_ci if (vif->bss_conf.enable_beacon) 10538c2ecf20Sopenharmony_ci ath9k_vif_iter_set_beacon(iter_data, vif); 10548c2ecf20Sopenharmony_ci break; 10558c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 10568c2ecf20Sopenharmony_ci iter_data->nmeshes++; 10578c2ecf20Sopenharmony_ci if (vif->bss_conf.enable_beacon) 10588c2ecf20Sopenharmony_ci ath9k_vif_iter_set_beacon(iter_data, vif); 10598c2ecf20Sopenharmony_ci break; 10608c2ecf20Sopenharmony_ci case NL80211_IFTYPE_WDS: 10618c2ecf20Sopenharmony_ci iter_data->nwds++; 10628c2ecf20Sopenharmony_ci break; 10638c2ecf20Sopenharmony_ci default: 10648c2ecf20Sopenharmony_ci break; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic void ath9k_update_bssid_mask(struct ath_softc *sc, 10698c2ecf20Sopenharmony_ci struct ath_chanctx *ctx, 10708c2ecf20Sopenharmony_ci struct ath9k_vif_iter_data *iter_data) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 10738c2ecf20Sopenharmony_ci struct ath_vif *avp; 10748c2ecf20Sopenharmony_ci int i; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (!ath9k_is_chanctx_enabled()) 10778c2ecf20Sopenharmony_ci return; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci list_for_each_entry(avp, &ctx->vifs, list) { 10808c2ecf20Sopenharmony_ci if (ctx->nvifs_assigned != 1) 10818c2ecf20Sopenharmony_ci continue; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (!iter_data->has_hw_macaddr) 10848c2ecf20Sopenharmony_ci continue; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci ether_addr_copy(common->curbssid, avp->bssid); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* perm_addr will be used as the p2p device address. */ 10898c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 10908c2ecf20Sopenharmony_ci iter_data->mask[i] &= 10918c2ecf20Sopenharmony_ci ~(iter_data->hw_macaddr[i] ^ 10928c2ecf20Sopenharmony_ci sc->hw->wiphy->perm_addr[i]); 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci/* Called with sc->mutex held. */ 10978c2ecf20Sopenharmony_civoid ath9k_calculate_iter_data(struct ath_softc *sc, 10988c2ecf20Sopenharmony_ci struct ath_chanctx *ctx, 10998c2ecf20Sopenharmony_ci struct ath9k_vif_iter_data *iter_data) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct ath_vif *avp; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* 11048c2ecf20Sopenharmony_ci * The hardware will use primary station addr together with the 11058c2ecf20Sopenharmony_ci * BSSID mask when matching addresses. 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_ci memset(iter_data, 0, sizeof(*iter_data)); 11088c2ecf20Sopenharmony_ci eth_broadcast_addr(iter_data->mask); 11098c2ecf20Sopenharmony_ci iter_data->slottime = 9; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci list_for_each_entry(avp, &ctx->vifs, list) 11128c2ecf20Sopenharmony_ci ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci ath9k_update_bssid_mask(sc, ctx, iter_data); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic void ath9k_set_assoc_state(struct ath_softc *sc, 11188c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, bool changed) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 11218c2ecf20Sopenharmony_ci struct ath_vif *avp = (struct ath_vif *)vif->drv_priv; 11228c2ecf20Sopenharmony_ci unsigned long flags; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci ether_addr_copy(common->curbssid, avp->bssid); 11278c2ecf20Sopenharmony_ci common->curaid = avp->aid; 11288c2ecf20Sopenharmony_ci ath9k_hw_write_associd(sc->sc_ah); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (changed) { 11318c2ecf20Sopenharmony_ci common->last_rssi = ATH_RSSI_DUMMY_MARKER; 11328c2ecf20Sopenharmony_ci sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 11358c2ecf20Sopenharmony_ci sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; 11368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (ath9k_hw_mci_is_enabled(sc->sc_ah)) 11408c2ecf20Sopenharmony_ci ath9k_mci_update_wlan_channels(sc, false); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, 11438c2ecf20Sopenharmony_ci "Primary Station interface: %pM, BSSID: %pM\n", 11448c2ecf20Sopenharmony_ci vif->addr, common->curbssid); 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT 11488c2ecf20Sopenharmony_cistatic void ath9k_set_offchannel_state(struct ath_softc *sc) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 11518c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 11528c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = NULL; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START) 11578c2ecf20Sopenharmony_ci vif = sc->offchannel.scan_vif; 11588c2ecf20Sopenharmony_ci else 11598c2ecf20Sopenharmony_ci vif = sc->offchannel.roc_vif; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (WARN_ON(!vif)) 11628c2ecf20Sopenharmony_ci goto exit; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci eth_zero_addr(common->curbssid); 11658c2ecf20Sopenharmony_ci eth_broadcast_addr(common->bssidmask); 11668c2ecf20Sopenharmony_ci memcpy(common->macaddr, vif->addr, ETH_ALEN); 11678c2ecf20Sopenharmony_ci common->curaid = 0; 11688c2ecf20Sopenharmony_ci ah->opmode = vif->type; 11698c2ecf20Sopenharmony_ci ah->imask &= ~ATH9K_INT_SWBA; 11708c2ecf20Sopenharmony_ci ah->imask &= ~ATH9K_INT_TSFOOR; 11718c2ecf20Sopenharmony_ci ah->slottime = 9; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci ath_hw_setbssidmask(common); 11748c2ecf20Sopenharmony_ci ath9k_hw_setopmode(ah); 11758c2ecf20Sopenharmony_ci ath9k_hw_write_associd(sc->sc_ah); 11768c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 11778c2ecf20Sopenharmony_ci ath9k_hw_init_global_settings(ah); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ciexit: 11808c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci#endif 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci/* Called with sc->mutex held. */ 11858c2ecf20Sopenharmony_civoid ath9k_calculate_summary_state(struct ath_softc *sc, 11868c2ecf20Sopenharmony_ci struct ath_chanctx *ctx) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 11898c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 11908c2ecf20Sopenharmony_ci struct ath9k_vif_iter_data iter_data; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci ath_chanctx_check_active(sc, ctx); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (ctx != sc->cur_chan) 11958c2ecf20Sopenharmony_ci return; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT 11988c2ecf20Sopenharmony_ci if (ctx == &sc->offchannel.chan) 11998c2ecf20Sopenharmony_ci return ath9k_set_offchannel_state(sc); 12008c2ecf20Sopenharmony_ci#endif 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 12038c2ecf20Sopenharmony_ci ath9k_calculate_iter_data(sc, ctx, &iter_data); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (iter_data.has_hw_macaddr) 12068c2ecf20Sopenharmony_ci memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); 12098c2ecf20Sopenharmony_ci ath_hw_setbssidmask(common); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (iter_data.naps > 0) { 12128c2ecf20Sopenharmony_ci ath9k_hw_set_tsfadjust(ah, true); 12138c2ecf20Sopenharmony_ci ah->opmode = NL80211_IFTYPE_AP; 12148c2ecf20Sopenharmony_ci } else { 12158c2ecf20Sopenharmony_ci ath9k_hw_set_tsfadjust(ah, false); 12168c2ecf20Sopenharmony_ci if (iter_data.beacons) 12178c2ecf20Sopenharmony_ci ath9k_beacon_ensure_primary_slot(sc); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (iter_data.nmeshes) 12208c2ecf20Sopenharmony_ci ah->opmode = NL80211_IFTYPE_MESH_POINT; 12218c2ecf20Sopenharmony_ci else if (iter_data.nocbs) 12228c2ecf20Sopenharmony_ci ah->opmode = NL80211_IFTYPE_OCB; 12238c2ecf20Sopenharmony_ci else if (iter_data.nwds) 12248c2ecf20Sopenharmony_ci ah->opmode = NL80211_IFTYPE_AP; 12258c2ecf20Sopenharmony_ci else if (iter_data.nadhocs) 12268c2ecf20Sopenharmony_ci ah->opmode = NL80211_IFTYPE_ADHOC; 12278c2ecf20Sopenharmony_ci else 12288c2ecf20Sopenharmony_ci ah->opmode = NL80211_IFTYPE_STATION; 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci ath9k_hw_setopmode(ah); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci ctx->switch_after_beacon = false; 12348c2ecf20Sopenharmony_ci if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0) 12358c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_TSFOOR; 12368c2ecf20Sopenharmony_ci else { 12378c2ecf20Sopenharmony_ci ah->imask &= ~ATH9K_INT_TSFOOR; 12388c2ecf20Sopenharmony_ci if (iter_data.naps == 1 && iter_data.beacons) 12398c2ecf20Sopenharmony_ci ctx->switch_after_beacon = true; 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_STATION) { 12438c2ecf20Sopenharmony_ci bool changed = (iter_data.primary_sta != ctx->primary_sta); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if (iter_data.primary_sta) { 12468c2ecf20Sopenharmony_ci iter_data.primary_beacon_vif = iter_data.primary_sta; 12478c2ecf20Sopenharmony_ci iter_data.beacons = true; 12488c2ecf20Sopenharmony_ci ath9k_set_assoc_state(sc, iter_data.primary_sta, 12498c2ecf20Sopenharmony_ci changed); 12508c2ecf20Sopenharmony_ci ctx->primary_sta = iter_data.primary_sta; 12518c2ecf20Sopenharmony_ci } else { 12528c2ecf20Sopenharmony_ci ctx->primary_sta = NULL; 12538c2ecf20Sopenharmony_ci eth_zero_addr(common->curbssid); 12548c2ecf20Sopenharmony_ci common->curaid = 0; 12558c2ecf20Sopenharmony_ci ath9k_hw_write_associd(sc->sc_ah); 12568c2ecf20Sopenharmony_ci if (ath9k_hw_mci_is_enabled(sc->sc_ah)) 12578c2ecf20Sopenharmony_ci ath9k_mci_update_wlan_channels(sc, true); 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci sc->nbcnvifs = iter_data.nbcnvifs; 12618c2ecf20Sopenharmony_ci ath9k_beacon_config(sc, iter_data.primary_beacon_vif, 12628c2ecf20Sopenharmony_ci iter_data.beacons); 12638c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (ah->slottime != iter_data.slottime) { 12668c2ecf20Sopenharmony_ci ah->slottime = iter_data.slottime; 12678c2ecf20Sopenharmony_ci ath9k_hw_init_global_settings(ah); 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (iter_data.primary_sta) 12718c2ecf20Sopenharmony_ci set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); 12728c2ecf20Sopenharmony_ci else 12738c2ecf20Sopenharmony_ci clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, 12768c2ecf20Sopenharmony_ci "macaddr: %pM, bssid: %pM, bssidmask: %pM\n", 12778c2ecf20Sopenharmony_ci common->macaddr, common->curbssid, common->bssidmask); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 12808c2ecf20Sopenharmony_ci} 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic void ath9k_tpc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci int *power = data; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (vif->bss_conf.txpower == INT_MIN) 12878c2ecf20Sopenharmony_ci return; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (*power < vif->bss_conf.txpower) 12908c2ecf20Sopenharmony_ci *power = vif->bss_conf.txpower; 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci/* Called with sc->mutex held. */ 12948c2ecf20Sopenharmony_civoid ath9k_set_txpower(struct ath_softc *sc, struct ieee80211_vif *vif) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci int power; 12978c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 12988c2ecf20Sopenharmony_ci struct ath_regulatory *reg = ath9k_hw_regulatory(ah); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 13018c2ecf20Sopenharmony_ci if (ah->tpc_enabled) { 13028c2ecf20Sopenharmony_ci power = (vif) ? vif->bss_conf.txpower : -1; 13038c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic( 13048c2ecf20Sopenharmony_ci sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, 13058c2ecf20Sopenharmony_ci ath9k_tpc_vif_iter, &power); 13068c2ecf20Sopenharmony_ci if (power == -1) 13078c2ecf20Sopenharmony_ci power = sc->hw->conf.power_level; 13088c2ecf20Sopenharmony_ci } else { 13098c2ecf20Sopenharmony_ci power = sc->hw->conf.power_level; 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci sc->cur_chan->txpower = 2 * power; 13128c2ecf20Sopenharmony_ci ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); 13138c2ecf20Sopenharmony_ci sc->cur_chan->cur_txpower = reg->max_power_level; 13148c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_cistatic void ath9k_assign_hw_queues(struct ieee80211_hw *hw, 13188c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 13198c2ecf20Sopenharmony_ci{ 13208c2ecf20Sopenharmony_ci int i; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (!ath9k_is_chanctx_enabled()) 13238c2ecf20Sopenharmony_ci return; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) 13268c2ecf20Sopenharmony_ci vif->hw_queue[i] = i; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_AP || 13298c2ecf20Sopenharmony_ci vif->type == NL80211_IFTYPE_MESH_POINT) 13308c2ecf20Sopenharmony_ci vif->cab_queue = hw->queues - 2; 13318c2ecf20Sopenharmony_ci else 13328c2ecf20Sopenharmony_ci vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic int ath9k_add_interface(struct ieee80211_hw *hw, 13368c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 13398c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 13408c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 13418c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 13428c2ecf20Sopenharmony_ci struct ath_node *an = &avp->mcast_node; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 13458c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) { 13468c2ecf20Sopenharmony_ci if (sc->cur_chan->nvifs >= 1) { 13478c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 13488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci sc->tx99_vif = vif; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); 13548c2ecf20Sopenharmony_ci sc->cur_chan->nvifs++; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION && ath9k_is_chanctx_enabled()) 13578c2ecf20Sopenharmony_ci vif->driver_flags |= IEEE80211_VIF_GET_NOA_UPDATE; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci if (ath9k_uses_beacons(vif->type)) 13608c2ecf20Sopenharmony_ci ath9k_beacon_assign_slot(sc, vif); 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci avp->vif = vif; 13638c2ecf20Sopenharmony_ci if (!ath9k_is_chanctx_enabled()) { 13648c2ecf20Sopenharmony_ci avp->chanctx = sc->cur_chan; 13658c2ecf20Sopenharmony_ci list_add_tail(&avp->list, &avp->chanctx->vifs); 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci ath9k_calculate_summary_state(sc, avp->chanctx); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci ath9k_assign_hw_queues(hw, vif); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci ath9k_set_txpower(sc, vif); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci an->sc = sc; 13758c2ecf20Sopenharmony_ci an->sta = NULL; 13768c2ecf20Sopenharmony_ci an->vif = vif; 13778c2ecf20Sopenharmony_ci an->no_ps_filter = true; 13788c2ecf20Sopenharmony_ci ath_tx_node_init(sc, an); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 13818c2ecf20Sopenharmony_ci return 0; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic int ath9k_change_interface(struct ieee80211_hw *hw, 13858c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 13868c2ecf20Sopenharmony_ci enum nl80211_iftype new_type, 13878c2ecf20Sopenharmony_ci bool p2p) 13888c2ecf20Sopenharmony_ci{ 13898c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 13908c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 13918c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) { 13968c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 13978c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Change Interface\n"); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci if (ath9k_uses_beacons(vif->type)) 14038c2ecf20Sopenharmony_ci ath9k_beacon_remove_slot(sc, vif); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci vif->type = new_type; 14068c2ecf20Sopenharmony_ci vif->p2p = p2p; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (ath9k_uses_beacons(vif->type)) 14098c2ecf20Sopenharmony_ci ath9k_beacon_assign_slot(sc, vif); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci ath9k_assign_hw_queues(hw, vif); 14128c2ecf20Sopenharmony_ci ath9k_calculate_summary_state(sc, avp->chanctx); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci ath9k_set_txpower(sc, vif); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 14178c2ecf20Sopenharmony_ci return 0; 14188c2ecf20Sopenharmony_ci} 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cistatic void ath9k_remove_interface(struct ieee80211_hw *hw, 14218c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 14228c2ecf20Sopenharmony_ci{ 14238c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 14248c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 14258c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Detach Interface\n"); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci ath9k_p2p_remove_vif(sc, vif); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci sc->cur_chan->nvifs--; 14348c2ecf20Sopenharmony_ci sc->tx99_vif = NULL; 14358c2ecf20Sopenharmony_ci if (!ath9k_is_chanctx_enabled()) 14368c2ecf20Sopenharmony_ci list_del(&avp->list); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci if (ath9k_uses_beacons(vif->type)) 14398c2ecf20Sopenharmony_ci ath9k_beacon_remove_slot(sc, vif); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci ath_tx_node_cleanup(sc, &avp->mcast_node); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci ath9k_calculate_summary_state(sc, avp->chanctx); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci ath9k_set_txpower(sc, NULL); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_cistatic void ath9k_enable_ps(struct ath_softc *sc) 14518c2ecf20Sopenharmony_ci{ 14528c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 14538c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) 14568c2ecf20Sopenharmony_ci return; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci sc->ps_enabled = true; 14598c2ecf20Sopenharmony_ci if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { 14608c2ecf20Sopenharmony_ci if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) { 14618c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_TIM_TIMER; 14628c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci ath9k_hw_setrxabort(ah, 1); 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci ath_dbg(common, PS, "PowerSave enabled\n"); 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic void ath9k_disable_ps(struct ath_softc *sc) 14708c2ecf20Sopenharmony_ci{ 14718c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 14728c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) 14758c2ecf20Sopenharmony_ci return; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci sc->ps_enabled = false; 14788c2ecf20Sopenharmony_ci ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); 14798c2ecf20Sopenharmony_ci if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { 14808c2ecf20Sopenharmony_ci ath9k_hw_setrxabort(ah, 0); 14818c2ecf20Sopenharmony_ci sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | 14828c2ecf20Sopenharmony_ci PS_WAIT_FOR_CAB | 14838c2ecf20Sopenharmony_ci PS_WAIT_FOR_PSPOLL_DATA | 14848c2ecf20Sopenharmony_ci PS_WAIT_FOR_TX_ACK); 14858c2ecf20Sopenharmony_ci if (ah->imask & ATH9K_INT_TIM_TIMER) { 14868c2ecf20Sopenharmony_ci ah->imask &= ~ATH9K_INT_TIM_TIMER; 14878c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci ath_dbg(common, PS, "PowerSave disabled\n"); 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic int ath9k_config(struct ieee80211_hw *hw, u32 changed) 14948c2ecf20Sopenharmony_ci{ 14958c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 14968c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 14978c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 14988c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 14998c2ecf20Sopenharmony_ci struct ath_chanctx *ctx = sc->cur_chan; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 15028c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_IDLE) { 15058c2ecf20Sopenharmony_ci sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); 15068c2ecf20Sopenharmony_ci if (sc->ps_idle) { 15078c2ecf20Sopenharmony_ci ath_cancel_work(sc); 15088c2ecf20Sopenharmony_ci ath9k_stop_btcoex(sc); 15098c2ecf20Sopenharmony_ci } else { 15108c2ecf20Sopenharmony_ci ath9k_start_btcoex(sc); 15118c2ecf20Sopenharmony_ci /* 15128c2ecf20Sopenharmony_ci * The chip needs a reset to properly wake up from 15138c2ecf20Sopenharmony_ci * full sleep 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_ci ath_chanctx_set_channel(sc, ctx, &ctx->chandef); 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci /* 15208c2ecf20Sopenharmony_ci * We just prepare to enable PS. We have to wait until our AP has 15218c2ecf20Sopenharmony_ci * ACK'd our null data frame to disable RX otherwise we'll ignore 15228c2ecf20Sopenharmony_ci * those ACKs and end up retransmitting the same null data frames. 15238c2ecf20Sopenharmony_ci * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode. 15248c2ecf20Sopenharmony_ci */ 15258c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_PS) { 15268c2ecf20Sopenharmony_ci unsigned long flags; 15278c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 15288c2ecf20Sopenharmony_ci if (conf->flags & IEEE80211_CONF_PS) 15298c2ecf20Sopenharmony_ci ath9k_enable_ps(sc); 15308c2ecf20Sopenharmony_ci else 15318c2ecf20Sopenharmony_ci ath9k_disable_ps(sc); 15328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_MONITOR) { 15368c2ecf20Sopenharmony_ci if (conf->flags & IEEE80211_CONF_MONITOR) { 15378c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Monitor mode is enabled\n"); 15388c2ecf20Sopenharmony_ci sc->sc_ah->is_monitoring = true; 15398c2ecf20Sopenharmony_ci } else { 15408c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Monitor mode is disabled\n"); 15418c2ecf20Sopenharmony_ci sc->sc_ah->is_monitoring = false; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (!ath9k_is_chanctx_enabled() && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { 15468c2ecf20Sopenharmony_ci ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL); 15478c2ecf20Sopenharmony_ci ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef); 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci if (changed & IEEE80211_CONF_CHANGE_POWER) 15518c2ecf20Sopenharmony_ci ath9k_set_txpower(sc, NULL); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 15548c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci return 0; 15578c2ecf20Sopenharmony_ci} 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci#define SUPPORTED_FILTERS \ 15608c2ecf20Sopenharmony_ci (FIF_ALLMULTI | \ 15618c2ecf20Sopenharmony_ci FIF_CONTROL | \ 15628c2ecf20Sopenharmony_ci FIF_PSPOLL | \ 15638c2ecf20Sopenharmony_ci FIF_OTHER_BSS | \ 15648c2ecf20Sopenharmony_ci FIF_BCN_PRBRESP_PROMISC | \ 15658c2ecf20Sopenharmony_ci FIF_PROBE_REQ | \ 15668c2ecf20Sopenharmony_ci FIF_MCAST_ACTION | \ 15678c2ecf20Sopenharmony_ci FIF_FCSFAIL) 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci/* FIXME: sc->sc_full_reset ? */ 15708c2ecf20Sopenharmony_cistatic void ath9k_configure_filter(struct ieee80211_hw *hw, 15718c2ecf20Sopenharmony_ci unsigned int changed_flags, 15728c2ecf20Sopenharmony_ci unsigned int *total_flags, 15738c2ecf20Sopenharmony_ci u64 multicast) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 15768c2ecf20Sopenharmony_ci struct ath_chanctx *ctx; 15778c2ecf20Sopenharmony_ci u32 rfilt; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci changed_flags &= SUPPORTED_FILTERS; 15808c2ecf20Sopenharmony_ci *total_flags &= SUPPORTED_FILTERS; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 15838c2ecf20Sopenharmony_ci ath_for_each_chanctx(sc, ctx) 15848c2ecf20Sopenharmony_ci ctx->rxfilter = *total_flags; 15858c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT 15868c2ecf20Sopenharmony_ci sc->offchannel.chan.rxfilter = *total_flags; 15878c2ecf20Sopenharmony_ci#endif 15888c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 15918c2ecf20Sopenharmony_ci rfilt = ath_calcrxfilter(sc); 15928c2ecf20Sopenharmony_ci ath9k_hw_setrxfilter(sc->sc_ah, rfilt); 15938c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG, "Set HW RX filter: 0x%x\n", 15968c2ecf20Sopenharmony_ci rfilt); 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_cistatic int ath9k_sta_add(struct ieee80211_hw *hw, 16008c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 16018c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 16048c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 16058c2ecf20Sopenharmony_ci struct ath_node *an = (struct ath_node *) sta->drv_priv; 16068c2ecf20Sopenharmony_ci struct ieee80211_key_conf ps_key = { }; 16078c2ecf20Sopenharmony_ci int key; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci ath_node_attach(sc, sta, vif); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (vif->type != NL80211_IFTYPE_AP && 16128c2ecf20Sopenharmony_ci vif->type != NL80211_IFTYPE_AP_VLAN) 16138c2ecf20Sopenharmony_ci return 0; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci key = ath_key_config(common, vif, sta, &ps_key); 16168c2ecf20Sopenharmony_ci if (key > 0) { 16178c2ecf20Sopenharmony_ci an->ps_key = key; 16188c2ecf20Sopenharmony_ci an->key_idx[0] = key; 16198c2ecf20Sopenharmony_ci } 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci return 0; 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistatic void ath9k_del_ps_key(struct ath_softc *sc, 16258c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 16268c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 16278c2ecf20Sopenharmony_ci{ 16288c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 16298c2ecf20Sopenharmony_ci struct ath_node *an = (struct ath_node *) sta->drv_priv; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (!an->ps_key) 16328c2ecf20Sopenharmony_ci return; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci ath_key_delete(common, an->ps_key); 16358c2ecf20Sopenharmony_ci an->ps_key = 0; 16368c2ecf20Sopenharmony_ci an->key_idx[0] = 0; 16378c2ecf20Sopenharmony_ci} 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_cistatic int ath9k_sta_remove(struct ieee80211_hw *hw, 16408c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 16418c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 16428c2ecf20Sopenharmony_ci{ 16438c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci ath9k_del_ps_key(sc, vif, sta); 16468c2ecf20Sopenharmony_ci ath_node_detach(sc, sta); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci return 0; 16498c2ecf20Sopenharmony_ci} 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic int ath9k_sta_state(struct ieee80211_hw *hw, 16528c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 16538c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 16548c2ecf20Sopenharmony_ci enum ieee80211_sta_state old_state, 16558c2ecf20Sopenharmony_ci enum ieee80211_sta_state new_state) 16568c2ecf20Sopenharmony_ci{ 16578c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 16588c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 16598c2ecf20Sopenharmony_ci int ret = 0; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci if (old_state == IEEE80211_STA_NOTEXIST && 16628c2ecf20Sopenharmony_ci new_state == IEEE80211_STA_NONE) { 16638c2ecf20Sopenharmony_ci ret = ath9k_sta_add(hw, vif, sta); 16648c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, 16658c2ecf20Sopenharmony_ci "Add station: %pM\n", sta->addr); 16668c2ecf20Sopenharmony_ci } else if (old_state == IEEE80211_STA_NONE && 16678c2ecf20Sopenharmony_ci new_state == IEEE80211_STA_NOTEXIST) { 16688c2ecf20Sopenharmony_ci ret = ath9k_sta_remove(hw, vif, sta); 16698c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, 16708c2ecf20Sopenharmony_ci "Remove station: %pM\n", sta->addr); 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci if (ath9k_is_chanctx_enabled()) { 16748c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_STATION) { 16758c2ecf20Sopenharmony_ci if (old_state == IEEE80211_STA_ASSOC && 16768c2ecf20Sopenharmony_ci new_state == IEEE80211_STA_AUTHORIZED) 16778c2ecf20Sopenharmony_ci ath_chanctx_event(sc, vif, 16788c2ecf20Sopenharmony_ci ATH_CHANCTX_EVENT_AUTHORIZED); 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci return ret; 16838c2ecf20Sopenharmony_ci} 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_cistatic void ath9k_sta_set_tx_filter(struct ath_hw *ah, 16868c2ecf20Sopenharmony_ci struct ath_node *an, 16878c2ecf20Sopenharmony_ci bool set) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci int i; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { 16928c2ecf20Sopenharmony_ci if (!an->key_idx[i]) 16938c2ecf20Sopenharmony_ci continue; 16948c2ecf20Sopenharmony_ci ath9k_hw_set_tx_filter(ah, an->key_idx[i], set); 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci} 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_cistatic void ath9k_sta_notify(struct ieee80211_hw *hw, 16998c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 17008c2ecf20Sopenharmony_ci enum sta_notify_cmd cmd, 17018c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 17048c2ecf20Sopenharmony_ci struct ath_node *an = (struct ath_node *) sta->drv_priv; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci switch (cmd) { 17078c2ecf20Sopenharmony_ci case STA_NOTIFY_SLEEP: 17088c2ecf20Sopenharmony_ci an->sleeping = true; 17098c2ecf20Sopenharmony_ci ath_tx_aggr_sleep(sta, sc, an); 17108c2ecf20Sopenharmony_ci ath9k_sta_set_tx_filter(sc->sc_ah, an, true); 17118c2ecf20Sopenharmony_ci break; 17128c2ecf20Sopenharmony_ci case STA_NOTIFY_AWAKE: 17138c2ecf20Sopenharmony_ci ath9k_sta_set_tx_filter(sc->sc_ah, an, false); 17148c2ecf20Sopenharmony_ci an->sleeping = false; 17158c2ecf20Sopenharmony_ci ath_tx_aggr_wakeup(sc, an); 17168c2ecf20Sopenharmony_ci break; 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci} 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_cistatic int ath9k_conf_tx(struct ieee80211_hw *hw, 17218c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, u16 queue, 17228c2ecf20Sopenharmony_ci const struct ieee80211_tx_queue_params *params) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 17258c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 17268c2ecf20Sopenharmony_ci struct ath_txq *txq; 17278c2ecf20Sopenharmony_ci struct ath9k_tx_queue_info qi; 17288c2ecf20Sopenharmony_ci int ret = 0; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci if (queue >= IEEE80211_NUM_ACS) 17318c2ecf20Sopenharmony_ci return 0; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci txq = sc->tx.txq_map[queue]; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 17368c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci qi.tqi_aifs = params->aifs; 17418c2ecf20Sopenharmony_ci qi.tqi_cwmin = params->cw_min; 17428c2ecf20Sopenharmony_ci qi.tqi_cwmax = params->cw_max; 17438c2ecf20Sopenharmony_ci qi.tqi_burstTime = params->txop * 32; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, 17468c2ecf20Sopenharmony_ci "Configure tx [queue/halq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", 17478c2ecf20Sopenharmony_ci queue, txq->axq_qnum, params->aifs, params->cw_min, 17488c2ecf20Sopenharmony_ci params->cw_max, params->txop); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci ath_update_max_aggr_framelen(sc, queue, qi.tqi_burstTime); 17518c2ecf20Sopenharmony_ci ret = ath_txq_update(sc, txq->axq_qnum, &qi); 17528c2ecf20Sopenharmony_ci if (ret) 17538c2ecf20Sopenharmony_ci ath_err(common, "TXQ Update failed\n"); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 17568c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci return ret; 17598c2ecf20Sopenharmony_ci} 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_cistatic int ath9k_set_key(struct ieee80211_hw *hw, 17628c2ecf20Sopenharmony_ci enum set_key_cmd cmd, 17638c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 17648c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 17658c2ecf20Sopenharmony_ci struct ieee80211_key_conf *key) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 17688c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 17698c2ecf20Sopenharmony_ci struct ath_node *an = NULL; 17708c2ecf20Sopenharmony_ci int ret = 0, i; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (ath9k_modparam_nohwcrypt) 17738c2ecf20Sopenharmony_ci return -ENOSPC; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci if ((vif->type == NL80211_IFTYPE_ADHOC || 17768c2ecf20Sopenharmony_ci vif->type == NL80211_IFTYPE_MESH_POINT) && 17778c2ecf20Sopenharmony_ci (key->cipher == WLAN_CIPHER_SUITE_TKIP || 17788c2ecf20Sopenharmony_ci key->cipher == WLAN_CIPHER_SUITE_CCMP) && 17798c2ecf20Sopenharmony_ci !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { 17808c2ecf20Sopenharmony_ci /* 17818c2ecf20Sopenharmony_ci * For now, disable hw crypto for the RSN IBSS group keys. This 17828c2ecf20Sopenharmony_ci * could be optimized in the future to use a modified key cache 17838c2ecf20Sopenharmony_ci * design to support per-STA RX GTK, but until that gets 17848c2ecf20Sopenharmony_ci * implemented, use of software crypto for group addressed 17858c2ecf20Sopenharmony_ci * frames is a acceptable to allow RSN IBSS to be used. 17868c2ecf20Sopenharmony_ci */ 17878c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci /* There may be MPDUs queued for the outgoing PTK key. Flush queues to 17918c2ecf20Sopenharmony_ci * make sure these are not send unencrypted or with a wrong (new) key 17928c2ecf20Sopenharmony_ci */ 17938c2ecf20Sopenharmony_ci if (cmd == DISABLE_KEY && key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { 17948c2ecf20Sopenharmony_ci ieee80211_stop_queues(hw); 17958c2ecf20Sopenharmony_ci ath9k_flush(hw, vif, 0, true); 17968c2ecf20Sopenharmony_ci ieee80211_wake_queues(hw); 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 18008c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 18018c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Set HW Key %d\n", cmd); 18028c2ecf20Sopenharmony_ci if (sta) 18038c2ecf20Sopenharmony_ci an = (struct ath_node *)sta->drv_priv; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci /* Delete pending key cache entries if no more frames are pointing to 18068c2ecf20Sopenharmony_ci * them in TXQs. 18078c2ecf20Sopenharmony_ci */ 18088c2ecf20Sopenharmony_ci for (i = 0; i < ATH_KEYMAX; i++) 18098c2ecf20Sopenharmony_ci ath9k_pending_key_del(sc, i); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci switch (cmd) { 18128c2ecf20Sopenharmony_ci case SET_KEY: 18138c2ecf20Sopenharmony_ci if (sta) 18148c2ecf20Sopenharmony_ci ath9k_del_ps_key(sc, vif, sta); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci key->hw_key_idx = 0; 18178c2ecf20Sopenharmony_ci ret = ath_key_config(common, vif, sta, key); 18188c2ecf20Sopenharmony_ci if (ret >= 0) { 18198c2ecf20Sopenharmony_ci key->hw_key_idx = ret; 18208c2ecf20Sopenharmony_ci /* push IV and Michael MIC generation to stack */ 18218c2ecf20Sopenharmony_ci key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; 18228c2ecf20Sopenharmony_ci if (key->cipher == WLAN_CIPHER_SUITE_TKIP) 18238c2ecf20Sopenharmony_ci key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; 18248c2ecf20Sopenharmony_ci if (sc->sc_ah->sw_mgmt_crypto_tx && 18258c2ecf20Sopenharmony_ci key->cipher == WLAN_CIPHER_SUITE_CCMP) 18268c2ecf20Sopenharmony_ci key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; 18278c2ecf20Sopenharmony_ci ret = 0; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci if (an && key->hw_key_idx) { 18308c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { 18318c2ecf20Sopenharmony_ci if (an->key_idx[i]) 18328c2ecf20Sopenharmony_ci continue; 18338c2ecf20Sopenharmony_ci an->key_idx[i] = key->hw_key_idx; 18348c2ecf20Sopenharmony_ci break; 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci WARN_ON(i == ARRAY_SIZE(an->key_idx)); 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci break; 18398c2ecf20Sopenharmony_ci case DISABLE_KEY: 18408c2ecf20Sopenharmony_ci if (ath9k_txq_has_key(sc, key->hw_key_idx)) { 18418c2ecf20Sopenharmony_ci /* Delay key cache entry deletion until there are no 18428c2ecf20Sopenharmony_ci * remaining TXQ frames pointing to this entry. 18438c2ecf20Sopenharmony_ci */ 18448c2ecf20Sopenharmony_ci set_bit(key->hw_key_idx, sc->sc_ah->pending_del_keymap); 18458c2ecf20Sopenharmony_ci ath_hw_keysetmac(common, key->hw_key_idx, NULL); 18468c2ecf20Sopenharmony_ci } else { 18478c2ecf20Sopenharmony_ci ath_key_delete(common, key->hw_key_idx); 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci if (an) { 18508c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { 18518c2ecf20Sopenharmony_ci if (an->key_idx[i] != key->hw_key_idx) 18528c2ecf20Sopenharmony_ci continue; 18538c2ecf20Sopenharmony_ci an->key_idx[i] = 0; 18548c2ecf20Sopenharmony_ci break; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci } 18578c2ecf20Sopenharmony_ci key->hw_key_idx = 0; 18588c2ecf20Sopenharmony_ci break; 18598c2ecf20Sopenharmony_ci default: 18608c2ecf20Sopenharmony_ci ret = -EINVAL; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 18648c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci return ret; 18678c2ecf20Sopenharmony_ci} 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_cistatic void ath9k_bss_info_changed(struct ieee80211_hw *hw, 18708c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 18718c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *bss_conf, 18728c2ecf20Sopenharmony_ci u32 changed) 18738c2ecf20Sopenharmony_ci{ 18748c2ecf20Sopenharmony_ci#define CHECK_ANI \ 18758c2ecf20Sopenharmony_ci (BSS_CHANGED_ASSOC | \ 18768c2ecf20Sopenharmony_ci BSS_CHANGED_IBSS | \ 18778c2ecf20Sopenharmony_ci BSS_CHANGED_BEACON_ENABLED) 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 18808c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 18818c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 18828c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 18838c2ecf20Sopenharmony_ci int slottime; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 18868c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_ASSOC) { 18898c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n", 18908c2ecf20Sopenharmony_ci bss_conf->bssid, bss_conf->assoc); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN); 18938c2ecf20Sopenharmony_ci avp->aid = bss_conf->aid; 18948c2ecf20Sopenharmony_ci avp->assoc = bss_conf->assoc; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci ath9k_calculate_summary_state(sc, avp->chanctx); 18978c2ecf20Sopenharmony_ci } 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci if ((changed & BSS_CHANGED_IBSS) || 19008c2ecf20Sopenharmony_ci (changed & BSS_CHANGED_OCB)) { 19018c2ecf20Sopenharmony_ci memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); 19028c2ecf20Sopenharmony_ci common->curaid = bss_conf->aid; 19038c2ecf20Sopenharmony_ci ath9k_hw_write_associd(sc->sc_ah); 19048c2ecf20Sopenharmony_ci } 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci if ((changed & BSS_CHANGED_BEACON_ENABLED) || 19078c2ecf20Sopenharmony_ci (changed & BSS_CHANGED_BEACON_INT) || 19088c2ecf20Sopenharmony_ci (changed & BSS_CHANGED_BEACON_INFO)) { 19098c2ecf20Sopenharmony_ci ath9k_calculate_summary_state(sc, avp->chanctx); 19108c2ecf20Sopenharmony_ci } 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci if ((avp->chanctx == sc->cur_chan) && 19138c2ecf20Sopenharmony_ci (changed & BSS_CHANGED_ERP_SLOT)) { 19148c2ecf20Sopenharmony_ci if (bss_conf->use_short_slot) 19158c2ecf20Sopenharmony_ci slottime = 9; 19168c2ecf20Sopenharmony_ci else 19178c2ecf20Sopenharmony_ci slottime = 20; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci if (vif->type == NL80211_IFTYPE_AP) { 19208c2ecf20Sopenharmony_ci /* 19218c2ecf20Sopenharmony_ci * Defer update, so that connected stations can adjust 19228c2ecf20Sopenharmony_ci * their settings at the same time. 19238c2ecf20Sopenharmony_ci * See beacon.c for more details 19248c2ecf20Sopenharmony_ci */ 19258c2ecf20Sopenharmony_ci sc->beacon.slottime = slottime; 19268c2ecf20Sopenharmony_ci sc->beacon.updateslot = UPDATE; 19278c2ecf20Sopenharmony_ci } else { 19288c2ecf20Sopenharmony_ci ah->slottime = slottime; 19298c2ecf20Sopenharmony_ci ath9k_hw_init_global_settings(ah); 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci } 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_P2P_PS) 19348c2ecf20Sopenharmony_ci ath9k_p2p_bss_info_changed(sc, vif); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci if (changed & CHECK_ANI) 19378c2ecf20Sopenharmony_ci ath_check_ani(sc); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci if (changed & BSS_CHANGED_TXPOWER) { 19408c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "vif %pM power %d dbm power_type %d\n", 19418c2ecf20Sopenharmony_ci vif->addr, bss_conf->txpower, bss_conf->txpower_type); 19428c2ecf20Sopenharmony_ci ath9k_set_txpower(sc, vif); 19438c2ecf20Sopenharmony_ci } 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 19468c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci#undef CHECK_ANI 19498c2ecf20Sopenharmony_ci} 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_cistatic u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 19528c2ecf20Sopenharmony_ci{ 19538c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 19548c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 19558c2ecf20Sopenharmony_ci u64 tsf; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 19588c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 19598c2ecf20Sopenharmony_ci /* Get current TSF either from HW or kernel time. */ 19608c2ecf20Sopenharmony_ci if (sc->cur_chan == avp->chanctx) { 19618c2ecf20Sopenharmony_ci tsf = ath9k_hw_gettsf64(sc->sc_ah); 19628c2ecf20Sopenharmony_ci } else { 19638c2ecf20Sopenharmony_ci tsf = sc->cur_chan->tsf_val + 19648c2ecf20Sopenharmony_ci ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci tsf += le64_to_cpu(avp->tsf_adjust); 19678c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 19688c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci return tsf; 19718c2ecf20Sopenharmony_ci} 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_cistatic void ath9k_set_tsf(struct ieee80211_hw *hw, 19748c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 19758c2ecf20Sopenharmony_ci u64 tsf) 19768c2ecf20Sopenharmony_ci{ 19778c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 19788c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 19818c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 19828c2ecf20Sopenharmony_ci tsf -= le64_to_cpu(avp->tsf_adjust); 19838c2ecf20Sopenharmony_ci ktime_get_raw_ts64(&avp->chanctx->tsf_ts); 19848c2ecf20Sopenharmony_ci if (sc->cur_chan == avp->chanctx) 19858c2ecf20Sopenharmony_ci ath9k_hw_settsf64(sc->sc_ah, tsf); 19868c2ecf20Sopenharmony_ci avp->chanctx->tsf_val = tsf; 19878c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 19888c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_cistatic void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) 19928c2ecf20Sopenharmony_ci{ 19938c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 19948c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 19998c2ecf20Sopenharmony_ci ktime_get_raw_ts64(&avp->chanctx->tsf_ts); 20008c2ecf20Sopenharmony_ci if (sc->cur_chan == avp->chanctx) 20018c2ecf20Sopenharmony_ci ath9k_hw_reset_tsf(sc->sc_ah); 20028c2ecf20Sopenharmony_ci avp->chanctx->tsf_val = 0; 20038c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 20068c2ecf20Sopenharmony_ci} 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_cistatic int ath9k_ampdu_action(struct ieee80211_hw *hw, 20098c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 20108c2ecf20Sopenharmony_ci struct ieee80211_ampdu_params *params) 20118c2ecf20Sopenharmony_ci{ 20128c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 20138c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 20148c2ecf20Sopenharmony_ci bool flush = false; 20158c2ecf20Sopenharmony_ci int ret = 0; 20168c2ecf20Sopenharmony_ci struct ieee80211_sta *sta = params->sta; 20178c2ecf20Sopenharmony_ci struct ath_node *an = (struct ath_node *)sta->drv_priv; 20188c2ecf20Sopenharmony_ci enum ieee80211_ampdu_mlme_action action = params->action; 20198c2ecf20Sopenharmony_ci u16 tid = params->tid; 20208c2ecf20Sopenharmony_ci u16 *ssn = ¶ms->ssn; 20218c2ecf20Sopenharmony_ci struct ath_atx_tid *atid; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci switch (action) { 20268c2ecf20Sopenharmony_ci case IEEE80211_AMPDU_RX_START: 20278c2ecf20Sopenharmony_ci break; 20288c2ecf20Sopenharmony_ci case IEEE80211_AMPDU_RX_STOP: 20298c2ecf20Sopenharmony_ci break; 20308c2ecf20Sopenharmony_ci case IEEE80211_AMPDU_TX_START: 20318c2ecf20Sopenharmony_ci if (ath9k_is_chanctx_enabled()) { 20328c2ecf20Sopenharmony_ci if (test_bit(ATH_OP_SCANNING, &common->op_flags)) { 20338c2ecf20Sopenharmony_ci ret = -EBUSY; 20348c2ecf20Sopenharmony_ci break; 20358c2ecf20Sopenharmony_ci } 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 20388c2ecf20Sopenharmony_ci ret = ath_tx_aggr_start(sc, sta, tid, ssn); 20398c2ecf20Sopenharmony_ci if (!ret) 20408c2ecf20Sopenharmony_ci ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; 20418c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 20428c2ecf20Sopenharmony_ci break; 20438c2ecf20Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_FLUSH: 20448c2ecf20Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: 20458c2ecf20Sopenharmony_ci flush = true; 20468c2ecf20Sopenharmony_ci fallthrough; 20478c2ecf20Sopenharmony_ci case IEEE80211_AMPDU_TX_STOP_CONT: 20488c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 20498c2ecf20Sopenharmony_ci ath_tx_aggr_stop(sc, sta, tid); 20508c2ecf20Sopenharmony_ci if (!flush) 20518c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 20528c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 20538c2ecf20Sopenharmony_ci break; 20548c2ecf20Sopenharmony_ci case IEEE80211_AMPDU_TX_OPERATIONAL: 20558c2ecf20Sopenharmony_ci atid = ath_node_to_tid(an, tid); 20568c2ecf20Sopenharmony_ci atid->baw_size = IEEE80211_MIN_AMPDU_BUF << 20578c2ecf20Sopenharmony_ci sta->ht_cap.ampdu_factor; 20588c2ecf20Sopenharmony_ci break; 20598c2ecf20Sopenharmony_ci default: 20608c2ecf20Sopenharmony_ci ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n"); 20618c2ecf20Sopenharmony_ci } 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci return ret; 20668c2ecf20Sopenharmony_ci} 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_cistatic int ath9k_get_survey(struct ieee80211_hw *hw, int idx, 20698c2ecf20Sopenharmony_ci struct survey_info *survey) 20708c2ecf20Sopenharmony_ci{ 20718c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 20728c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 20738c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 20748c2ecf20Sopenharmony_ci struct ieee80211_channel *chan; 20758c2ecf20Sopenharmony_ci unsigned long flags; 20768c2ecf20Sopenharmony_ci int pos; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) 20798c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci spin_lock_irqsave(&common->cc_lock, flags); 20828c2ecf20Sopenharmony_ci if (idx == 0) 20838c2ecf20Sopenharmony_ci ath_update_survey_stats(sc); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci sband = hw->wiphy->bands[NL80211_BAND_2GHZ]; 20868c2ecf20Sopenharmony_ci if (sband && idx >= sband->n_channels) { 20878c2ecf20Sopenharmony_ci idx -= sband->n_channels; 20888c2ecf20Sopenharmony_ci sband = NULL; 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci if (!sband) 20928c2ecf20Sopenharmony_ci sband = hw->wiphy->bands[NL80211_BAND_5GHZ]; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci if (!sband || idx >= sband->n_channels) { 20958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&common->cc_lock, flags); 20968c2ecf20Sopenharmony_ci return -ENOENT; 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci chan = &sband->channels[idx]; 21008c2ecf20Sopenharmony_ci pos = chan->hw_value; 21018c2ecf20Sopenharmony_ci memcpy(survey, &sc->survey[pos], sizeof(*survey)); 21028c2ecf20Sopenharmony_ci survey->channel = chan; 21038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&common->cc_lock, flags); 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci return 0; 21068c2ecf20Sopenharmony_ci} 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_cistatic void ath9k_enable_dynack(struct ath_softc *sc) 21098c2ecf20Sopenharmony_ci{ 21108c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_DYNACK 21118c2ecf20Sopenharmony_ci u32 rfilt; 21128c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci ath_dynack_reset(ah); 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci ah->dynack.enabled = true; 21178c2ecf20Sopenharmony_ci rfilt = ath_calcrxfilter(sc); 21188c2ecf20Sopenharmony_ci ath9k_hw_setrxfilter(ah, rfilt); 21198c2ecf20Sopenharmony_ci#endif 21208c2ecf20Sopenharmony_ci} 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_cistatic void ath9k_set_coverage_class(struct ieee80211_hw *hw, 21238c2ecf20Sopenharmony_ci s16 coverage_class) 21248c2ecf20Sopenharmony_ci{ 21258c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 21268c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) 21298c2ecf20Sopenharmony_ci return; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci if (coverage_class >= 0) { 21348c2ecf20Sopenharmony_ci ah->coverage_class = coverage_class; 21358c2ecf20Sopenharmony_ci if (ah->dynack.enabled) { 21368c2ecf20Sopenharmony_ci u32 rfilt; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci ah->dynack.enabled = false; 21398c2ecf20Sopenharmony_ci rfilt = ath_calcrxfilter(sc); 21408c2ecf20Sopenharmony_ci ath9k_hw_setrxfilter(ah, rfilt); 21418c2ecf20Sopenharmony_ci } 21428c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 21438c2ecf20Sopenharmony_ci ath9k_hw_init_global_settings(ah); 21448c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 21458c2ecf20Sopenharmony_ci } else if (!ah->dynack.enabled) { 21468c2ecf20Sopenharmony_ci ath9k_enable_dynack(sc); 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 21508c2ecf20Sopenharmony_ci} 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_cistatic bool ath9k_has_tx_pending(struct ath_softc *sc, 21538c2ecf20Sopenharmony_ci bool sw_pending) 21548c2ecf20Sopenharmony_ci{ 21558c2ecf20Sopenharmony_ci int i, npend = 0; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { 21588c2ecf20Sopenharmony_ci if (!ATH_TXQ_SETUP(sc, i)) 21598c2ecf20Sopenharmony_ci continue; 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i], 21628c2ecf20Sopenharmony_ci sw_pending); 21638c2ecf20Sopenharmony_ci if (npend) 21648c2ecf20Sopenharmony_ci break; 21658c2ecf20Sopenharmony_ci } 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci return !!npend; 21688c2ecf20Sopenharmony_ci} 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_cistatic void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 21718c2ecf20Sopenharmony_ci u32 queues, bool drop) 21728c2ecf20Sopenharmony_ci{ 21738c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 21748c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci if (ath9k_is_chanctx_enabled()) { 21778c2ecf20Sopenharmony_ci if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) 21788c2ecf20Sopenharmony_ci goto flush; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci /* 21818c2ecf20Sopenharmony_ci * If MCC is active, extend the flush timeout 21828c2ecf20Sopenharmony_ci * and wait for the HW/SW queues to become 21838c2ecf20Sopenharmony_ci * empty. This needs to be done outside the 21848c2ecf20Sopenharmony_ci * sc->mutex lock to allow the channel scheduler 21858c2ecf20Sopenharmony_ci * to switch channel contexts. 21868c2ecf20Sopenharmony_ci * 21878c2ecf20Sopenharmony_ci * The vif queues have been stopped in mac80211, 21888c2ecf20Sopenharmony_ci * so there won't be any incoming frames. 21898c2ecf20Sopenharmony_ci */ 21908c2ecf20Sopenharmony_ci __ath9k_flush(hw, queues, drop, true, true); 21918c2ecf20Sopenharmony_ci return; 21928c2ecf20Sopenharmony_ci } 21938c2ecf20Sopenharmony_ciflush: 21948c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 21958c2ecf20Sopenharmony_ci __ath9k_flush(hw, queues, drop, true, false); 21968c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 21978c2ecf20Sopenharmony_ci} 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_civoid __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop, 22008c2ecf20Sopenharmony_ci bool sw_pending, bool timeout_override) 22018c2ecf20Sopenharmony_ci{ 22028c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 22038c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 22048c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 22058c2ecf20Sopenharmony_ci int timeout; 22068c2ecf20Sopenharmony_ci bool drain_txq; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&sc->hw_check_work); 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci if (ah->ah_flags & AH_UNPLUGGED) { 22118c2ecf20Sopenharmony_ci ath_dbg(common, ANY, "Device has been unplugged!\n"); 22128c2ecf20Sopenharmony_ci return; 22138c2ecf20Sopenharmony_ci } 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci if (test_bit(ATH_OP_INVALID, &common->op_flags)) { 22168c2ecf20Sopenharmony_ci ath_dbg(common, ANY, "Device not present\n"); 22178c2ecf20Sopenharmony_ci return; 22188c2ecf20Sopenharmony_ci } 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 22218c2ecf20Sopenharmony_ci if (timeout_override) 22228c2ecf20Sopenharmony_ci timeout = HZ / 5; 22238c2ecf20Sopenharmony_ci else 22248c2ecf20Sopenharmony_ci timeout = sc->cur_chan->flush_timeout; 22258c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 22288c2ecf20Sopenharmony_ci "Flush timeout: %d\n", jiffies_to_msecs(timeout)); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc, sw_pending), 22318c2ecf20Sopenharmony_ci timeout) > 0) 22328c2ecf20Sopenharmony_ci drop = false; 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci if (drop) { 22358c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 22368c2ecf20Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 22378c2ecf20Sopenharmony_ci drain_txq = ath_drain_all_txq(sc); 22388c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci if (!drain_txq) 22418c2ecf20Sopenharmony_ci ath_reset(sc, NULL); 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(hw, &sc->hw_check_work, 22478c2ecf20Sopenharmony_ci msecs_to_jiffies(ATH_HW_CHECK_POLL_INT)); 22488c2ecf20Sopenharmony_ci} 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_cistatic bool ath9k_tx_frames_pending(struct ieee80211_hw *hw) 22518c2ecf20Sopenharmony_ci{ 22528c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci return ath9k_has_tx_pending(sc, true); 22558c2ecf20Sopenharmony_ci} 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_cistatic int ath9k_tx_last_beacon(struct ieee80211_hw *hw) 22588c2ecf20Sopenharmony_ci{ 22598c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 22608c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 22618c2ecf20Sopenharmony_ci struct ieee80211_vif *vif; 22628c2ecf20Sopenharmony_ci struct ath_vif *avp; 22638c2ecf20Sopenharmony_ci struct ath_buf *bf; 22648c2ecf20Sopenharmony_ci struct ath_tx_status ts; 22658c2ecf20Sopenharmony_ci bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); 22668c2ecf20Sopenharmony_ci int status; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci vif = sc->beacon.bslot[0]; 22698c2ecf20Sopenharmony_ci if (!vif) 22708c2ecf20Sopenharmony_ci return 0; 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci if (!vif->bss_conf.enable_beacon) 22738c2ecf20Sopenharmony_ci return 0; 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci avp = (void *)vif->drv_priv; 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci if (!sc->beacon.tx_processed && !edma) { 22788c2ecf20Sopenharmony_ci tasklet_disable(&sc->bcon_tasklet); 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci bf = avp->av_bcbuf; 22818c2ecf20Sopenharmony_ci if (!bf || !bf->bf_mpdu) 22828c2ecf20Sopenharmony_ci goto skip; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci status = ath9k_hw_txprocdesc(ah, bf->bf_desc, &ts); 22858c2ecf20Sopenharmony_ci if (status == -EINPROGRESS) 22868c2ecf20Sopenharmony_ci goto skip; 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci sc->beacon.tx_processed = true; 22898c2ecf20Sopenharmony_ci sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ciskip: 22928c2ecf20Sopenharmony_ci tasklet_enable(&sc->bcon_tasklet); 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci return sc->beacon.tx_last; 22968c2ecf20Sopenharmony_ci} 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_cistatic int ath9k_get_stats(struct ieee80211_hw *hw, 22998c2ecf20Sopenharmony_ci struct ieee80211_low_level_stats *stats) 23008c2ecf20Sopenharmony_ci{ 23018c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 23028c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 23038c2ecf20Sopenharmony_ci struct ath9k_mib_stats *mib_stats = &ah->ah_mibStats; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci stats->dot11ACKFailureCount = mib_stats->ackrcv_bad; 23068c2ecf20Sopenharmony_ci stats->dot11RTSFailureCount = mib_stats->rts_bad; 23078c2ecf20Sopenharmony_ci stats->dot11FCSErrorCount = mib_stats->fcs_bad; 23088c2ecf20Sopenharmony_ci stats->dot11RTSSuccessCount = mib_stats->rts_good; 23098c2ecf20Sopenharmony_ci return 0; 23108c2ecf20Sopenharmony_ci} 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_cistatic u32 fill_chainmask(u32 cap, u32 new) 23138c2ecf20Sopenharmony_ci{ 23148c2ecf20Sopenharmony_ci u32 filled = 0; 23158c2ecf20Sopenharmony_ci int i; 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci for (i = 0; cap && new; i++, cap >>= 1) { 23188c2ecf20Sopenharmony_ci if (!(cap & BIT(0))) 23198c2ecf20Sopenharmony_ci continue; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci if (new & BIT(0)) 23228c2ecf20Sopenharmony_ci filled |= BIT(i); 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci new >>= 1; 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci return filled; 23288c2ecf20Sopenharmony_ci} 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_cistatic bool validate_antenna_mask(struct ath_hw *ah, u32 val) 23318c2ecf20Sopenharmony_ci{ 23328c2ecf20Sopenharmony_ci if (AR_SREV_9300_20_OR_LATER(ah)) 23338c2ecf20Sopenharmony_ci return true; 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci switch (val & 0x7) { 23368c2ecf20Sopenharmony_ci case 0x1: 23378c2ecf20Sopenharmony_ci case 0x3: 23388c2ecf20Sopenharmony_ci case 0x7: 23398c2ecf20Sopenharmony_ci return true; 23408c2ecf20Sopenharmony_ci case 0x2: 23418c2ecf20Sopenharmony_ci return (ah->caps.rx_chainmask == 1); 23428c2ecf20Sopenharmony_ci default: 23438c2ecf20Sopenharmony_ci return false; 23448c2ecf20Sopenharmony_ci } 23458c2ecf20Sopenharmony_ci} 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_cistatic int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) 23488c2ecf20Sopenharmony_ci{ 23498c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 23508c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci if (ah->caps.rx_chainmask != 1) 23538c2ecf20Sopenharmony_ci rx_ant |= tx_ant; 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci if (!validate_antenna_mask(ah, rx_ant) || !tx_ant) 23568c2ecf20Sopenharmony_ci return -EINVAL; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci sc->ant_rx = rx_ant; 23598c2ecf20Sopenharmony_ci sc->ant_tx = tx_ant; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci if (ah->caps.rx_chainmask == 1) 23628c2ecf20Sopenharmony_ci return 0; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci /* AR9100 runs into calibration issues if not all rx chains are enabled */ 23658c2ecf20Sopenharmony_ci if (AR_SREV_9100(ah)) 23668c2ecf20Sopenharmony_ci ah->rxchainmask = 0x7; 23678c2ecf20Sopenharmony_ci else 23688c2ecf20Sopenharmony_ci ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant); 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant); 23718c2ecf20Sopenharmony_ci ath9k_cmn_reload_chainmask(ah); 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci return 0; 23748c2ecf20Sopenharmony_ci} 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_cistatic int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) 23778c2ecf20Sopenharmony_ci{ 23788c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci *tx_ant = sc->ant_tx; 23818c2ecf20Sopenharmony_ci *rx_ant = sc->ant_rx; 23828c2ecf20Sopenharmony_ci return 0; 23838c2ecf20Sopenharmony_ci} 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_cistatic void ath9k_sw_scan_start(struct ieee80211_hw *hw, 23868c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 23878c2ecf20Sopenharmony_ci const u8 *mac_addr) 23888c2ecf20Sopenharmony_ci{ 23898c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 23908c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 23918c2ecf20Sopenharmony_ci set_bit(ATH_OP_SCANNING, &common->op_flags); 23928c2ecf20Sopenharmony_ci} 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_cistatic void ath9k_sw_scan_complete(struct ieee80211_hw *hw, 23958c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 23968c2ecf20Sopenharmony_ci{ 23978c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 23988c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 23998c2ecf20Sopenharmony_ci clear_bit(ATH_OP_SCANNING, &common->op_flags); 24008c2ecf20Sopenharmony_ci} 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_cistatic void ath9k_cancel_pending_offchannel(struct ath_softc *sc) 24058c2ecf20Sopenharmony_ci{ 24068c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci if (sc->offchannel.roc_vif) { 24098c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 24108c2ecf20Sopenharmony_ci "%s: Aborting RoC\n", __func__); 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci del_timer_sync(&sc->offchannel.timer); 24138c2ecf20Sopenharmony_ci if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) 24148c2ecf20Sopenharmony_ci ath_roc_complete(sc, ATH_ROC_COMPLETE_ABORT); 24158c2ecf20Sopenharmony_ci } 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci if (test_bit(ATH_OP_SCANNING, &common->op_flags)) { 24188c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 24198c2ecf20Sopenharmony_ci "%s: Aborting HW scan\n", __func__); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci del_timer_sync(&sc->offchannel.timer); 24228c2ecf20Sopenharmony_ci ath_scan_complete(sc, true); 24238c2ecf20Sopenharmony_ci } 24248c2ecf20Sopenharmony_ci} 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_cistatic int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 24278c2ecf20Sopenharmony_ci struct ieee80211_scan_request *hw_req) 24288c2ecf20Sopenharmony_ci{ 24298c2ecf20Sopenharmony_ci struct cfg80211_scan_request *req = &hw_req->req; 24308c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 24318c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 24328c2ecf20Sopenharmony_ci int ret = 0; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci if (WARN_ON(sc->offchannel.scan_req)) { 24378c2ecf20Sopenharmony_ci ret = -EBUSY; 24388c2ecf20Sopenharmony_ci goto out; 24398c2ecf20Sopenharmony_ci } 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 24428c2ecf20Sopenharmony_ci set_bit(ATH_OP_SCANNING, &common->op_flags); 24438c2ecf20Sopenharmony_ci sc->offchannel.scan_vif = vif; 24448c2ecf20Sopenharmony_ci sc->offchannel.scan_req = req; 24458c2ecf20Sopenharmony_ci sc->offchannel.scan_idx = 0; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, "HW scan request received on vif: %pM\n", 24488c2ecf20Sopenharmony_ci vif->addr); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) { 24518c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, "Starting HW scan\n"); 24528c2ecf20Sopenharmony_ci ath_offchannel_next(sc); 24538c2ecf20Sopenharmony_ci } 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ciout: 24568c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci return ret; 24598c2ecf20Sopenharmony_ci} 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_cistatic void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, 24628c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 24638c2ecf20Sopenharmony_ci{ 24648c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 24658c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, "Cancel HW scan on vif: %pM\n", vif->addr); 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 24708c2ecf20Sopenharmony_ci del_timer_sync(&sc->offchannel.timer); 24718c2ecf20Sopenharmony_ci ath_scan_complete(sc, true); 24728c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 24738c2ecf20Sopenharmony_ci} 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_cistatic int ath9k_remain_on_channel(struct ieee80211_hw *hw, 24768c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 24778c2ecf20Sopenharmony_ci struct ieee80211_channel *chan, int duration, 24788c2ecf20Sopenharmony_ci enum ieee80211_roc_type type) 24798c2ecf20Sopenharmony_ci{ 24808c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 24818c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 24828c2ecf20Sopenharmony_ci int ret = 0; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci if (WARN_ON(sc->offchannel.roc_vif)) { 24878c2ecf20Sopenharmony_ci ret = -EBUSY; 24888c2ecf20Sopenharmony_ci goto out; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 24928c2ecf20Sopenharmony_ci sc->offchannel.roc_vif = vif; 24938c2ecf20Sopenharmony_ci sc->offchannel.roc_chan = chan; 24948c2ecf20Sopenharmony_ci sc->offchannel.roc_duration = duration; 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 24978c2ecf20Sopenharmony_ci "RoC request on vif: %pM, type: %d duration: %d\n", 24988c2ecf20Sopenharmony_ci vif->addr, type, duration); 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) { 25018c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, "Starting RoC period\n"); 25028c2ecf20Sopenharmony_ci ath_offchannel_next(sc); 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ciout: 25068c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci return ret; 25098c2ecf20Sopenharmony_ci} 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_cistatic int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw, 25128c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 25138c2ecf20Sopenharmony_ci{ 25148c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 25158c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, "Cancel RoC\n"); 25208c2ecf20Sopenharmony_ci del_timer_sync(&sc->offchannel.timer); 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci if (sc->offchannel.roc_vif) { 25238c2ecf20Sopenharmony_ci if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) 25248c2ecf20Sopenharmony_ci ath_roc_complete(sc, ATH_ROC_COMPLETE_CANCEL); 25258c2ecf20Sopenharmony_ci } 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci return 0; 25308c2ecf20Sopenharmony_ci} 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_cistatic int ath9k_add_chanctx(struct ieee80211_hw *hw, 25338c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *conf) 25348c2ecf20Sopenharmony_ci{ 25358c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 25368c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 25378c2ecf20Sopenharmony_ci struct ath_chanctx *ctx, **ptr; 25388c2ecf20Sopenharmony_ci int pos; 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci ath_for_each_chanctx(sc, ctx) { 25438c2ecf20Sopenharmony_ci if (ctx->assigned) 25448c2ecf20Sopenharmony_ci continue; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci ptr = (void *) conf->drv_priv; 25478c2ecf20Sopenharmony_ci *ptr = ctx; 25488c2ecf20Sopenharmony_ci ctx->assigned = true; 25498c2ecf20Sopenharmony_ci pos = ctx - &sc->chanctx[0]; 25508c2ecf20Sopenharmony_ci ctx->hw_queue_base = pos * IEEE80211_NUM_ACS; 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 25538c2ecf20Sopenharmony_ci "Add channel context: %d MHz\n", 25548c2ecf20Sopenharmony_ci conf->def.chan->center_freq); 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci ath_chanctx_set_channel(sc, ctx, &conf->def); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 25598c2ecf20Sopenharmony_ci return 0; 25608c2ecf20Sopenharmony_ci } 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 25638c2ecf20Sopenharmony_ci return -ENOSPC; 25648c2ecf20Sopenharmony_ci} 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_cistatic void ath9k_remove_chanctx(struct ieee80211_hw *hw, 25688c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *conf) 25698c2ecf20Sopenharmony_ci{ 25708c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 25718c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 25728c2ecf20Sopenharmony_ci struct ath_chanctx *ctx = ath_chanctx_get(conf); 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 25778c2ecf20Sopenharmony_ci "Remove channel context: %d MHz\n", 25788c2ecf20Sopenharmony_ci conf->def.chan->center_freq); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci ctx->assigned = false; 25818c2ecf20Sopenharmony_ci ctx->hw_queue_base = 0; 25828c2ecf20Sopenharmony_ci ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN); 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 25858c2ecf20Sopenharmony_ci} 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_cistatic void ath9k_change_chanctx(struct ieee80211_hw *hw, 25888c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *conf, 25898c2ecf20Sopenharmony_ci u32 changed) 25908c2ecf20Sopenharmony_ci{ 25918c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 25928c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 25938c2ecf20Sopenharmony_ci struct ath_chanctx *ctx = ath_chanctx_get(conf); 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 25968c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 25978c2ecf20Sopenharmony_ci "Change channel context: %d MHz\n", 25988c2ecf20Sopenharmony_ci conf->def.chan->center_freq); 25998c2ecf20Sopenharmony_ci ath_chanctx_set_channel(sc, ctx, &conf->def); 26008c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 26018c2ecf20Sopenharmony_ci} 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_cistatic int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw, 26048c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 26058c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *conf) 26068c2ecf20Sopenharmony_ci{ 26078c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 26088c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 26098c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 26108c2ecf20Sopenharmony_ci struct ath_chanctx *ctx = ath_chanctx_get(conf); 26118c2ecf20Sopenharmony_ci int i; 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci ath9k_cancel_pending_offchannel(sc); 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 26188c2ecf20Sopenharmony_ci "Assign VIF (addr: %pM, type: %d, p2p: %d) to channel context: %d MHz\n", 26198c2ecf20Sopenharmony_ci vif->addr, vif->type, vif->p2p, 26208c2ecf20Sopenharmony_ci conf->def.chan->center_freq); 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci avp->chanctx = ctx; 26238c2ecf20Sopenharmony_ci ctx->nvifs_assigned++; 26248c2ecf20Sopenharmony_ci list_add_tail(&avp->list, &ctx->vifs); 26258c2ecf20Sopenharmony_ci ath9k_calculate_summary_state(sc, ctx); 26268c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) 26278c2ecf20Sopenharmony_ci vif->hw_queue[i] = ctx->hw_queue_base + i; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci return 0; 26328c2ecf20Sopenharmony_ci} 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_cistatic void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw, 26358c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 26368c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *conf) 26378c2ecf20Sopenharmony_ci{ 26388c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 26398c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 26408c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 26418c2ecf20Sopenharmony_ci struct ath_chanctx *ctx = ath_chanctx_get(conf); 26428c2ecf20Sopenharmony_ci int ac; 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci ath9k_cancel_pending_offchannel(sc); 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 26498c2ecf20Sopenharmony_ci "Remove VIF (addr: %pM, type: %d, p2p: %d) from channel context: %d MHz\n", 26508c2ecf20Sopenharmony_ci vif->addr, vif->type, vif->p2p, 26518c2ecf20Sopenharmony_ci conf->def.chan->center_freq); 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci avp->chanctx = NULL; 26548c2ecf20Sopenharmony_ci ctx->nvifs_assigned--; 26558c2ecf20Sopenharmony_ci list_del(&avp->list); 26568c2ecf20Sopenharmony_ci ath9k_calculate_summary_state(sc, ctx); 26578c2ecf20Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 26588c2ecf20Sopenharmony_ci vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE; 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 26618c2ecf20Sopenharmony_ci} 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_cistatic void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw, 26648c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 26658c2ecf20Sopenharmony_ci u16 duration) 26668c2ecf20Sopenharmony_ci{ 26678c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 26688c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 26698c2ecf20Sopenharmony_ci struct ath_vif *avp = (struct ath_vif *) vif->drv_priv; 26708c2ecf20Sopenharmony_ci struct ath_beacon_config *cur_conf; 26718c2ecf20Sopenharmony_ci struct ath_chanctx *go_ctx; 26728c2ecf20Sopenharmony_ci unsigned long timeout; 26738c2ecf20Sopenharmony_ci bool changed = false; 26748c2ecf20Sopenharmony_ci u32 beacon_int; 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) 26778c2ecf20Sopenharmony_ci return; 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci if (!avp->chanctx) 26808c2ecf20Sopenharmony_ci return; 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 26858c2ecf20Sopenharmony_ci if (sc->next_chan || (sc->cur_chan != avp->chanctx)) 26868c2ecf20Sopenharmony_ci changed = true; 26878c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci if (!changed) 26908c2ecf20Sopenharmony_ci goto out; 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_ci ath9k_cancel_pending_offchannel(sc); 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_ci go_ctx = ath_is_go_chanctx_present(sc); 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci if (go_ctx) { 26978c2ecf20Sopenharmony_ci /* 26988c2ecf20Sopenharmony_ci * Wait till the GO interface gets a chance 26998c2ecf20Sopenharmony_ci * to send out an NoA. 27008c2ecf20Sopenharmony_ci */ 27018c2ecf20Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 27028c2ecf20Sopenharmony_ci sc->sched.mgd_prepare_tx = true; 27038c2ecf20Sopenharmony_ci cur_conf = &go_ctx->beacon; 27048c2ecf20Sopenharmony_ci beacon_int = TU_TO_USEC(cur_conf->beacon_interval); 27058c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ci timeout = usecs_to_jiffies(beacon_int * 2); 27088c2ecf20Sopenharmony_ci init_completion(&sc->go_beacon); 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci if (wait_for_completion_timeout(&sc->go_beacon, 27138c2ecf20Sopenharmony_ci timeout) == 0) { 27148c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 27158c2ecf20Sopenharmony_ci "Failed to send new NoA\n"); 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 27188c2ecf20Sopenharmony_ci sc->sched.mgd_prepare_tx = false; 27198c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 27208c2ecf20Sopenharmony_ci } 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 27238c2ecf20Sopenharmony_ci } 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_ci ath_dbg(common, CHAN_CTX, 27268c2ecf20Sopenharmony_ci "%s: Set chanctx state to FORCE_ACTIVE for vif: %pM\n", 27278c2ecf20Sopenharmony_ci __func__, vif->addr); 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 27308c2ecf20Sopenharmony_ci sc->next_chan = avp->chanctx; 27318c2ecf20Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE; 27328c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_ci ath_chanctx_set_next(sc, true); 27358c2ecf20Sopenharmony_ciout: 27368c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 27378c2ecf20Sopenharmony_ci} 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_civoid ath9k_fill_chanctx_ops(void) 27408c2ecf20Sopenharmony_ci{ 27418c2ecf20Sopenharmony_ci if (!ath9k_is_chanctx_enabled()) 27428c2ecf20Sopenharmony_ci return; 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci ath9k_ops.hw_scan = ath9k_hw_scan; 27458c2ecf20Sopenharmony_ci ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; 27468c2ecf20Sopenharmony_ci ath9k_ops.remain_on_channel = ath9k_remain_on_channel; 27478c2ecf20Sopenharmony_ci ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; 27488c2ecf20Sopenharmony_ci ath9k_ops.add_chanctx = ath9k_add_chanctx; 27498c2ecf20Sopenharmony_ci ath9k_ops.remove_chanctx = ath9k_remove_chanctx; 27508c2ecf20Sopenharmony_ci ath9k_ops.change_chanctx = ath9k_change_chanctx; 27518c2ecf20Sopenharmony_ci ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx; 27528c2ecf20Sopenharmony_ci ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx; 27538c2ecf20Sopenharmony_ci ath9k_ops.mgd_prepare_tx = ath9k_mgd_prepare_tx; 27548c2ecf20Sopenharmony_ci} 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci#endif 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_cistatic int ath9k_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, 27598c2ecf20Sopenharmony_ci int *dbm) 27608c2ecf20Sopenharmony_ci{ 27618c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 27628c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 27658c2ecf20Sopenharmony_ci if (avp->chanctx) 27668c2ecf20Sopenharmony_ci *dbm = avp->chanctx->cur_txpower; 27678c2ecf20Sopenharmony_ci else 27688c2ecf20Sopenharmony_ci *dbm = sc->cur_chan->cur_txpower; 27698c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci *dbm /= 2; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci return 0; 27748c2ecf20Sopenharmony_ci} 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_cistruct ieee80211_ops ath9k_ops = { 27778c2ecf20Sopenharmony_ci .tx = ath9k_tx, 27788c2ecf20Sopenharmony_ci .start = ath9k_start, 27798c2ecf20Sopenharmony_ci .stop = ath9k_stop, 27808c2ecf20Sopenharmony_ci .add_interface = ath9k_add_interface, 27818c2ecf20Sopenharmony_ci .change_interface = ath9k_change_interface, 27828c2ecf20Sopenharmony_ci .remove_interface = ath9k_remove_interface, 27838c2ecf20Sopenharmony_ci .config = ath9k_config, 27848c2ecf20Sopenharmony_ci .configure_filter = ath9k_configure_filter, 27858c2ecf20Sopenharmony_ci .sta_state = ath9k_sta_state, 27868c2ecf20Sopenharmony_ci .sta_notify = ath9k_sta_notify, 27878c2ecf20Sopenharmony_ci .conf_tx = ath9k_conf_tx, 27888c2ecf20Sopenharmony_ci .bss_info_changed = ath9k_bss_info_changed, 27898c2ecf20Sopenharmony_ci .set_key = ath9k_set_key, 27908c2ecf20Sopenharmony_ci .get_tsf = ath9k_get_tsf, 27918c2ecf20Sopenharmony_ci .set_tsf = ath9k_set_tsf, 27928c2ecf20Sopenharmony_ci .reset_tsf = ath9k_reset_tsf, 27938c2ecf20Sopenharmony_ci .ampdu_action = ath9k_ampdu_action, 27948c2ecf20Sopenharmony_ci .get_survey = ath9k_get_survey, 27958c2ecf20Sopenharmony_ci .rfkill_poll = ath9k_rfkill_poll_state, 27968c2ecf20Sopenharmony_ci .set_coverage_class = ath9k_set_coverage_class, 27978c2ecf20Sopenharmony_ci .flush = ath9k_flush, 27988c2ecf20Sopenharmony_ci .tx_frames_pending = ath9k_tx_frames_pending, 27998c2ecf20Sopenharmony_ci .tx_last_beacon = ath9k_tx_last_beacon, 28008c2ecf20Sopenharmony_ci .release_buffered_frames = ath9k_release_buffered_frames, 28018c2ecf20Sopenharmony_ci .get_stats = ath9k_get_stats, 28028c2ecf20Sopenharmony_ci .set_antenna = ath9k_set_antenna, 28038c2ecf20Sopenharmony_ci .get_antenna = ath9k_get_antenna, 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_WOW 28068c2ecf20Sopenharmony_ci .suspend = ath9k_suspend, 28078c2ecf20Sopenharmony_ci .resume = ath9k_resume, 28088c2ecf20Sopenharmony_ci .set_wakeup = ath9k_set_wakeup, 28098c2ecf20Sopenharmony_ci#endif 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci#ifdef CONFIG_ATH9K_DEBUGFS 28128c2ecf20Sopenharmony_ci .get_et_sset_count = ath9k_get_et_sset_count, 28138c2ecf20Sopenharmony_ci .get_et_stats = ath9k_get_et_stats, 28148c2ecf20Sopenharmony_ci .get_et_strings = ath9k_get_et_strings, 28158c2ecf20Sopenharmony_ci#endif 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_STATION_STATISTICS) 28188c2ecf20Sopenharmony_ci .sta_add_debugfs = ath9k_sta_add_debugfs, 28198c2ecf20Sopenharmony_ci#endif 28208c2ecf20Sopenharmony_ci .sw_scan_start = ath9k_sw_scan_start, 28218c2ecf20Sopenharmony_ci .sw_scan_complete = ath9k_sw_scan_complete, 28228c2ecf20Sopenharmony_ci .get_txpower = ath9k_get_txpower, 28238c2ecf20Sopenharmony_ci .wake_tx_queue = ath9k_wake_tx_queue, 28248c2ecf20Sopenharmony_ci}; 2825