162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2014 Qualcomm Atheros, Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "ath9k.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Set/change channels. If the channel is really being changed, it's done 2062306a36Sopenharmony_ci * by reseting the chip. To accomplish this we must first cleanup any pending 2162306a36Sopenharmony_ci * DMA, then restart stuff. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_cistatic int ath_set_channel(struct ath_softc *sc) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 2662306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 2762306a36Sopenharmony_ci struct ieee80211_hw *hw = sc->hw; 2862306a36Sopenharmony_ci struct ath9k_channel *hchan; 2962306a36Sopenharmony_ci struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef; 3062306a36Sopenharmony_ci struct ieee80211_channel *chan = chandef->chan; 3162306a36Sopenharmony_ci int pos = chan->hw_value; 3262306a36Sopenharmony_ci unsigned long flags; 3362306a36Sopenharmony_ci int old_pos = -1; 3462306a36Sopenharmony_ci int r; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (test_bit(ATH_OP_INVALID, &common->op_flags)) 3762306a36Sopenharmony_ci return -EIO; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (ah->curchan) 4062306a36Sopenharmony_ci old_pos = ah->curchan - &ah->channels[0]; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", 4362306a36Sopenharmony_ci chan->center_freq, chandef->width); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* update survey stats for the old channel before switching */ 4662306a36Sopenharmony_ci spin_lock_irqsave(&common->cc_lock, flags); 4762306a36Sopenharmony_ci ath_update_survey_stats(sc); 4862306a36Sopenharmony_ci spin_unlock_irqrestore(&common->cc_lock, flags); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci ath9k_cmn_get_channel(hw, ah, chandef); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* If the operating channel changes, change the survey in-use flags 5362306a36Sopenharmony_ci * along with it. 5462306a36Sopenharmony_ci * Reset the survey data for the new channel, unless we're switching 5562306a36Sopenharmony_ci * back to the operating channel from an off-channel operation. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) { 5862306a36Sopenharmony_ci if (sc->cur_survey) 5962306a36Sopenharmony_ci sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci sc->cur_survey = &sc->survey[pos]; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci memset(sc->cur_survey, 0, sizeof(struct survey_info)); 6462306a36Sopenharmony_ci sc->cur_survey->filled |= SURVEY_INFO_IN_USE; 6562306a36Sopenharmony_ci } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { 6662306a36Sopenharmony_ci memset(&sc->survey[pos], 0, sizeof(struct survey_info)); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci hchan = &sc->sc_ah->channels[pos]; 7062306a36Sopenharmony_ci r = ath_reset(sc, hchan); 7162306a36Sopenharmony_ci if (r) 7262306a36Sopenharmony_ci return r; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* The most recent snapshot of channel->noisefloor for the old 7562306a36Sopenharmony_ci * channel is only available after the hardware reset. Copy it to 7662306a36Sopenharmony_ci * the survey stats now. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci if (old_pos >= 0) 7962306a36Sopenharmony_ci ath_update_survey_nf(sc, old_pos); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Enable radar pulse detection if on a DFS channel. Spectral 8262306a36Sopenharmony_ci * scanning and radar detection can not be used concurrently. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci if (hw->conf.radar_enabled) { 8562306a36Sopenharmony_ci u32 rxfilter; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci rxfilter = ath9k_hw_getrxfilter(ah); 8862306a36Sopenharmony_ci rxfilter |= ATH9K_RX_FILTER_PHYRADAR | 8962306a36Sopenharmony_ci ATH9K_RX_FILTER_PHYERR; 9062306a36Sopenharmony_ci ath9k_hw_setrxfilter(ah, rxfilter); 9162306a36Sopenharmony_ci ath_dbg(common, DFS, "DFS enabled at freq %d\n", 9262306a36Sopenharmony_ci chan->center_freq); 9362306a36Sopenharmony_ci } else { 9462306a36Sopenharmony_ci /* perform spectral scan if requested. */ 9562306a36Sopenharmony_ci if (test_bit(ATH_OP_SCANNING, &common->op_flags) && 9662306a36Sopenharmony_ci sc->spec_priv.spectral_mode == SPECTRAL_CHANSCAN) 9762306a36Sopenharmony_ci ath9k_cmn_spectral_scan_trigger(common, &sc->spec_priv); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_civoid ath_chanctx_init(struct ath_softc *sc) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct ath_chanctx *ctx; 10662306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 10762306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 10862306a36Sopenharmony_ci struct ieee80211_channel *chan; 10962306a36Sopenharmony_ci int i, j; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci sband = &common->sbands[NL80211_BAND_2GHZ]; 11262306a36Sopenharmony_ci if (!sband->n_channels) 11362306a36Sopenharmony_ci sband = &common->sbands[NL80211_BAND_5GHZ]; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci chan = &sband->channels[0]; 11662306a36Sopenharmony_ci for (i = 0; i < ATH9K_NUM_CHANCTX; i++) { 11762306a36Sopenharmony_ci ctx = &sc->chanctx[i]; 11862306a36Sopenharmony_ci cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); 11962306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->vifs); 12062306a36Sopenharmony_ci ctx->txpower = ATH_TXPOWER_MAX; 12162306a36Sopenharmony_ci ctx->flush_timeout = HZ / 5; /* 200ms */ 12262306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(ctx->acq); j++) { 12362306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->acq[j].acq_new); 12462306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->acq[j].acq_old); 12562306a36Sopenharmony_ci spin_lock_init(&ctx->acq[j].lock); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_civoid ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, 13162306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 13462306a36Sopenharmony_ci bool cur_chan; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 13762306a36Sopenharmony_ci if (chandef) 13862306a36Sopenharmony_ci memcpy(&ctx->chandef, chandef, sizeof(*chandef)); 13962306a36Sopenharmony_ci cur_chan = sc->cur_chan == ctx; 14062306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (!cur_chan) { 14362306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 14462306a36Sopenharmony_ci "Current context differs from the new context\n"); 14562306a36Sopenharmony_ci return; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ath_set_channel(sc); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/*************/ 15462306a36Sopenharmony_ci/* Utilities */ 15562306a36Sopenharmony_ci/*************/ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistruct ath_chanctx* ath_is_go_chanctx_present(struct ath_softc *sc) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct ath_chanctx *ctx; 16062306a36Sopenharmony_ci struct ath_vif *avp; 16162306a36Sopenharmony_ci struct ieee80211_vif *vif; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ath_for_each_chanctx(sc, ctx) { 16662306a36Sopenharmony_ci if (!ctx->active) 16762306a36Sopenharmony_ci continue; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci list_for_each_entry(avp, &ctx->vifs, list) { 17062306a36Sopenharmony_ci vif = avp->vif; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO) { 17362306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 17462306a36Sopenharmony_ci return ctx; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 18062306a36Sopenharmony_ci return NULL; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/**********************************************************/ 18462306a36Sopenharmony_ci/* Functions to handle the channel context state machine. */ 18562306a36Sopenharmony_ci/**********************************************************/ 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const char *offchannel_state_string(enum ath_offchannel_state state) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci switch (state) { 19062306a36Sopenharmony_ci case_rtn_string(ATH_OFFCHANNEL_IDLE); 19162306a36Sopenharmony_ci case_rtn_string(ATH_OFFCHANNEL_PROBE_SEND); 19262306a36Sopenharmony_ci case_rtn_string(ATH_OFFCHANNEL_PROBE_WAIT); 19362306a36Sopenharmony_ci case_rtn_string(ATH_OFFCHANNEL_SUSPEND); 19462306a36Sopenharmony_ci case_rtn_string(ATH_OFFCHANNEL_ROC_START); 19562306a36Sopenharmony_ci case_rtn_string(ATH_OFFCHANNEL_ROC_WAIT); 19662306a36Sopenharmony_ci case_rtn_string(ATH_OFFCHANNEL_ROC_DONE); 19762306a36Sopenharmony_ci default: 19862306a36Sopenharmony_ci return "unknown"; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic const char *chanctx_event_string(enum ath_chanctx_event ev) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci switch (ev) { 20562306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_BEACON_PREPARE); 20662306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_BEACON_SENT); 20762306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_TSF_TIMER); 20862306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_BEACON_RECEIVED); 20962306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_AUTHORIZED); 21062306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_SWITCH); 21162306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_ASSIGN); 21262306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_UNASSIGN); 21362306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_CHANGE); 21462306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); 21562306a36Sopenharmony_ci default: 21662306a36Sopenharmony_ci return "unknown"; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic const char *chanctx_state_string(enum ath_chanctx_state state) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci switch (state) { 22362306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_STATE_IDLE); 22462306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_STATE_WAIT_FOR_BEACON); 22562306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_STATE_WAIT_FOR_TIMER); 22662306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_STATE_SWITCH); 22762306a36Sopenharmony_ci case_rtn_string(ATH_CHANCTX_STATE_FORCE_ACTIVE); 22862306a36Sopenharmony_ci default: 22962306a36Sopenharmony_ci return "unknown"; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic u32 chanctx_event_delta(struct ath_softc *sc) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci u64 ms; 23662306a36Sopenharmony_ci struct timespec64 ts, *old; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ktime_get_raw_ts64(&ts); 23962306a36Sopenharmony_ci old = &sc->last_event_time; 24062306a36Sopenharmony_ci ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; 24162306a36Sopenharmony_ci ms -= old->tv_sec * 1000 + old->tv_nsec / 1000000; 24262306a36Sopenharmony_ci sc->last_event_time = ts; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return (u32)ms; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_civoid ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 25062306a36Sopenharmony_ci struct ath_chanctx *ictx; 25162306a36Sopenharmony_ci struct ath_vif *avp; 25262306a36Sopenharmony_ci bool active = false; 25362306a36Sopenharmony_ci u8 n_active = 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!ctx) 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (ctx == &sc->offchannel.chan) { 25962306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (likely(sc->sched.channel_switch_time)) 26262306a36Sopenharmony_ci ctx->flush_timeout = 26362306a36Sopenharmony_ci usecs_to_jiffies(sc->sched.channel_switch_time); 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci ctx->flush_timeout = 26662306a36Sopenharmony_ci msecs_to_jiffies(10); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * There is no need to iterate over the 27262306a36Sopenharmony_ci * active/assigned channel contexts if 27362306a36Sopenharmony_ci * the current context is offchannel. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci return; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci ictx = ctx; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci list_for_each_entry(avp, &ctx->vifs, list) { 28162306a36Sopenharmony_ci struct ieee80211_vif *vif = avp->vif; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci switch (vif->type) { 28462306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 28562306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 28662306a36Sopenharmony_ci if (avp->assoc) 28762306a36Sopenharmony_ci active = true; 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci default: 29062306a36Sopenharmony_ci active = true; 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci ctx->active = active; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ath_for_each_chanctx(sc, ctx) { 29762306a36Sopenharmony_ci if (!ctx->assigned || list_empty(&ctx->vifs)) 29862306a36Sopenharmony_ci continue; 29962306a36Sopenharmony_ci n_active++; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (n_active <= 1) { 30562306a36Sopenharmony_ci ictx->flush_timeout = HZ / 5; 30662306a36Sopenharmony_ci clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); 30762306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 30862306a36Sopenharmony_ci return; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ictx->flush_timeout = usecs_to_jiffies(sc->sched.channel_switch_time); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) { 31462306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (ath9k_is_chanctx_enabled()) { 32162306a36Sopenharmony_ci ath_chanctx_event(sc, NULL, 32262306a36Sopenharmony_ci ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic struct ath_chanctx * 32762306a36Sopenharmony_ciath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int idx = ctx - &sc->chanctx[0]; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return &sc->chanctx[!idx]; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct ath_chanctx *prev, *cur; 33762306a36Sopenharmony_ci struct timespec64 ts; 33862306a36Sopenharmony_ci u32 cur_tsf, prev_tsf, beacon_int; 33962306a36Sopenharmony_ci s32 offset; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci cur = sc->cur_chan; 34462306a36Sopenharmony_ci prev = ath_chanctx_get_next(sc, cur); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!prev->switch_after_beacon) 34762306a36Sopenharmony_ci return; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ktime_get_raw_ts64(&ts); 35062306a36Sopenharmony_ci cur_tsf = (u32) cur->tsf_val + 35162306a36Sopenharmony_ci ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf; 35462306a36Sopenharmony_ci prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Adjust the TSF time of the AP chanctx to keep its beacons 35762306a36Sopenharmony_ci * at half beacon interval offset relative to the STA chanctx. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci offset = cur_tsf - prev_tsf; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Ignore stale data or spurious timestamps */ 36262306a36Sopenharmony_ci if (offset < 0 || offset > 3 * beacon_int) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci offset = beacon_int / 2 - (offset % beacon_int); 36662306a36Sopenharmony_ci prev->tsf_val += offset; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* Configure the TSF based hardware timer for a channel switch. 37062306a36Sopenharmony_ci * Also set up backup software timer, in case the gen timer fails. 37162306a36Sopenharmony_ci * This could be caused by a hardware reset. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistatic void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 37662306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 37762306a36Sopenharmony_ci unsigned long timeout; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000); 38062306a36Sopenharmony_ci tsf_time -= ath9k_hw_gettsf32(ah); 38162306a36Sopenharmony_ci timeout = msecs_to_jiffies(tsf_time / 1000) + 1; 38262306a36Sopenharmony_ci mod_timer(&sc->sched.timer, jiffies + timeout); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 38562306a36Sopenharmony_ci "Setup chanctx timer with timeout: %d (%d) ms\n", 38662306a36Sopenharmony_ci tsf_time / 1000, jiffies_to_msecs(timeout)); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void ath_chanctx_handle_bmiss(struct ath_softc *sc, 39062306a36Sopenharmony_ci struct ath_chanctx *ctx, 39162306a36Sopenharmony_ci struct ath_vif *avp) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * Clear the extend_absence flag if it had been 39562306a36Sopenharmony_ci * set during the previous beacon transmission, 39662306a36Sopenharmony_ci * since we need to revert to the normal NoA 39762306a36Sopenharmony_ci * schedule. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci if (ctx->active && sc->sched.extend_absence) { 40062306a36Sopenharmony_ci avp->noa_duration = 0; 40162306a36Sopenharmony_ci sc->sched.extend_absence = false; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* If at least two consecutive beacons were missed on the STA 40562306a36Sopenharmony_ci * chanctx, stay on the STA channel for one extra beacon period, 40662306a36Sopenharmony_ci * to resync the timer properly. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci if (ctx->active && sc->sched.beacon_miss >= 2) { 40962306a36Sopenharmony_ci avp->noa_duration = 0; 41062306a36Sopenharmony_ci sc->sched.extend_absence = true; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic void ath_chanctx_offchannel_noa(struct ath_softc *sc, 41562306a36Sopenharmony_ci struct ath_chanctx *ctx, 41662306a36Sopenharmony_ci struct ath_vif *avp, 41762306a36Sopenharmony_ci u32 tsf_time) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci avp->noa_index++; 42262306a36Sopenharmony_ci avp->offchannel_start = tsf_time; 42362306a36Sopenharmony_ci avp->offchannel_duration = sc->sched.offchannel_duration; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 42662306a36Sopenharmony_ci "offchannel noa_duration: %d, noa_start: %u, noa_index: %d\n", 42762306a36Sopenharmony_ci avp->offchannel_duration, 42862306a36Sopenharmony_ci avp->offchannel_start, 42962306a36Sopenharmony_ci avp->noa_index); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* 43262306a36Sopenharmony_ci * When multiple contexts are active, the NoA 43362306a36Sopenharmony_ci * has to be recalculated and advertised after 43462306a36Sopenharmony_ci * an offchannel operation. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci if (ctx->active && avp->noa_duration) 43762306a36Sopenharmony_ci avp->noa_duration = 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void ath_chanctx_set_periodic_noa(struct ath_softc *sc, 44162306a36Sopenharmony_ci struct ath_vif *avp, 44262306a36Sopenharmony_ci struct ath_beacon_config *cur_conf, 44362306a36Sopenharmony_ci u32 tsf_time, 44462306a36Sopenharmony_ci u32 beacon_int) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci avp->noa_index++; 44962306a36Sopenharmony_ci avp->noa_start = tsf_time; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (sc->sched.extend_absence) 45262306a36Sopenharmony_ci avp->noa_duration = (3 * beacon_int / 2) + 45362306a36Sopenharmony_ci sc->sched.channel_switch_time; 45462306a36Sopenharmony_ci else 45562306a36Sopenharmony_ci avp->noa_duration = 45662306a36Sopenharmony_ci TU_TO_USEC(cur_conf->beacon_interval) / 2 + 45762306a36Sopenharmony_ci sc->sched.channel_switch_time; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (test_bit(ATH_OP_SCANNING, &common->op_flags) || 46062306a36Sopenharmony_ci sc->sched.extend_absence) 46162306a36Sopenharmony_ci avp->periodic_noa = false; 46262306a36Sopenharmony_ci else 46362306a36Sopenharmony_ci avp->periodic_noa = true; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 46662306a36Sopenharmony_ci "noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n", 46762306a36Sopenharmony_ci avp->noa_duration, 46862306a36Sopenharmony_ci avp->noa_start, 46962306a36Sopenharmony_ci avp->noa_index, 47062306a36Sopenharmony_ci avp->periodic_noa); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic void ath_chanctx_set_oneshot_noa(struct ath_softc *sc, 47462306a36Sopenharmony_ci struct ath_vif *avp, 47562306a36Sopenharmony_ci u32 tsf_time, 47662306a36Sopenharmony_ci u32 duration) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci avp->noa_index++; 48162306a36Sopenharmony_ci avp->noa_start = tsf_time; 48262306a36Sopenharmony_ci avp->periodic_noa = false; 48362306a36Sopenharmony_ci avp->oneshot_noa = true; 48462306a36Sopenharmony_ci avp->noa_duration = duration + sc->sched.channel_switch_time; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 48762306a36Sopenharmony_ci "oneshot noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n", 48862306a36Sopenharmony_ci avp->noa_duration, 48962306a36Sopenharmony_ci avp->noa_start, 49062306a36Sopenharmony_ci avp->noa_index, 49162306a36Sopenharmony_ci avp->periodic_noa); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_civoid ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, 49562306a36Sopenharmony_ci enum ath_chanctx_event ev) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 49862306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 49962306a36Sopenharmony_ci struct ath_beacon_config *cur_conf; 50062306a36Sopenharmony_ci struct ath_vif *avp = NULL; 50162306a36Sopenharmony_ci struct ath_chanctx *ctx; 50262306a36Sopenharmony_ci u32 tsf_time; 50362306a36Sopenharmony_ci u32 beacon_int; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (vif) 50662306a36Sopenharmony_ci avp = (struct ath_vif *) vif->drv_priv; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s, delta: %u ms\n", 51162306a36Sopenharmony_ci sc->cur_chan->chandef.center_freq1, 51262306a36Sopenharmony_ci chanctx_event_string(ev), 51362306a36Sopenharmony_ci chanctx_state_string(sc->sched.state), 51462306a36Sopenharmony_ci chanctx_event_delta(sc)); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci switch (ev) { 51762306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_BEACON_PREPARE: 51862306a36Sopenharmony_ci if (avp->offchannel_duration) 51962306a36Sopenharmony_ci avp->offchannel_duration = 0; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (avp->oneshot_noa) { 52262306a36Sopenharmony_ci avp->noa_duration = 0; 52362306a36Sopenharmony_ci avp->oneshot_noa = false; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 52662306a36Sopenharmony_ci "Clearing oneshot NoA\n"); 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (avp->chanctx != sc->cur_chan) { 53062306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 53162306a36Sopenharmony_ci "Contexts differ, not preparing beacon\n"); 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (sc->sched.offchannel_pending && !sc->sched.wait_switch) { 53662306a36Sopenharmony_ci sc->sched.offchannel_pending = false; 53762306a36Sopenharmony_ci sc->next_chan = &sc->offchannel.chan; 53862306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; 53962306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 54062306a36Sopenharmony_ci "Setting offchannel_pending to false\n"); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci ctx = ath_chanctx_get_next(sc, sc->cur_chan); 54462306a36Sopenharmony_ci if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) { 54562306a36Sopenharmony_ci sc->next_chan = ctx; 54662306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; 54762306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 54862306a36Sopenharmony_ci "Set next context, move chanctx state to WAIT_FOR_BEACON\n"); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* if the timer missed its window, use the next interval */ 55262306a36Sopenharmony_ci if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) { 55362306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; 55462306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 55562306a36Sopenharmony_ci "Move chanctx state from WAIT_FOR_TIMER to WAIT_FOR_BEACON\n"); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (sc->sched.mgd_prepare_tx) 55962306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * When a context becomes inactive, for example, 56362306a36Sopenharmony_ci * disassociation of a station context, the NoA 56462306a36Sopenharmony_ci * attribute needs to be removed from subsequent 56562306a36Sopenharmony_ci * beacons. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci if (!ctx->active && avp->noa_duration && 56862306a36Sopenharmony_ci sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) { 56962306a36Sopenharmony_ci avp->noa_duration = 0; 57062306a36Sopenharmony_ci avp->periodic_noa = false; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 57362306a36Sopenharmony_ci "Clearing NoA schedule\n"); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "Preparing beacon for vif: %pM\n", vif->addr); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci sc->sched.beacon_pending = true; 58262306a36Sopenharmony_ci sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci cur_conf = &sc->cur_chan->beacon; 58562306a36Sopenharmony_ci beacon_int = TU_TO_USEC(cur_conf->beacon_interval); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* defer channel switch by a quarter beacon interval */ 58862306a36Sopenharmony_ci tsf_time = sc->sched.next_tbtt + beacon_int / 4; 58962306a36Sopenharmony_ci sc->sched.switch_start_time = tsf_time; 59062306a36Sopenharmony_ci sc->cur_chan->last_beacon = sc->sched.next_tbtt; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* 59362306a36Sopenharmony_ci * If an offchannel switch is scheduled to happen after 59462306a36Sopenharmony_ci * a beacon transmission, update the NoA with one-shot 59562306a36Sopenharmony_ci * values and increment the index. 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci if (sc->next_chan == &sc->offchannel.chan) { 59862306a36Sopenharmony_ci ath_chanctx_offchannel_noa(sc, ctx, avp, tsf_time); 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ath_chanctx_handle_bmiss(sc, ctx, avp); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* 60562306a36Sopenharmony_ci * If a mgd_prepare_tx() has been called by mac80211, 60662306a36Sopenharmony_ci * a one-shot NoA needs to be sent. This can happen 60762306a36Sopenharmony_ci * with one or more active channel contexts - in both 60862306a36Sopenharmony_ci * cases, a new NoA schedule has to be advertised. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci if (sc->sched.mgd_prepare_tx) { 61162306a36Sopenharmony_ci ath_chanctx_set_oneshot_noa(sc, avp, tsf_time, 61262306a36Sopenharmony_ci jiffies_to_usecs(HZ / 5)); 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Prevent wrap-around issues */ 61762306a36Sopenharmony_ci if (avp->noa_duration && tsf_time - avp->noa_start > BIT(30)) 61862306a36Sopenharmony_ci avp->noa_duration = 0; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* 62162306a36Sopenharmony_ci * If multiple contexts are active, start periodic 62262306a36Sopenharmony_ci * NoA and increment the index for the first 62362306a36Sopenharmony_ci * announcement. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci if (ctx->active && 62662306a36Sopenharmony_ci (!avp->noa_duration || sc->sched.force_noa_update)) 62762306a36Sopenharmony_ci ath_chanctx_set_periodic_noa(sc, avp, cur_conf, 62862306a36Sopenharmony_ci tsf_time, beacon_int); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (ctx->active && sc->sched.force_noa_update) 63162306a36Sopenharmony_ci sc->sched.force_noa_update = false; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_BEACON_SENT: 63562306a36Sopenharmony_ci if (!sc->sched.beacon_pending) { 63662306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 63762306a36Sopenharmony_ci "No pending beacon\n"); 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci sc->sched.beacon_pending = false; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (sc->sched.mgd_prepare_tx) { 64462306a36Sopenharmony_ci sc->sched.mgd_prepare_tx = false; 64562306a36Sopenharmony_ci complete(&sc->go_beacon); 64662306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 64762306a36Sopenharmony_ci "Beacon sent, complete go_beacon\n"); 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 65562306a36Sopenharmony_ci "Move chanctx state to WAIT_FOR_TIMER\n"); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; 65862306a36Sopenharmony_ci ath_chanctx_setup_timer(sc, sc->sched.switch_start_time); 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_TSF_TIMER: 66162306a36Sopenharmony_ci if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER) 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (!sc->cur_chan->switch_after_beacon && 66562306a36Sopenharmony_ci sc->sched.beacon_pending) 66662306a36Sopenharmony_ci sc->sched.beacon_miss++; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 66962306a36Sopenharmony_ci "Move chanctx state to SWITCH\n"); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_SWITCH; 67262306a36Sopenharmony_ci ieee80211_queue_work(sc->hw, &sc->chanctx_work); 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_BEACON_RECEIVED: 67562306a36Sopenharmony_ci if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || 67662306a36Sopenharmony_ci sc->cur_chan == &sc->offchannel.chan) 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci sc->sched.beacon_pending = false; 68062306a36Sopenharmony_ci sc->sched.beacon_miss = 0; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE || 68362306a36Sopenharmony_ci !sc->sched.beacon_adjust || 68462306a36Sopenharmony_ci !sc->cur_chan->tsf_val) 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci ath_chanctx_adjust_tbtt_delta(sc); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* TSF time might have been updated by the incoming beacon, 69062306a36Sopenharmony_ci * need update the channel switch timer to reflect the change. 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci tsf_time = sc->sched.switch_start_time; 69362306a36Sopenharmony_ci tsf_time -= (u32) sc->cur_chan->tsf_val + 69462306a36Sopenharmony_ci ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL); 69562306a36Sopenharmony_ci tsf_time += ath9k_hw_gettsf32(ah); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci sc->sched.beacon_adjust = false; 69862306a36Sopenharmony_ci ath_chanctx_setup_timer(sc, tsf_time); 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_AUTHORIZED: 70162306a36Sopenharmony_ci if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || 70262306a36Sopenharmony_ci avp->chanctx != sc->cur_chan) 70362306a36Sopenharmony_ci break; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 70662306a36Sopenharmony_ci "Move chanctx state from FORCE_ACTIVE to IDLE\n"); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_IDLE; 70962306a36Sopenharmony_ci fallthrough; 71062306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_SWITCH: 71162306a36Sopenharmony_ci if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || 71262306a36Sopenharmony_ci sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE || 71362306a36Sopenharmony_ci sc->cur_chan->switch_after_beacon || 71462306a36Sopenharmony_ci sc->cur_chan == &sc->offchannel.chan) 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* If this is a station chanctx, stay active for a half 71862306a36Sopenharmony_ci * beacon period (minus channel switch time) 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_ci sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); 72162306a36Sopenharmony_ci cur_conf = &sc->cur_chan->beacon; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 72462306a36Sopenharmony_ci "Move chanctx state to WAIT_FOR_TIMER (event SWITCH)\n"); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; 72762306a36Sopenharmony_ci sc->sched.wait_switch = false; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (sc->sched.extend_absence) { 73262306a36Sopenharmony_ci sc->sched.beacon_miss = 0; 73362306a36Sopenharmony_ci tsf_time *= 3; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci tsf_time -= sc->sched.channel_switch_time; 73762306a36Sopenharmony_ci tsf_time += ath9k_hw_gettsf32(sc->sc_ah); 73862306a36Sopenharmony_ci sc->sched.switch_start_time = tsf_time; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci ath_chanctx_setup_timer(sc, tsf_time); 74162306a36Sopenharmony_ci sc->sched.beacon_pending = true; 74262306a36Sopenharmony_ci sc->sched.beacon_adjust = true; 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: 74562306a36Sopenharmony_ci if (sc->cur_chan == &sc->offchannel.chan || 74662306a36Sopenharmony_ci sc->cur_chan->switch_after_beacon) 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); 75062306a36Sopenharmony_ci ieee80211_queue_work(sc->hw, &sc->chanctx_work); 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_UNASSIGN: 75362306a36Sopenharmony_ci if (sc->cur_chan->assigned) { 75462306a36Sopenharmony_ci if (sc->next_chan && !sc->next_chan->assigned && 75562306a36Sopenharmony_ci sc->next_chan != &sc->offchannel.chan) 75662306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_IDLE; 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci ctx = ath_chanctx_get_next(sc, sc->cur_chan); 76162306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_IDLE; 76262306a36Sopenharmony_ci if (!ctx->assigned) 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci sc->next_chan = ctx; 76662306a36Sopenharmony_ci ieee80211_queue_work(sc->hw, &sc->chanctx_work); 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_ASSIGN: 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci case ATH_CHANCTX_EVENT_CHANGE: 77162306a36Sopenharmony_ci break; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_civoid ath_chanctx_beacon_sent_ev(struct ath_softc *sc, 77862306a36Sopenharmony_ci enum ath_chanctx_event ev) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci if (sc->sched.beacon_pending) 78162306a36Sopenharmony_ci ath_chanctx_event(sc, NULL, ev); 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_civoid ath_chanctx_beacon_recv_ev(struct ath_softc *sc, 78562306a36Sopenharmony_ci enum ath_chanctx_event ev) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci ath_chanctx_event(sc, NULL, ev); 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic int ath_scan_channel_duration(struct ath_softc *sc, 79162306a36Sopenharmony_ci struct ieee80211_channel *chan) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct cfg80211_scan_request *req = sc->offchannel.scan_req; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR)) 79662306a36Sopenharmony_ci return (HZ / 9); /* ~110 ms */ 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return (HZ / 16); /* ~60 ms */ 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, 80262306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) && 80962306a36Sopenharmony_ci (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) { 81062306a36Sopenharmony_ci if (chandef) 81162306a36Sopenharmony_ci ctx->chandef = *chandef; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci sc->sched.offchannel_pending = true; 81462306a36Sopenharmony_ci sc->sched.wait_switch = true; 81562306a36Sopenharmony_ci sc->sched.offchannel_duration = 81662306a36Sopenharmony_ci jiffies_to_usecs(sc->offchannel.duration) + 81762306a36Sopenharmony_ci sc->sched.channel_switch_time; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 82062306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 82162306a36Sopenharmony_ci "Set offchannel_pending to true\n"); 82262306a36Sopenharmony_ci return; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci sc->next_chan = ctx; 82662306a36Sopenharmony_ci if (chandef) { 82762306a36Sopenharmony_ci ctx->chandef = *chandef; 82862306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 82962306a36Sopenharmony_ci "Assigned next_chan to %d MHz\n", chandef->center_freq1); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (sc->next_chan == &sc->offchannel.chan) { 83362306a36Sopenharmony_ci sc->sched.offchannel_duration = 83462306a36Sopenharmony_ci jiffies_to_usecs(sc->offchannel.duration) + 83562306a36Sopenharmony_ci sc->sched.channel_switch_time; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (chandef) { 83862306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 83962306a36Sopenharmony_ci "Offchannel duration for chan %d MHz : %u\n", 84062306a36Sopenharmony_ci chandef->center_freq1, 84162306a36Sopenharmony_ci sc->sched.offchannel_duration); 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 84562306a36Sopenharmony_ci ieee80211_queue_work(sc->hw, &sc->chanctx_work); 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic void ath_chanctx_offchan_switch(struct ath_softc *sc, 84962306a36Sopenharmony_ci struct ieee80211_channel *chan) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 85262306a36Sopenharmony_ci struct cfg80211_chan_def chandef; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); 85562306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 85662306a36Sopenharmony_ci "Channel definition created: %d MHz\n", chandef.center_freq1); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef); 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, 86262306a36Sopenharmony_ci bool active) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct ath_chanctx *ctx; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci ath_for_each_chanctx(sc, ctx) { 86762306a36Sopenharmony_ci if (!ctx->assigned || list_empty(&ctx->vifs)) 86862306a36Sopenharmony_ci continue; 86962306a36Sopenharmony_ci if (active && !ctx->active) 87062306a36Sopenharmony_ci continue; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (ctx->switch_after_beacon) 87362306a36Sopenharmony_ci return ctx; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return &sc->chanctx[0]; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic void 88062306a36Sopenharmony_ciath_scan_next_channel(struct ath_softc *sc) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 88362306a36Sopenharmony_ci struct cfg80211_scan_request *req = sc->offchannel.scan_req; 88462306a36Sopenharmony_ci struct ieee80211_channel *chan; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (sc->offchannel.scan_idx >= req->n_channels) { 88762306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 88862306a36Sopenharmony_ci "Moving offchannel state to ATH_OFFCHANNEL_IDLE, " 88962306a36Sopenharmony_ci "scan_idx: %d, n_channels: %d\n", 89062306a36Sopenharmony_ci sc->offchannel.scan_idx, 89162306a36Sopenharmony_ci req->n_channels); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_IDLE; 89462306a36Sopenharmony_ci ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), 89562306a36Sopenharmony_ci NULL); 89662306a36Sopenharmony_ci return; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 90062306a36Sopenharmony_ci "Moving offchannel state to ATH_OFFCHANNEL_PROBE_SEND, scan_idx: %d\n", 90162306a36Sopenharmony_ci sc->offchannel.scan_idx); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci chan = req->channels[sc->offchannel.scan_idx++]; 90462306a36Sopenharmony_ci sc->offchannel.duration = ath_scan_channel_duration(sc, chan); 90562306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci ath_chanctx_offchan_switch(sc, chan); 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_civoid ath_offchannel_next(struct ath_softc *sc) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci struct ieee80211_vif *vif; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (sc->offchannel.scan_req) { 91562306a36Sopenharmony_ci vif = sc->offchannel.scan_vif; 91662306a36Sopenharmony_ci sc->offchannel.chan.txpower = vif->bss_conf.txpower; 91762306a36Sopenharmony_ci ath_scan_next_channel(sc); 91862306a36Sopenharmony_ci } else if (sc->offchannel.roc_vif) { 91962306a36Sopenharmony_ci vif = sc->offchannel.roc_vif; 92062306a36Sopenharmony_ci sc->offchannel.chan.txpower = vif->bss_conf.txpower; 92162306a36Sopenharmony_ci sc->offchannel.duration = 92262306a36Sopenharmony_ci msecs_to_jiffies(sc->offchannel.roc_duration); 92362306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_ROC_START; 92462306a36Sopenharmony_ci ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan); 92562306a36Sopenharmony_ci } else { 92662306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 92762306a36Sopenharmony_ci sc->sched.offchannel_pending = false; 92862306a36Sopenharmony_ci sc->sched.wait_switch = false; 92962306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false), 93262306a36Sopenharmony_ci NULL); 93362306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_IDLE; 93462306a36Sopenharmony_ci if (sc->ps_idle) 93562306a36Sopenharmony_ci ath_cancel_work(sc); 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_civoid ath_roc_complete(struct ath_softc *sc, enum ath_roc_complete_reason reason) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci sc->offchannel.roc_vif = NULL; 94462306a36Sopenharmony_ci sc->offchannel.roc_chan = NULL; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci switch (reason) { 94762306a36Sopenharmony_ci case ATH_ROC_COMPLETE_ABORT: 94862306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "RoC aborted\n"); 94962306a36Sopenharmony_ci ieee80211_remain_on_channel_expired(sc->hw); 95062306a36Sopenharmony_ci break; 95162306a36Sopenharmony_ci case ATH_ROC_COMPLETE_EXPIRE: 95262306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "RoC expired\n"); 95362306a36Sopenharmony_ci ieee80211_remain_on_channel_expired(sc->hw); 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci case ATH_ROC_COMPLETE_CANCEL: 95662306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "RoC canceled\n"); 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci ath_offchannel_next(sc); 96162306a36Sopenharmony_ci ath9k_ps_restore(sc); 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_civoid ath_scan_complete(struct ath_softc *sc, bool abort) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 96762306a36Sopenharmony_ci struct cfg80211_scan_info info = { 96862306a36Sopenharmony_ci .aborted = abort, 96962306a36Sopenharmony_ci }; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (abort) 97262306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "HW scan aborted\n"); 97362306a36Sopenharmony_ci else 97462306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "HW scan complete\n"); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci sc->offchannel.scan_req = NULL; 97762306a36Sopenharmony_ci sc->offchannel.scan_vif = NULL; 97862306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_IDLE; 97962306a36Sopenharmony_ci ieee80211_scan_completed(sc->hw, &info); 98062306a36Sopenharmony_ci clear_bit(ATH_OP_SCANNING, &common->op_flags); 98162306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 98262306a36Sopenharmony_ci if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) 98362306a36Sopenharmony_ci sc->sched.force_noa_update = true; 98462306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 98562306a36Sopenharmony_ci ath_offchannel_next(sc); 98662306a36Sopenharmony_ci ath9k_ps_restore(sc); 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic void ath_scan_send_probe(struct ath_softc *sc, 99062306a36Sopenharmony_ci struct cfg80211_ssid *ssid) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct cfg80211_scan_request *req = sc->offchannel.scan_req; 99362306a36Sopenharmony_ci struct ieee80211_vif *vif = sc->offchannel.scan_vif; 99462306a36Sopenharmony_ci struct ath_tx_control txctl = {}; 99562306a36Sopenharmony_ci struct sk_buff *skb; 99662306a36Sopenharmony_ci struct ieee80211_tx_info *info; 99762306a36Sopenharmony_ci int band = sc->offchannel.chan.chandef.chan->band; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci skb = ieee80211_probereq_get(sc->hw, vif->addr, 100062306a36Sopenharmony_ci ssid->ssid, ssid->ssid_len, req->ie_len); 100162306a36Sopenharmony_ci if (!skb) 100262306a36Sopenharmony_ci return; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 100562306a36Sopenharmony_ci if (req->no_cck) 100662306a36Sopenharmony_ci info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (req->ie_len) 100962306a36Sopenharmony_ci skb_put_data(skb, req->ie, req->ie_len); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci skb_set_queue_mapping(skb, IEEE80211_AC_VO); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL)) 101462306a36Sopenharmony_ci goto error; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; 101762306a36Sopenharmony_ci if (ath_tx_start(sc->hw, skb, &txctl)) 101862306a36Sopenharmony_ci goto error; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cierror: 102362306a36Sopenharmony_ci ieee80211_free_txskb(sc->hw, skb); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic void ath_scan_channel_start(struct ath_softc *sc) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 102962306a36Sopenharmony_ci struct cfg80211_scan_request *req = sc->offchannel.scan_req; 103062306a36Sopenharmony_ci int i; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) && 103362306a36Sopenharmony_ci req->n_ssids) { 103462306a36Sopenharmony_ci for (i = 0; i < req->n_ssids; i++) 103562306a36Sopenharmony_ci ath_scan_send_probe(sc, &req->ssids[i]); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 104062306a36Sopenharmony_ci "Moving offchannel state to ATH_OFFCHANNEL_PROBE_WAIT\n"); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT; 104362306a36Sopenharmony_ci mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration); 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic void ath_chanctx_timer(struct timer_list *t) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci struct ath_softc *sc = from_timer(sc, t, sched.timer); 104962306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 105262306a36Sopenharmony_ci "Channel context timer invoked\n"); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic void ath_offchannel_timer(struct timer_list *t) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct ath_softc *sc = from_timer(sc, t, offchannel.timer); 106062306a36Sopenharmony_ci struct ath_chanctx *ctx; 106162306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "%s: offchannel state: %s\n", 106462306a36Sopenharmony_ci __func__, offchannel_state_string(sc->offchannel.state)); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci switch (sc->offchannel.state) { 106762306a36Sopenharmony_ci case ATH_OFFCHANNEL_PROBE_WAIT: 106862306a36Sopenharmony_ci if (!sc->offchannel.scan_req) 106962306a36Sopenharmony_ci return; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci /* get first active channel context */ 107262306a36Sopenharmony_ci ctx = ath_chanctx_get_oper_chan(sc, true); 107362306a36Sopenharmony_ci if (ctx->active) { 107462306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 107562306a36Sopenharmony_ci "Switch to oper/active context, " 107662306a36Sopenharmony_ci "move offchannel state to ATH_OFFCHANNEL_SUSPEND\n"); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; 107962306a36Sopenharmony_ci ath_chanctx_switch(sc, ctx, NULL); 108062306a36Sopenharmony_ci mod_timer(&sc->offchannel.timer, jiffies + HZ / 10); 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci fallthrough; 108462306a36Sopenharmony_ci case ATH_OFFCHANNEL_SUSPEND: 108562306a36Sopenharmony_ci if (!sc->offchannel.scan_req) 108662306a36Sopenharmony_ci return; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci ath_scan_next_channel(sc); 108962306a36Sopenharmony_ci break; 109062306a36Sopenharmony_ci case ATH_OFFCHANNEL_ROC_START: 109162306a36Sopenharmony_ci case ATH_OFFCHANNEL_ROC_WAIT: 109262306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; 109362306a36Sopenharmony_ci ath_roc_complete(sc, ATH_ROC_COMPLETE_EXPIRE); 109462306a36Sopenharmony_ci break; 109562306a36Sopenharmony_ci default: 109662306a36Sopenharmony_ci break; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_cistatic bool 110162306a36Sopenharmony_ciath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, 110262306a36Sopenharmony_ci bool powersave) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct ieee80211_vif *vif = avp->vif; 110562306a36Sopenharmony_ci struct ieee80211_sta *sta = NULL; 110662306a36Sopenharmony_ci struct ieee80211_hdr_3addr *nullfunc; 110762306a36Sopenharmony_ci struct ath_tx_control txctl; 110862306a36Sopenharmony_ci struct sk_buff *skb; 110962306a36Sopenharmony_ci int band = sc->cur_chan->chandef.chan->band; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci switch (vif->type) { 111262306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 111362306a36Sopenharmony_ci if (!avp->assoc) 111462306a36Sopenharmony_ci return false; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci skb = ieee80211_nullfunc_get(sc->hw, vif, -1, false); 111762306a36Sopenharmony_ci if (!skb) 111862306a36Sopenharmony_ci return false; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci nullfunc = (struct ieee80211_hdr_3addr *) skb->data; 112162306a36Sopenharmony_ci if (powersave) 112262306a36Sopenharmony_ci nullfunc->frame_control |= 112362306a36Sopenharmony_ci cpu_to_le16(IEEE80211_FCTL_PM); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci skb->priority = 7; 112662306a36Sopenharmony_ci skb_set_queue_mapping(skb, IEEE80211_AC_VO); 112762306a36Sopenharmony_ci if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) { 112862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 112962306a36Sopenharmony_ci return false; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci break; 113262306a36Sopenharmony_ci default: 113362306a36Sopenharmony_ci return false; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci memset(&txctl, 0, sizeof(txctl)); 113762306a36Sopenharmony_ci txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; 113862306a36Sopenharmony_ci txctl.sta = sta; 113962306a36Sopenharmony_ci if (ath_tx_start(sc->hw, skb, &txctl)) { 114062306a36Sopenharmony_ci ieee80211_free_txskb(sc->hw, skb); 114162306a36Sopenharmony_ci return false; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci return true; 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cistatic bool 114862306a36Sopenharmony_ciath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci struct ath_vif *avp; 115162306a36Sopenharmony_ci bool sent = false; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci rcu_read_lock(); 115462306a36Sopenharmony_ci list_for_each_entry(avp, &sc->cur_chan->vifs, list) { 115562306a36Sopenharmony_ci if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave)) 115662306a36Sopenharmony_ci sent = true; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci rcu_read_unlock(); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci return sent; 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic bool ath_chanctx_defer_switch(struct ath_softc *sc) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (sc->cur_chan == &sc->offchannel.chan) 116862306a36Sopenharmony_ci return false; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci switch (sc->sched.state) { 117162306a36Sopenharmony_ci case ATH_CHANCTX_STATE_SWITCH: 117262306a36Sopenharmony_ci return false; 117362306a36Sopenharmony_ci case ATH_CHANCTX_STATE_IDLE: 117462306a36Sopenharmony_ci if (!sc->cur_chan->switch_after_beacon) 117562306a36Sopenharmony_ci return false; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 117862306a36Sopenharmony_ci "Defer switch, set chanctx state to WAIT_FOR_BEACON\n"); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; 118162306a36Sopenharmony_ci break; 118262306a36Sopenharmony_ci default: 118362306a36Sopenharmony_ci break; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci return true; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic void ath_offchannel_channel_change(struct ath_softc *sc) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "%s: offchannel state: %s\n", 119462306a36Sopenharmony_ci __func__, offchannel_state_string(sc->offchannel.state)); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci switch (sc->offchannel.state) { 119762306a36Sopenharmony_ci case ATH_OFFCHANNEL_PROBE_SEND: 119862306a36Sopenharmony_ci if (!sc->offchannel.scan_req) 119962306a36Sopenharmony_ci return; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (sc->cur_chan->chandef.chan != 120262306a36Sopenharmony_ci sc->offchannel.chan.chandef.chan) 120362306a36Sopenharmony_ci return; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci ath_scan_channel_start(sc); 120662306a36Sopenharmony_ci break; 120762306a36Sopenharmony_ci case ATH_OFFCHANNEL_IDLE: 120862306a36Sopenharmony_ci if (!sc->offchannel.scan_req) 120962306a36Sopenharmony_ci return; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci ath_scan_complete(sc, false); 121262306a36Sopenharmony_ci break; 121362306a36Sopenharmony_ci case ATH_OFFCHANNEL_ROC_START: 121462306a36Sopenharmony_ci if (sc->cur_chan != &sc->offchannel.chan) 121562306a36Sopenharmony_ci break; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT; 121862306a36Sopenharmony_ci mod_timer(&sc->offchannel.timer, 121962306a36Sopenharmony_ci jiffies + sc->offchannel.duration); 122062306a36Sopenharmony_ci ieee80211_ready_on_channel(sc->hw); 122162306a36Sopenharmony_ci break; 122262306a36Sopenharmony_ci case ATH_OFFCHANNEL_ROC_DONE: 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci default: 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_civoid ath_chanctx_set_next(struct ath_softc *sc, bool force) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 123262306a36Sopenharmony_ci struct ath_chanctx *old_ctx; 123362306a36Sopenharmony_ci struct timespec64 ts; 123462306a36Sopenharmony_ci bool measure_time = false; 123562306a36Sopenharmony_ci bool send_ps = false; 123662306a36Sopenharmony_ci bool queues_stopped = false; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 123962306a36Sopenharmony_ci if (!sc->next_chan) { 124062306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 124162306a36Sopenharmony_ci return; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (!force && ath_chanctx_defer_switch(sc)) { 124562306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 124662306a36Sopenharmony_ci return; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 125062306a36Sopenharmony_ci "%s: current: %d MHz, next: %d MHz\n", 125162306a36Sopenharmony_ci __func__, 125262306a36Sopenharmony_ci sc->cur_chan->chandef.center_freq1, 125362306a36Sopenharmony_ci sc->next_chan->chandef.center_freq1); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci if (sc->cur_chan != sc->next_chan) { 125662306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 125762306a36Sopenharmony_ci "Stopping current chanctx: %d\n", 125862306a36Sopenharmony_ci sc->cur_chan->chandef.center_freq1); 125962306a36Sopenharmony_ci sc->cur_chan->stopped = true; 126062306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (sc->next_chan == &sc->offchannel.chan) { 126362306a36Sopenharmony_ci ktime_get_raw_ts64(&ts); 126462306a36Sopenharmony_ci measure_time = true; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci ath9k_chanctx_stop_queues(sc, sc->cur_chan); 126862306a36Sopenharmony_ci queues_stopped = true; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci __ath9k_flush(sc->hw, ~0, true, false, false); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci if (ath_chanctx_send_ps_frame(sc, true)) 127362306a36Sopenharmony_ci __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), 127462306a36Sopenharmony_ci false, false, false); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci send_ps = true; 127762306a36Sopenharmony_ci spin_lock_bh(&sc->chan_lock); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (sc->cur_chan != &sc->offchannel.chan) { 128062306a36Sopenharmony_ci ktime_get_raw_ts64(&sc->cur_chan->tsf_ts); 128162306a36Sopenharmony_ci sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah); 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci old_ctx = sc->cur_chan; 128562306a36Sopenharmony_ci sc->cur_chan = sc->next_chan; 128662306a36Sopenharmony_ci sc->cur_chan->stopped = false; 128762306a36Sopenharmony_ci sc->next_chan = NULL; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (!sc->sched.offchannel_pending) 129062306a36Sopenharmony_ci sc->sched.offchannel_duration = 0; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE) 129362306a36Sopenharmony_ci sc->sched.state = ATH_CHANCTX_STATE_IDLE; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci spin_unlock_bh(&sc->chan_lock); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (sc->sc_ah->chip_fullsleep || 129862306a36Sopenharmony_ci memcmp(&sc->cur_chandef, &sc->cur_chan->chandef, 129962306a36Sopenharmony_ci sizeof(sc->cur_chandef))) { 130062306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, 130162306a36Sopenharmony_ci "%s: Set channel %d MHz\n", 130262306a36Sopenharmony_ci __func__, sc->cur_chan->chandef.center_freq1); 130362306a36Sopenharmony_ci ath_set_channel(sc); 130462306a36Sopenharmony_ci if (measure_time) 130562306a36Sopenharmony_ci sc->sched.channel_switch_time = 130662306a36Sopenharmony_ci ath9k_hw_get_tsf_offset(&ts, NULL); 130762306a36Sopenharmony_ci /* 130862306a36Sopenharmony_ci * A reset will ensure that all queues are woken up, 130962306a36Sopenharmony_ci * so there is no need to awaken them again. 131062306a36Sopenharmony_ci */ 131162306a36Sopenharmony_ci goto out; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (queues_stopped) 131562306a36Sopenharmony_ci ath9k_chanctx_wake_queues(sc, old_ctx); 131662306a36Sopenharmony_ciout: 131762306a36Sopenharmony_ci if (send_ps) 131862306a36Sopenharmony_ci ath_chanctx_send_ps_frame(sc, false); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci ath_offchannel_channel_change(sc); 132162306a36Sopenharmony_ci ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH); 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic void ath_chanctx_work(struct work_struct *work) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci struct ath_softc *sc = container_of(work, struct ath_softc, 132762306a36Sopenharmony_ci chanctx_work); 132862306a36Sopenharmony_ci mutex_lock(&sc->mutex); 132962306a36Sopenharmony_ci ath_chanctx_set_next(sc, false); 133062306a36Sopenharmony_ci mutex_unlock(&sc->mutex); 133162306a36Sopenharmony_ci} 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_civoid ath9k_offchannel_init(struct ath_softc *sc) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci struct ath_chanctx *ctx; 133662306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 133762306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 133862306a36Sopenharmony_ci struct ieee80211_channel *chan; 133962306a36Sopenharmony_ci int i; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci sband = &common->sbands[NL80211_BAND_2GHZ]; 134262306a36Sopenharmony_ci if (!sband->n_channels) 134362306a36Sopenharmony_ci sband = &common->sbands[NL80211_BAND_5GHZ]; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci chan = &sband->channels[0]; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci ctx = &sc->offchannel.chan; 134862306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->vifs); 134962306a36Sopenharmony_ci ctx->txpower = ATH_TXPOWER_MAX; 135062306a36Sopenharmony_ci cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctx->acq); i++) { 135362306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->acq[i].acq_new); 135462306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->acq[i].acq_old); 135562306a36Sopenharmony_ci spin_lock_init(&ctx->acq[i].lock); 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci sc->offchannel.chan.offchannel = true; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_civoid ath9k_init_channel_context(struct ath_softc *sc) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci INIT_WORK(&sc->chanctx_work, ath_chanctx_work); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci timer_setup(&sc->offchannel.timer, ath_offchannel_timer, 0); 136662306a36Sopenharmony_ci timer_setup(&sc->sched.timer, ath_chanctx_timer, 0); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci init_completion(&sc->go_beacon); 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_civoid ath9k_deinit_channel_context(struct ath_softc *sc) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci cancel_work_sync(&sc->chanctx_work); 137462306a36Sopenharmony_ci} 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_cibool ath9k_is_chanctx_enabled(void) 137762306a36Sopenharmony_ci{ 137862306a36Sopenharmony_ci return (ath9k_use_chanctx == 1); 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci/********************/ 138262306a36Sopenharmony_ci/* Queue management */ 138362306a36Sopenharmony_ci/********************/ 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_civoid ath9k_chanctx_stop_queues(struct ath_softc *sc, struct ath_chanctx *ctx) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 138862306a36Sopenharmony_ci int i; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci if (ctx == &sc->offchannel.chan) { 139162306a36Sopenharmony_ci ieee80211_stop_queue(sc->hw, 139262306a36Sopenharmony_ci sc->hw->offchannel_tx_hw_queue); 139362306a36Sopenharmony_ci } else { 139462306a36Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) 139562306a36Sopenharmony_ci ieee80211_stop_queue(sc->hw, 139662306a36Sopenharmony_ci ctx->hw_queue_base + i); 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP) 140062306a36Sopenharmony_ci ieee80211_stop_queue(sc->hw, sc->hw->queues - 2); 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_civoid ath9k_chanctx_wake_queues(struct ath_softc *sc, struct ath_chanctx *ctx) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 140762306a36Sopenharmony_ci int i; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (ctx == &sc->offchannel.chan) { 141062306a36Sopenharmony_ci ieee80211_wake_queue(sc->hw, 141162306a36Sopenharmony_ci sc->hw->offchannel_tx_hw_queue); 141262306a36Sopenharmony_ci } else { 141362306a36Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) 141462306a36Sopenharmony_ci ieee80211_wake_queue(sc->hw, 141562306a36Sopenharmony_ci ctx->hw_queue_base + i); 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (ah->opmode == NL80211_IFTYPE_AP) 141962306a36Sopenharmony_ci ieee80211_wake_queue(sc->hw, sc->hw->queues - 2); 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci/*****************/ 142362306a36Sopenharmony_ci/* P2P Powersave */ 142462306a36Sopenharmony_ci/*****************/ 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cistatic void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 142962306a36Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 143062306a36Sopenharmony_ci u32 tsf, target_tsf; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (!avp || !avp->noa.has_next_tsf) 143362306a36Sopenharmony_ci return; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci tsf = ath9k_hw_gettsf32(sc->sc_ah); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci target_tsf = avp->noa.next_tsf; 144062306a36Sopenharmony_ci if (!avp->noa.absent) 144162306a36Sopenharmony_ci target_tsf -= ATH_P2P_PS_STOP_TIME; 144262306a36Sopenharmony_ci else 144362306a36Sopenharmony_ci target_tsf += ATH_P2P_PS_STOP_TIME; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) 144662306a36Sopenharmony_ci target_tsf = tsf + ATH_P2P_PS_STOP_TIME; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci ath_dbg(common, CHAN_CTX, "%s absent %d tsf 0x%08X next_tsf 0x%08X (%dms)\n", 144962306a36Sopenharmony_ci __func__, avp->noa.absent, tsf, target_tsf, 145062306a36Sopenharmony_ci (target_tsf - tsf) / 1000); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, target_tsf, 1000000); 145362306a36Sopenharmony_ci} 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_cistatic void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 145862306a36Sopenharmony_ci u32 tsf; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (!sc->p2p_ps_timer) 146162306a36Sopenharmony_ci return; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci if (vif->type != NL80211_IFTYPE_STATION) 146462306a36Sopenharmony_ci return; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci sc->p2p_ps_vif = avp; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (sc->ps_flags & PS_BEACON_SYNC) 146962306a36Sopenharmony_ci return; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci tsf = ath9k_hw_gettsf32(sc->sc_ah); 147262306a36Sopenharmony_ci ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); 147362306a36Sopenharmony_ci ath9k_update_p2p_ps_timer(sc, avp); 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic u8 ath9k_get_ctwin(struct ath_softc *sc, struct ath_vif *avp) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; 147962306a36Sopenharmony_ci u8 switch_time, ctwin; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci /* 148262306a36Sopenharmony_ci * Channel switch in multi-channel mode is deferred 148362306a36Sopenharmony_ci * by a quarter beacon interval when handling 148462306a36Sopenharmony_ci * ATH_CHANCTX_EVENT_BEACON_PREPARE, so the P2P-GO 148562306a36Sopenharmony_ci * interface is guaranteed to be discoverable 148662306a36Sopenharmony_ci * for that duration after a TBTT. 148762306a36Sopenharmony_ci */ 148862306a36Sopenharmony_ci switch_time = cur_conf->beacon_interval / 4; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci ctwin = avp->vif->bss_conf.p2p_noa_attr.oppps_ctwindow; 149162306a36Sopenharmony_ci if (ctwin && (ctwin < switch_time)) 149262306a36Sopenharmony_ci return ctwin; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci if (switch_time < P2P_DEFAULT_CTWIN) 149562306a36Sopenharmony_ci return 0; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return P2P_DEFAULT_CTWIN; 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_civoid ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, 150162306a36Sopenharmony_ci struct sk_buff *skb) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci static const u8 noa_ie_hdr[] = { 150462306a36Sopenharmony_ci WLAN_EID_VENDOR_SPECIFIC, /* type */ 150562306a36Sopenharmony_ci 0, /* length */ 150662306a36Sopenharmony_ci 0x50, 0x6f, 0x9a, /* WFA OUI */ 150762306a36Sopenharmony_ci 0x09, /* P2P subtype */ 150862306a36Sopenharmony_ci 0x0c, /* Notice of Absence */ 150962306a36Sopenharmony_ci 0x00, /* LSB of little-endian len */ 151062306a36Sopenharmony_ci 0x00, /* MSB of little-endian len */ 151162306a36Sopenharmony_ci }; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci struct ieee80211_p2p_noa_attr *noa; 151462306a36Sopenharmony_ci int noa_len, noa_desc, i = 0; 151562306a36Sopenharmony_ci u8 *hdr; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (!avp->offchannel_duration && !avp->noa_duration) 151862306a36Sopenharmony_ci return; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci noa_desc = !!avp->offchannel_duration + !!avp->noa_duration; 152162306a36Sopenharmony_ci noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci hdr = skb_put_data(skb, noa_ie_hdr, sizeof(noa_ie_hdr)); 152462306a36Sopenharmony_ci hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2; 152562306a36Sopenharmony_ci hdr[7] = noa_len; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci noa = skb_put_zero(skb, noa_len); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci noa->index = avp->noa_index; 153062306a36Sopenharmony_ci noa->oppps_ctwindow = ath9k_get_ctwin(sc, avp); 153162306a36Sopenharmony_ci if (noa->oppps_ctwindow) 153262306a36Sopenharmony_ci noa->oppps_ctwindow |= BIT(7); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci if (avp->noa_duration) { 153562306a36Sopenharmony_ci if (avp->periodic_noa) { 153662306a36Sopenharmony_ci u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval); 153762306a36Sopenharmony_ci noa->desc[i].count = 255; 153862306a36Sopenharmony_ci noa->desc[i].interval = cpu_to_le32(interval); 153962306a36Sopenharmony_ci } else { 154062306a36Sopenharmony_ci noa->desc[i].count = 1; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci noa->desc[i].start_time = cpu_to_le32(avp->noa_start); 154462306a36Sopenharmony_ci noa->desc[i].duration = cpu_to_le32(avp->noa_duration); 154562306a36Sopenharmony_ci i++; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci if (avp->offchannel_duration) { 154962306a36Sopenharmony_ci noa->desc[i].count = 1; 155062306a36Sopenharmony_ci noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start); 155162306a36Sopenharmony_ci noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration); 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci} 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_civoid ath9k_p2p_ps_timer(void *priv) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci struct ath_softc *sc = priv; 155862306a36Sopenharmony_ci struct ath_vif *avp = sc->p2p_ps_vif; 155962306a36Sopenharmony_ci struct ieee80211_vif *vif; 156062306a36Sopenharmony_ci struct ieee80211_sta *sta; 156162306a36Sopenharmony_ci struct ath_node *an; 156262306a36Sopenharmony_ci u32 tsf; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci del_timer_sync(&sc->sched.timer); 156562306a36Sopenharmony_ci ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer); 156662306a36Sopenharmony_ci ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (!avp || avp->chanctx != sc->cur_chan) 156962306a36Sopenharmony_ci return; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci tsf = ath9k_hw_gettsf32(sc->sc_ah); 157262306a36Sopenharmony_ci if (!avp->noa.absent) 157362306a36Sopenharmony_ci tsf += ATH_P2P_PS_STOP_TIME; 157462306a36Sopenharmony_ci else 157562306a36Sopenharmony_ci tsf -= ATH_P2P_PS_STOP_TIME; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci if (!avp->noa.has_next_tsf || 157862306a36Sopenharmony_ci avp->noa.next_tsf - tsf > BIT(31)) 157962306a36Sopenharmony_ci ieee80211_update_p2p_noa(&avp->noa, tsf); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci ath9k_update_p2p_ps_timer(sc, avp); 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci rcu_read_lock(); 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci vif = avp->vif; 158662306a36Sopenharmony_ci sta = ieee80211_find_sta(vif, avp->bssid); 158762306a36Sopenharmony_ci if (!sta) 158862306a36Sopenharmony_ci goto out; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci an = (void *) sta->drv_priv; 159162306a36Sopenharmony_ci if (an->sleeping == !!avp->noa.absent) 159262306a36Sopenharmony_ci goto out; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci an->sleeping = avp->noa.absent; 159562306a36Sopenharmony_ci if (an->sleeping) 159662306a36Sopenharmony_ci ath_tx_aggr_sleep(sta, sc, an); 159762306a36Sopenharmony_ci else 159862306a36Sopenharmony_ci ath_tx_aggr_wakeup(sc, an); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ciout: 160162306a36Sopenharmony_ci rcu_read_unlock(); 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_civoid ath9k_p2p_bss_info_changed(struct ath_softc *sc, 160562306a36Sopenharmony_ci struct ieee80211_vif *vif) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci unsigned long flags; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 161062306a36Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 161162306a36Sopenharmony_ci ath9k_update_p2p_ps(sc, vif); 161262306a36Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 161362306a36Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_civoid ath9k_p2p_beacon_sync(struct ath_softc *sc) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci if (sc->p2p_ps_vif) 161962306a36Sopenharmony_ci ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif); 162062306a36Sopenharmony_ci} 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_civoid ath9k_p2p_remove_vif(struct ath_softc *sc, 162362306a36Sopenharmony_ci struct ieee80211_vif *vif) 162462306a36Sopenharmony_ci{ 162562306a36Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 162862306a36Sopenharmony_ci if (avp == sc->p2p_ps_vif) { 162962306a36Sopenharmony_ci sc->p2p_ps_vif = NULL; 163062306a36Sopenharmony_ci ath9k_update_p2p_ps_timer(sc, NULL); 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 163362306a36Sopenharmony_ci} 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ciint ath9k_init_p2p(struct ath_softc *sc) 163662306a36Sopenharmony_ci{ 163762306a36Sopenharmony_ci sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer, 163862306a36Sopenharmony_ci NULL, sc, AR_FIRST_NDP_TIMER); 163962306a36Sopenharmony_ci if (!sc->p2p_ps_timer) 164062306a36Sopenharmony_ci return -ENOMEM; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci return 0; 164362306a36Sopenharmony_ci} 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_civoid ath9k_deinit_p2p(struct ath_softc *sc) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci if (sc->p2p_ps_timer) 164862306a36Sopenharmony_ci ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer); 164962306a36Sopenharmony_ci} 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ 1652