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