162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications 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 <linux/dma-mapping.h>
1862306a36Sopenharmony_ci#include "ath9k.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define FUDGE 2
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic void ath9k_reset_beacon_status(struct ath_softc *sc)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	sc->beacon.tx_processed = false;
2562306a36Sopenharmony_ci	sc->beacon.tx_last = false;
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci *  This function will modify certain transmit queue properties depending on
3062306a36Sopenharmony_ci *  the operating mode of the station (AP or AdHoc).  Parameters are AIFS
3162306a36Sopenharmony_ci *  settings and channel width min/max
3262306a36Sopenharmony_ci*/
3362306a36Sopenharmony_cistatic void ath9k_beaconq_config(struct ath_softc *sc)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
3662306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
3762306a36Sopenharmony_ci	struct ath9k_tx_queue_info qi, qi_be;
3862306a36Sopenharmony_ci	struct ath_txq *txq;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
4362306a36Sopenharmony_ci	    sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
4462306a36Sopenharmony_ci		/* Always burst out beacon and CAB traffic. */
4562306a36Sopenharmony_ci		qi.tqi_aifs = 1;
4662306a36Sopenharmony_ci		qi.tqi_cwmin = 0;
4762306a36Sopenharmony_ci		qi.tqi_cwmax = 0;
4862306a36Sopenharmony_ci	} else {
4962306a36Sopenharmony_ci		/* Adhoc mode; important thing is to use 2x cwmin. */
5062306a36Sopenharmony_ci		txq = sc->tx.txq_map[IEEE80211_AC_BE];
5162306a36Sopenharmony_ci		ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be);
5262306a36Sopenharmony_ci		qi.tqi_aifs = qi_be.tqi_aifs;
5362306a36Sopenharmony_ci		if (ah->slottime == 20)
5462306a36Sopenharmony_ci			qi.tqi_cwmin = 2*qi_be.tqi_cwmin;
5562306a36Sopenharmony_ci		else
5662306a36Sopenharmony_ci			qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
5762306a36Sopenharmony_ci		qi.tqi_cwmax = qi_be.tqi_cwmax;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
6162306a36Sopenharmony_ci		ath_err(common, "Unable to update h/w beacon queue parameters\n");
6262306a36Sopenharmony_ci	} else {
6362306a36Sopenharmony_ci		ath9k_hw_resettxqueue(ah, sc->beacon.beaconq);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci *  Associates the beacon frame buffer with a transmit descriptor.  Will set
6962306a36Sopenharmony_ci *  up rate codes, and channel flags. Beacons are always sent out at the
7062306a36Sopenharmony_ci *  lowest rate, and are not retried.
7162306a36Sopenharmony_ci*/
7262306a36Sopenharmony_cistatic void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
7362306a36Sopenharmony_ci			     struct ath_buf *bf, int rateidx)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct sk_buff *skb = bf->bf_mpdu;
7662306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
7762306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
7862306a36Sopenharmony_ci	struct ath_tx_info info;
7962306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
8062306a36Sopenharmony_ci	u8 chainmask = ah->txchainmask;
8162306a36Sopenharmony_ci	u8 i, rate = 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	sband = &common->sbands[sc->cur_chandef.chan->band];
8462306a36Sopenharmony_ci	rate = sband->bitrates[rateidx].hw_value;
8562306a36Sopenharmony_ci	if (vif->bss_conf.use_short_preamble)
8662306a36Sopenharmony_ci		rate |= sband->bitrates[rateidx].hw_value_short;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	memset(&info, 0, sizeof(info));
8962306a36Sopenharmony_ci	info.pkt_len = skb->len + FCS_LEN;
9062306a36Sopenharmony_ci	info.type = ATH9K_PKT_TYPE_BEACON;
9162306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
9262306a36Sopenharmony_ci		info.txpower[i] = MAX_RATE_POWER;
9362306a36Sopenharmony_ci	info.keyix = ATH9K_TXKEYIX_INVALID;
9462306a36Sopenharmony_ci	info.keytype = ATH9K_KEY_TYPE_CLEAR;
9562306a36Sopenharmony_ci	info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	info.buf_addr[0] = bf->bf_buf_addr;
9862306a36Sopenharmony_ci	info.buf_len[0] = roundup(skb->len, 4);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	info.is_first = true;
10162306a36Sopenharmony_ci	info.is_last = true;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	info.qcu = sc->beacon.beaconq;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	info.rates[0].Tries = 1;
10662306a36Sopenharmony_ci	info.rates[0].Rate = rate;
10762306a36Sopenharmony_ci	info.rates[0].ChSel = ath_txchainmask_reduction(sc, chainmask, rate);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
11362306a36Sopenharmony_ci					     struct ieee80211_vif *vif)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct ath_softc *sc = hw->priv;
11662306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
11762306a36Sopenharmony_ci	struct ath_buf *bf;
11862306a36Sopenharmony_ci	struct ath_vif *avp = (void *)vif->drv_priv;
11962306a36Sopenharmony_ci	struct sk_buff *skb;
12062306a36Sopenharmony_ci	struct ath_txq *cabq = sc->beacon.cabq;
12162306a36Sopenharmony_ci	struct ieee80211_tx_info *info;
12262306a36Sopenharmony_ci	struct ieee80211_mgmt *mgmt_hdr;
12362306a36Sopenharmony_ci	int cabq_depth;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (avp->av_bcbuf == NULL)
12662306a36Sopenharmony_ci		return NULL;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	bf = avp->av_bcbuf;
12962306a36Sopenharmony_ci	skb = bf->bf_mpdu;
13062306a36Sopenharmony_ci	if (skb) {
13162306a36Sopenharmony_ci		dma_unmap_single(sc->dev, bf->bf_buf_addr,
13262306a36Sopenharmony_ci				 skb->len, DMA_TO_DEVICE);
13362306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
13462306a36Sopenharmony_ci		bf->bf_buf_addr = 0;
13562306a36Sopenharmony_ci		bf->bf_mpdu = NULL;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	skb = ieee80211_beacon_get(hw, vif, 0);
13962306a36Sopenharmony_ci	if (skb == NULL)
14062306a36Sopenharmony_ci		return NULL;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	bf->bf_mpdu = skb;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	mgmt_hdr = (struct ieee80211_mgmt *)skb->data;
14562306a36Sopenharmony_ci	mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	ath_assign_seq(common, skb);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* Always assign NOA attr when MCC enabled */
15262306a36Sopenharmony_ci	if (ath9k_is_chanctx_enabled())
15362306a36Sopenharmony_ci		ath9k_beacon_add_noa(sc, avp, skb);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
15662306a36Sopenharmony_ci					 skb->len, DMA_TO_DEVICE);
15762306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
15862306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
15962306a36Sopenharmony_ci		bf->bf_mpdu = NULL;
16062306a36Sopenharmony_ci		bf->bf_buf_addr = 0;
16162306a36Sopenharmony_ci		ath_err(common, "dma_mapping_error on beaconing\n");
16262306a36Sopenharmony_ci		return NULL;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	skb = ieee80211_get_buffered_bc(hw, vif);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/*
16862306a36Sopenharmony_ci	 * if the CABQ traffic from previous DTIM is pending and the current
16962306a36Sopenharmony_ci	 *  beacon is also a DTIM.
17062306a36Sopenharmony_ci	 *  1) if there is only one vif let the cab traffic continue.
17162306a36Sopenharmony_ci	 *  2) if there are more than one vif and we are using staggered
17262306a36Sopenharmony_ci	 *     beacons, then drain the cabq by dropping all the frames in
17362306a36Sopenharmony_ci	 *     the cabq so that the current vifs cab traffic can be scheduled.
17462306a36Sopenharmony_ci	 */
17562306a36Sopenharmony_ci	spin_lock_bh(&cabq->axq_lock);
17662306a36Sopenharmony_ci	cabq_depth = cabq->axq_depth;
17762306a36Sopenharmony_ci	spin_unlock_bh(&cabq->axq_lock);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (skb && cabq_depth) {
18062306a36Sopenharmony_ci		if (sc->cur_chan->nvifs > 1) {
18162306a36Sopenharmony_ci			ath_dbg(common, BEACON,
18262306a36Sopenharmony_ci				"Flushing previous cabq traffic\n");
18362306a36Sopenharmony_ci			ath_draintxq(sc, cabq);
18462306a36Sopenharmony_ci		}
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (skb)
19062306a36Sopenharmony_ci		ath_tx_cabq(hw, vif, skb);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return bf;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_civoid ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
19862306a36Sopenharmony_ci	struct ath_vif *avp = (void *)vif->drv_priv;
19962306a36Sopenharmony_ci	int slot;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list);
20262306a36Sopenharmony_ci	list_del(&avp->av_bcbuf->list);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	for (slot = 0; slot < ATH_BCBUF; slot++) {
20562306a36Sopenharmony_ci		if (sc->beacon.bslot[slot] == NULL) {
20662306a36Sopenharmony_ci			avp->av_bslot = slot;
20762306a36Sopenharmony_ci			break;
20862306a36Sopenharmony_ci		}
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	sc->beacon.bslot[avp->av_bslot] = vif;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
21462306a36Sopenharmony_ci		avp->av_bslot);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_civoid ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
22062306a36Sopenharmony_ci	struct ath_vif *avp = (void *)vif->drv_priv;
22162306a36Sopenharmony_ci	struct ath_buf *bf = avp->av_bcbuf;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
22462306a36Sopenharmony_ci		avp->av_bslot);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	tasklet_disable(&sc->bcon_tasklet);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (bf && bf->bf_mpdu) {
22962306a36Sopenharmony_ci		struct sk_buff *skb = bf->bf_mpdu;
23062306a36Sopenharmony_ci		dma_unmap_single(sc->dev, bf->bf_buf_addr,
23162306a36Sopenharmony_ci				 skb->len, DMA_TO_DEVICE);
23262306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
23362306a36Sopenharmony_ci		bf->bf_mpdu = NULL;
23462306a36Sopenharmony_ci		bf->bf_buf_addr = 0;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	avp->av_bcbuf = NULL;
23862306a36Sopenharmony_ci	sc->beacon.bslot[avp->av_bslot] = NULL;
23962306a36Sopenharmony_ci	list_add_tail(&bf->list, &sc->beacon.bbuf);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	tasklet_enable(&sc->bcon_tasklet);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_civoid ath9k_beacon_ensure_primary_slot(struct ath_softc *sc)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
24762306a36Sopenharmony_ci	struct ieee80211_vif *vif;
24862306a36Sopenharmony_ci	struct ath_vif *avp;
24962306a36Sopenharmony_ci	s64 tsfadjust;
25062306a36Sopenharmony_ci	u32 offset;
25162306a36Sopenharmony_ci	int first_slot = ATH_BCBUF;
25262306a36Sopenharmony_ci	int slot;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	tasklet_disable_in_atomic(&sc->bcon_tasklet);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* Find first taken slot. */
25762306a36Sopenharmony_ci	for (slot = 0; slot < ATH_BCBUF; slot++) {
25862306a36Sopenharmony_ci		if (sc->beacon.bslot[slot]) {
25962306a36Sopenharmony_ci			first_slot = slot;
26062306a36Sopenharmony_ci			break;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	if (first_slot == 0)
26462306a36Sopenharmony_ci		goto out;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/* Re-enumarate all slots, moving them forward. */
26762306a36Sopenharmony_ci	for (slot = 0; slot < ATH_BCBUF; slot++) {
26862306a36Sopenharmony_ci		if (slot + first_slot < ATH_BCBUF) {
26962306a36Sopenharmony_ci			vif = sc->beacon.bslot[slot + first_slot];
27062306a36Sopenharmony_ci			sc->beacon.bslot[slot] = vif;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci			if (vif) {
27362306a36Sopenharmony_ci				avp = (void *)vif->drv_priv;
27462306a36Sopenharmony_ci				avp->av_bslot = slot;
27562306a36Sopenharmony_ci			}
27662306a36Sopenharmony_ci		} else {
27762306a36Sopenharmony_ci			sc->beacon.bslot[slot] = NULL;
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	vif = sc->beacon.bslot[0];
28262306a36Sopenharmony_ci	if (WARN_ON(!vif))
28362306a36Sopenharmony_ci		goto out;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* Get the tsf_adjust value for the new first slot. */
28662306a36Sopenharmony_ci	avp = (void *)vif->drv_priv;
28762306a36Sopenharmony_ci	tsfadjust = le64_to_cpu(avp->tsf_adjust);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	ath_dbg(common, CONFIG,
29062306a36Sopenharmony_ci		"Adjusting global TSF after beacon slot reassignment: %lld\n",
29162306a36Sopenharmony_ci		(signed long long)tsfadjust);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Modify TSF as required and update the HW. */
29462306a36Sopenharmony_ci	avp->chanctx->tsf_val += tsfadjust;
29562306a36Sopenharmony_ci	if (sc->cur_chan == avp->chanctx) {
29662306a36Sopenharmony_ci		offset = ath9k_hw_get_tsf_offset(&avp->chanctx->tsf_ts, NULL);
29762306a36Sopenharmony_ci		ath9k_hw_settsf64(sc->sc_ah, avp->chanctx->tsf_val + offset);
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* The slots tsf_adjust will be updated by ath9k_beacon_config later. */
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ciout:
30362306a36Sopenharmony_ci	tasklet_enable(&sc->bcon_tasklet);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic int ath9k_beacon_choose_slot(struct ath_softc *sc)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
30962306a36Sopenharmony_ci	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
31062306a36Sopenharmony_ci	u16 intval;
31162306a36Sopenharmony_ci	u32 tsftu;
31262306a36Sopenharmony_ci	u64 tsf;
31362306a36Sopenharmony_ci	int slot;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (sc->sc_ah->opmode != NL80211_IFTYPE_AP &&
31662306a36Sopenharmony_ci	    sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) {
31762306a36Sopenharmony_ci		ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
31862306a36Sopenharmony_ci			ath9k_hw_gettsf64(sc->sc_ah));
31962306a36Sopenharmony_ci		return 0;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL;
32362306a36Sopenharmony_ci	tsf = ath9k_hw_gettsf64(sc->sc_ah);
32462306a36Sopenharmony_ci	tsf += TU_TO_USEC(sc->sc_ah->config.sw_beacon_response_time);
32562306a36Sopenharmony_ci	tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF);
32662306a36Sopenharmony_ci	slot = (tsftu % (intval * ATH_BCBUF)) / intval;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	ath_dbg(common, BEACON, "slot: %d tsf: %llu tsftu: %u\n",
32962306a36Sopenharmony_ci		slot, tsf, tsftu / ATH_BCBUF);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return slot;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void ath9k_set_tsfadjust(struct ath_softc *sc,
33562306a36Sopenharmony_ci				struct ath_beacon_config *cur_conf)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
33862306a36Sopenharmony_ci	s64 tsfadjust;
33962306a36Sopenharmony_ci	int slot;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	for (slot = 0; slot < ATH_BCBUF; slot++) {
34262306a36Sopenharmony_ci		struct ath_vif *avp;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		if (!sc->beacon.bslot[slot])
34562306a36Sopenharmony_ci			continue;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		avp = (void *)sc->beacon.bslot[slot]->drv_priv;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		/* tsf_adjust is added to the TSF value. We send out the
35062306a36Sopenharmony_ci		 * beacon late, so need to adjust the TSF starting point to be
35162306a36Sopenharmony_ci		 * later in time (i.e. the theoretical first beacon has a TSF
35262306a36Sopenharmony_ci		 * of 0 after correction).
35362306a36Sopenharmony_ci		 */
35462306a36Sopenharmony_ci		tsfadjust = cur_conf->beacon_interval * avp->av_bslot;
35562306a36Sopenharmony_ci		tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF;
35662306a36Sopenharmony_ci		avp->tsf_adjust = cpu_to_le64(tsfadjust);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci		ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n",
35962306a36Sopenharmony_ci			(signed long long)tsfadjust, avp->av_bslot);
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cibool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	if (!vif || !vif->bss_conf.csa_active)
36662306a36Sopenharmony_ci		return false;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (!ieee80211_beacon_cntdwn_is_complete(vif))
36962306a36Sopenharmony_ci		return false;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	ieee80211_csa_finish(vif);
37262306a36Sopenharmony_ci	return true;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic void ath9k_csa_update_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct ath_softc *sc = data;
37862306a36Sopenharmony_ci	ath9k_csa_is_finished(sc, vif);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_civoid ath9k_csa_update(struct ath_softc *sc)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(sc->hw,
38462306a36Sopenharmony_ci						   IEEE80211_IFACE_ITER_NORMAL,
38562306a36Sopenharmony_ci						   ath9k_csa_update_vif, sc);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_civoid ath9k_beacon_tasklet(struct tasklet_struct *t)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct ath_softc *sc = from_tasklet(sc, t, bcon_tasklet);
39162306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
39262306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
39362306a36Sopenharmony_ci	struct ath_buf *bf = NULL;
39462306a36Sopenharmony_ci	struct ieee80211_vif *vif;
39562306a36Sopenharmony_ci	bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
39662306a36Sopenharmony_ci	int slot;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
39962306a36Sopenharmony_ci		ath_dbg(common, RESET,
40062306a36Sopenharmony_ci			"reset work is pending, skip beaconing now\n");
40162306a36Sopenharmony_ci		return;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/*
40562306a36Sopenharmony_ci	 * Check if the previous beacon has gone out.  If
40662306a36Sopenharmony_ci	 * not don't try to post another, skip this period
40762306a36Sopenharmony_ci	 * and wait for the next.  Missed beacons indicate
40862306a36Sopenharmony_ci	 * a problem and should not occur.  If we miss too
40962306a36Sopenharmony_ci	 * many consecutive beacons reset the device.
41062306a36Sopenharmony_ci	 */
41162306a36Sopenharmony_ci	if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) {
41262306a36Sopenharmony_ci		sc->beacon.bmisscnt++;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		ath9k_hw_check_nav(ah);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		/*
41762306a36Sopenharmony_ci		 * If the previous beacon has not been transmitted
41862306a36Sopenharmony_ci		 * and a MAC/BB hang has been identified, return
41962306a36Sopenharmony_ci		 * here because a chip reset would have been
42062306a36Sopenharmony_ci		 * initiated.
42162306a36Sopenharmony_ci		 */
42262306a36Sopenharmony_ci		if (!ath_hw_check(sc))
42362306a36Sopenharmony_ci			return;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) {
42662306a36Sopenharmony_ci			ath_dbg(common, BSTUCK,
42762306a36Sopenharmony_ci				"missed %u consecutive beacons\n",
42862306a36Sopenharmony_ci				sc->beacon.bmisscnt);
42962306a36Sopenharmony_ci			ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq);
43062306a36Sopenharmony_ci			if (sc->beacon.bmisscnt > 3)
43162306a36Sopenharmony_ci				ath9k_hw_bstuck_nfcal(ah);
43262306a36Sopenharmony_ci		} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
43362306a36Sopenharmony_ci			ath_dbg(common, BSTUCK, "beacon is officially stuck\n");
43462306a36Sopenharmony_ci			sc->beacon.bmisscnt = 0;
43562306a36Sopenharmony_ci			ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK);
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		return;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	slot = ath9k_beacon_choose_slot(sc);
44262306a36Sopenharmony_ci	vif = sc->beacon.bslot[slot];
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* EDMA devices check that in the tx completion function. */
44562306a36Sopenharmony_ci	if (!edma) {
44662306a36Sopenharmony_ci		if (ath9k_is_chanctx_enabled()) {
44762306a36Sopenharmony_ci			ath_chanctx_beacon_sent_ev(sc,
44862306a36Sopenharmony_ci					  ATH_CHANCTX_EVENT_BEACON_SENT);
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		if (ath9k_csa_is_finished(sc, vif))
45262306a36Sopenharmony_ci			return;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!vif || !vif->bss_conf.enable_beacon)
45662306a36Sopenharmony_ci		return;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (ath9k_is_chanctx_enabled()) {
45962306a36Sopenharmony_ci		ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE);
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	bf = ath9k_beacon_generate(sc->hw, vif);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (sc->beacon.bmisscnt != 0) {
46562306a36Sopenharmony_ci		ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n",
46662306a36Sopenharmony_ci			sc->beacon.bmisscnt);
46762306a36Sopenharmony_ci		sc->beacon.bmisscnt = 0;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/*
47162306a36Sopenharmony_ci	 * Handle slot time change when a non-ERP station joins/leaves
47262306a36Sopenharmony_ci	 * an 11g network.  The 802.11 layer notifies us via callback,
47362306a36Sopenharmony_ci	 * we mark updateslot, then wait one beacon before effecting
47462306a36Sopenharmony_ci	 * the change.  This gives associated stations at least one
47562306a36Sopenharmony_ci	 * beacon interval to note the state change.
47662306a36Sopenharmony_ci	 *
47762306a36Sopenharmony_ci	 * NB: The slot time change state machine is clocked according
47862306a36Sopenharmony_ci	 *     to whether we are bursting or staggering beacons.  We
47962306a36Sopenharmony_ci	 *     recognize the request to update and record the current
48062306a36Sopenharmony_ci	 *     slot then don't transition until that slot is reached
48162306a36Sopenharmony_ci	 *     again.  If we miss a beacon for that slot then we'll be
48262306a36Sopenharmony_ci	 *     slow to transition but we'll be sure at least one beacon
48362306a36Sopenharmony_ci	 *     interval has passed.  When bursting slot is always left
48462306a36Sopenharmony_ci	 *     set to ATH_BCBUF so this check is a noop.
48562306a36Sopenharmony_ci	 */
48662306a36Sopenharmony_ci	if (sc->beacon.updateslot == UPDATE) {
48762306a36Sopenharmony_ci		sc->beacon.updateslot = COMMIT;
48862306a36Sopenharmony_ci		sc->beacon.slotupdate = slot;
48962306a36Sopenharmony_ci	} else if (sc->beacon.updateslot == COMMIT &&
49062306a36Sopenharmony_ci		   sc->beacon.slotupdate == slot) {
49162306a36Sopenharmony_ci		ah->slottime = sc->beacon.slottime;
49262306a36Sopenharmony_ci		ath9k_hw_init_global_settings(ah);
49362306a36Sopenharmony_ci		sc->beacon.updateslot = OK;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (bf) {
49762306a36Sopenharmony_ci		ath9k_reset_beacon_status(sc);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		ath_dbg(common, BEACON,
50062306a36Sopenharmony_ci			"Transmitting beacon for slot: %d\n", slot);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		/* NB: cabq traffic should already be queued and primed */
50362306a36Sopenharmony_ci		ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		if (!edma)
50662306a36Sopenharmony_ci			ath9k_hw_txstart(ah, sc->beacon.beaconq);
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci/*
51162306a36Sopenharmony_ci * Both nexttbtt and intval have to be in usecs.
51262306a36Sopenharmony_ci */
51362306a36Sopenharmony_cistatic void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt,
51462306a36Sopenharmony_ci			      u32 intval)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	ath9k_hw_disable_interrupts(ah);
51962306a36Sopenharmony_ci	ath9k_beaconq_config(sc);
52062306a36Sopenharmony_ci	ath9k_hw_beaconinit(ah, nexttbtt, intval);
52162306a36Sopenharmony_ci	ah->imask |= ATH9K_INT_SWBA;
52262306a36Sopenharmony_ci	sc->beacon.bmisscnt = 0;
52362306a36Sopenharmony_ci	ath9k_hw_set_interrupts(ah);
52462306a36Sopenharmony_ci	ath9k_hw_enable_interrupts(ah);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic void ath9k_beacon_stop(struct ath_softc *sc)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	ath9k_hw_disable_interrupts(sc->sc_ah);
53062306a36Sopenharmony_ci	sc->sc_ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
53162306a36Sopenharmony_ci	sc->beacon.bmisscnt = 0;
53262306a36Sopenharmony_ci	ath9k_hw_set_interrupts(sc->sc_ah);
53362306a36Sopenharmony_ci	ath9k_hw_enable_interrupts(sc->sc_ah);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/*
53762306a36Sopenharmony_ci * For multi-bss ap support beacons are either staggered evenly over N slots or
53862306a36Sopenharmony_ci * burst together.  For the former arrange for the SWBA to be delivered for each
53962306a36Sopenharmony_ci * slot. Slots that are not occupied will generate nothing.
54062306a36Sopenharmony_ci */
54162306a36Sopenharmony_cistatic void ath9k_beacon_config_ap(struct ath_softc *sc,
54262306a36Sopenharmony_ci				   struct ath_beacon_config *conf)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF);
54762306a36Sopenharmony_ci	ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic void ath9k_beacon_config_sta(struct ath_hw *ah,
55162306a36Sopenharmony_ci				    struct ath_beacon_config *conf)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct ath9k_beacon_state bs;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (ath9k_cmn_beacon_config_sta(ah, conf, &bs) == -EPERM)
55662306a36Sopenharmony_ci		return;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	ath9k_hw_disable_interrupts(ah);
55962306a36Sopenharmony_ci	ath9k_hw_set_sta_beacon_timers(ah, &bs);
56062306a36Sopenharmony_ci	ah->imask |= ATH9K_INT_BMISS;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	ath9k_hw_set_interrupts(ah);
56362306a36Sopenharmony_ci	ath9k_hw_enable_interrupts(ah);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void ath9k_beacon_config_adhoc(struct ath_softc *sc,
56762306a36Sopenharmony_ci				      struct ath_beacon_config *conf)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
57062306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ath9k_reset_beacon_status(sc);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	ath9k_cmn_beacon_config_adhoc(ah, conf);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/*
57962306a36Sopenharmony_ci	 * Set the global 'beacon has been configured' flag for the
58062306a36Sopenharmony_ci	 * joiner case in IBSS mode.
58162306a36Sopenharmony_ci	 */
58262306a36Sopenharmony_ci	if (!conf->ibss_creator && conf->enable_beacon)
58362306a36Sopenharmony_ci		set_bit(ATH_OP_BEACONS, &common->op_flags);
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic void ath9k_cache_beacon_config(struct ath_softc *sc,
58762306a36Sopenharmony_ci				      struct ath_chanctx *ctx,
58862306a36Sopenharmony_ci				      struct ieee80211_vif *vif)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
59162306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
59262306a36Sopenharmony_ci	struct ath_beacon_config *cur_conf = &ctx->beacon;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	ath_dbg(common, BEACON,
59562306a36Sopenharmony_ci		"Caching beacon data for BSS: %pM\n", bss_conf->bssid);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	cur_conf->beacon_interval = bss_conf->beacon_int;
59862306a36Sopenharmony_ci	cur_conf->dtim_period = bss_conf->dtim_period;
59962306a36Sopenharmony_ci	cur_conf->dtim_count = 1;
60062306a36Sopenharmony_ci	cur_conf->ibss_creator = vif->cfg.ibss_creator;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/*
60362306a36Sopenharmony_ci	 * It looks like mac80211 may end up using beacon interval of zero in
60462306a36Sopenharmony_ci	 * some cases (at least for mesh point). Avoid getting into an
60562306a36Sopenharmony_ci	 * infinite loop by using a bit safer value instead. To be safe,
60662306a36Sopenharmony_ci	 * do sanity check on beacon interval for all operating modes.
60762306a36Sopenharmony_ci	 */
60862306a36Sopenharmony_ci	if (cur_conf->beacon_interval == 0)
60962306a36Sopenharmony_ci		cur_conf->beacon_interval = 100;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	cur_conf->bmiss_timeout =
61262306a36Sopenharmony_ci		ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/*
61562306a36Sopenharmony_ci	 * We don't parse dtim period from mac80211 during the driver
61662306a36Sopenharmony_ci	 * initialization as it breaks association with hidden-ssid
61762306a36Sopenharmony_ci	 * AP and it causes latency in roaming
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	if (cur_conf->dtim_period == 0)
62062306a36Sopenharmony_ci		cur_conf->dtim_period = 1;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	ath9k_set_tsfadjust(sc, cur_conf);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_civoid ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif,
62662306a36Sopenharmony_ci			 bool beacons)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
62962306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
63062306a36Sopenharmony_ci	struct ath_vif *avp;
63162306a36Sopenharmony_ci	struct ath_chanctx *ctx;
63262306a36Sopenharmony_ci	struct ath_beacon_config *cur_conf;
63362306a36Sopenharmony_ci	unsigned long flags;
63462306a36Sopenharmony_ci	bool enabled;
63562306a36Sopenharmony_ci	bool skip_beacon = false;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	if (!beacons) {
63862306a36Sopenharmony_ci		clear_bit(ATH_OP_BEACONS, &common->op_flags);
63962306a36Sopenharmony_ci		ath9k_beacon_stop(sc);
64062306a36Sopenharmony_ci		return;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	if (WARN_ON(!main_vif))
64462306a36Sopenharmony_ci		return;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	avp = (void *)main_vif->drv_priv;
64762306a36Sopenharmony_ci	ctx = avp->chanctx;
64862306a36Sopenharmony_ci	cur_conf = &ctx->beacon;
64962306a36Sopenharmony_ci	enabled = cur_conf->enable_beacon;
65062306a36Sopenharmony_ci	cur_conf->enable_beacon = beacons;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
65362306a36Sopenharmony_ci		ath9k_cache_beacon_config(sc, ctx, main_vif);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		ath9k_set_beacon(sc);
65662306a36Sopenharmony_ci		set_bit(ATH_OP_BEACONS, &common->op_flags);
65762306a36Sopenharmony_ci		return;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	/* Update the beacon configuration. */
66162306a36Sopenharmony_ci	ath9k_cache_beacon_config(sc, ctx, main_vif);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/*
66462306a36Sopenharmony_ci	 * Configure the HW beacon registers only when we have a valid
66562306a36Sopenharmony_ci	 * beacon interval.
66662306a36Sopenharmony_ci	 */
66762306a36Sopenharmony_ci	if (cur_conf->beacon_interval) {
66862306a36Sopenharmony_ci		/* Special case to sync the TSF when joining an existing IBSS.
66962306a36Sopenharmony_ci		 * This is only done if no AP interface is active.
67062306a36Sopenharmony_ci		 * Note that mac80211 always resets the TSF when creating a new
67162306a36Sopenharmony_ci		 * IBSS interface.
67262306a36Sopenharmony_ci		 */
67362306a36Sopenharmony_ci		if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC &&
67462306a36Sopenharmony_ci		    !enabled && beacons && !main_vif->cfg.ibss_creator) {
67562306a36Sopenharmony_ci			spin_lock_irqsave(&sc->sc_pm_lock, flags);
67662306a36Sopenharmony_ci			sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
67762306a36Sopenharmony_ci			spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
67862306a36Sopenharmony_ci			skip_beacon = true;
67962306a36Sopenharmony_ci		}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		/*
68262306a36Sopenharmony_ci		 * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode
68362306a36Sopenharmony_ci		 * here, it is done in ath9k_beacon_config_adhoc().
68462306a36Sopenharmony_ci		 */
68562306a36Sopenharmony_ci		if (beacons && !skip_beacon) {
68662306a36Sopenharmony_ci			set_bit(ATH_OP_BEACONS, &common->op_flags);
68762306a36Sopenharmony_ci			ath9k_set_beacon(sc);
68862306a36Sopenharmony_ci		} else {
68962306a36Sopenharmony_ci			clear_bit(ATH_OP_BEACONS, &common->op_flags);
69062306a36Sopenharmony_ci			ath9k_beacon_stop(sc);
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci	} else {
69362306a36Sopenharmony_ci		clear_bit(ATH_OP_BEACONS, &common->op_flags);
69462306a36Sopenharmony_ci		ath9k_beacon_stop(sc);
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_civoid ath9k_set_beacon(struct ath_softc *sc)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
70162306a36Sopenharmony_ci	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	switch (sc->sc_ah->opmode) {
70462306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
70562306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
70662306a36Sopenharmony_ci		ath9k_beacon_config_ap(sc, cur_conf);
70762306a36Sopenharmony_ci		break;
70862306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
70962306a36Sopenharmony_ci		ath9k_beacon_config_adhoc(sc, cur_conf);
71062306a36Sopenharmony_ci		break;
71162306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
71262306a36Sopenharmony_ci		ath9k_beacon_config_sta(sc->sc_ah, cur_conf);
71362306a36Sopenharmony_ci		break;
71462306a36Sopenharmony_ci	default:
71562306a36Sopenharmony_ci		ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
71662306a36Sopenharmony_ci		return;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci}
719