18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
188c2ecf20Sopenharmony_ci#include "ath9k.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define FUDGE 2
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic void ath9k_reset_beacon_status(struct ath_softc *sc)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	sc->beacon.tx_processed = false;
258c2ecf20Sopenharmony_ci	sc->beacon.tx_last = false;
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci *  This function will modify certain transmit queue properties depending on
308c2ecf20Sopenharmony_ci *  the operating mode of the station (AP or AdHoc).  Parameters are AIFS
318c2ecf20Sopenharmony_ci *  settings and channel width min/max
328c2ecf20Sopenharmony_ci*/
338c2ecf20Sopenharmony_cistatic void ath9k_beaconq_config(struct ath_softc *sc)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
368c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
378c2ecf20Sopenharmony_ci	struct ath9k_tx_queue_info qi, qi_be;
388c2ecf20Sopenharmony_ci	struct ath_txq *txq;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
438c2ecf20Sopenharmony_ci	    sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
448c2ecf20Sopenharmony_ci		/* Always burst out beacon and CAB traffic. */
458c2ecf20Sopenharmony_ci		qi.tqi_aifs = 1;
468c2ecf20Sopenharmony_ci		qi.tqi_cwmin = 0;
478c2ecf20Sopenharmony_ci		qi.tqi_cwmax = 0;
488c2ecf20Sopenharmony_ci	} else {
498c2ecf20Sopenharmony_ci		/* Adhoc mode; important thing is to use 2x cwmin. */
508c2ecf20Sopenharmony_ci		txq = sc->tx.txq_map[IEEE80211_AC_BE];
518c2ecf20Sopenharmony_ci		ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be);
528c2ecf20Sopenharmony_ci		qi.tqi_aifs = qi_be.tqi_aifs;
538c2ecf20Sopenharmony_ci		if (ah->slottime == 20)
548c2ecf20Sopenharmony_ci			qi.tqi_cwmin = 2*qi_be.tqi_cwmin;
558c2ecf20Sopenharmony_ci		else
568c2ecf20Sopenharmony_ci			qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
578c2ecf20Sopenharmony_ci		qi.tqi_cwmax = qi_be.tqi_cwmax;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
618c2ecf20Sopenharmony_ci		ath_err(common, "Unable to update h/w beacon queue parameters\n");
628c2ecf20Sopenharmony_ci	} else {
638c2ecf20Sopenharmony_ci		ath9k_hw_resettxqueue(ah, sc->beacon.beaconq);
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*
688c2ecf20Sopenharmony_ci *  Associates the beacon frame buffer with a transmit descriptor.  Will set
698c2ecf20Sopenharmony_ci *  up rate codes, and channel flags. Beacons are always sent out at the
708c2ecf20Sopenharmony_ci *  lowest rate, and are not retried.
718c2ecf20Sopenharmony_ci*/
728c2ecf20Sopenharmony_cistatic void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
738c2ecf20Sopenharmony_ci			     struct ath_buf *bf, int rateidx)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct sk_buff *skb = bf->bf_mpdu;
768c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
778c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
788c2ecf20Sopenharmony_ci	struct ath_tx_info info;
798c2ecf20Sopenharmony_ci	struct ieee80211_supported_band *sband;
808c2ecf20Sopenharmony_ci	u8 chainmask = ah->txchainmask;
818c2ecf20Sopenharmony_ci	u8 i, rate = 0;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	sband = &common->sbands[sc->cur_chandef.chan->band];
848c2ecf20Sopenharmony_ci	rate = sband->bitrates[rateidx].hw_value;
858c2ecf20Sopenharmony_ci	if (vif->bss_conf.use_short_preamble)
868c2ecf20Sopenharmony_ci		rate |= sband->bitrates[rateidx].hw_value_short;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	memset(&info, 0, sizeof(info));
898c2ecf20Sopenharmony_ci	info.pkt_len = skb->len + FCS_LEN;
908c2ecf20Sopenharmony_ci	info.type = ATH9K_PKT_TYPE_BEACON;
918c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
928c2ecf20Sopenharmony_ci		info.txpower[i] = MAX_RATE_POWER;
938c2ecf20Sopenharmony_ci	info.keyix = ATH9K_TXKEYIX_INVALID;
948c2ecf20Sopenharmony_ci	info.keytype = ATH9K_KEY_TYPE_CLEAR;
958c2ecf20Sopenharmony_ci	info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	info.buf_addr[0] = bf->bf_buf_addr;
988c2ecf20Sopenharmony_ci	info.buf_len[0] = roundup(skb->len, 4);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	info.is_first = true;
1018c2ecf20Sopenharmony_ci	info.is_last = true;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	info.qcu = sc->beacon.beaconq;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	info.rates[0].Tries = 1;
1068c2ecf20Sopenharmony_ci	info.rates[0].Rate = rate;
1078c2ecf20Sopenharmony_ci	info.rates[0].ChSel = ath_txchainmask_reduction(sc, chainmask, rate);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
1138c2ecf20Sopenharmony_ci					     struct ieee80211_vif *vif)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct ath_softc *sc = hw->priv;
1168c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
1178c2ecf20Sopenharmony_ci	struct ath_buf *bf;
1188c2ecf20Sopenharmony_ci	struct ath_vif *avp = (void *)vif->drv_priv;
1198c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1208c2ecf20Sopenharmony_ci	struct ath_txq *cabq = sc->beacon.cabq;
1218c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
1228c2ecf20Sopenharmony_ci	struct ieee80211_mgmt *mgmt_hdr;
1238c2ecf20Sopenharmony_ci	int cabq_depth;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (avp->av_bcbuf == NULL)
1268c2ecf20Sopenharmony_ci		return NULL;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	bf = avp->av_bcbuf;
1298c2ecf20Sopenharmony_ci	skb = bf->bf_mpdu;
1308c2ecf20Sopenharmony_ci	if (skb) {
1318c2ecf20Sopenharmony_ci		dma_unmap_single(sc->dev, bf->bf_buf_addr,
1328c2ecf20Sopenharmony_ci				 skb->len, DMA_TO_DEVICE);
1338c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
1348c2ecf20Sopenharmony_ci		bf->bf_buf_addr = 0;
1358c2ecf20Sopenharmony_ci		bf->bf_mpdu = NULL;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	skb = ieee80211_beacon_get(hw, vif);
1398c2ecf20Sopenharmony_ci	if (skb == NULL)
1408c2ecf20Sopenharmony_ci		return NULL;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	bf->bf_mpdu = skb;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	mgmt_hdr = (struct ieee80211_mgmt *)skb->data;
1458c2ecf20Sopenharmony_ci	mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	ath_assign_seq(common, skb);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* Always assign NOA attr when MCC enabled */
1528c2ecf20Sopenharmony_ci	if (ath9k_is_chanctx_enabled())
1538c2ecf20Sopenharmony_ci		ath9k_beacon_add_noa(sc, avp, skb);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
1568c2ecf20Sopenharmony_ci					 skb->len, DMA_TO_DEVICE);
1578c2ecf20Sopenharmony_ci	if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
1588c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
1598c2ecf20Sopenharmony_ci		bf->bf_mpdu = NULL;
1608c2ecf20Sopenharmony_ci		bf->bf_buf_addr = 0;
1618c2ecf20Sopenharmony_ci		ath_err(common, "dma_mapping_error on beaconing\n");
1628c2ecf20Sopenharmony_ci		return NULL;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	skb = ieee80211_get_buffered_bc(hw, vif);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/*
1688c2ecf20Sopenharmony_ci	 * if the CABQ traffic from previous DTIM is pending and the current
1698c2ecf20Sopenharmony_ci	 *  beacon is also a DTIM.
1708c2ecf20Sopenharmony_ci	 *  1) if there is only one vif let the cab traffic continue.
1718c2ecf20Sopenharmony_ci	 *  2) if there are more than one vif and we are using staggered
1728c2ecf20Sopenharmony_ci	 *     beacons, then drain the cabq by dropping all the frames in
1738c2ecf20Sopenharmony_ci	 *     the cabq so that the current vifs cab traffic can be scheduled.
1748c2ecf20Sopenharmony_ci	 */
1758c2ecf20Sopenharmony_ci	spin_lock_bh(&cabq->axq_lock);
1768c2ecf20Sopenharmony_ci	cabq_depth = cabq->axq_depth;
1778c2ecf20Sopenharmony_ci	spin_unlock_bh(&cabq->axq_lock);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (skb && cabq_depth) {
1808c2ecf20Sopenharmony_ci		if (sc->cur_chan->nvifs > 1) {
1818c2ecf20Sopenharmony_ci			ath_dbg(common, BEACON,
1828c2ecf20Sopenharmony_ci				"Flushing previous cabq traffic\n");
1838c2ecf20Sopenharmony_ci			ath_draintxq(sc, cabq);
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (skb)
1908c2ecf20Sopenharmony_ci		ath_tx_cabq(hw, vif, skb);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return bf;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_civoid ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
1988c2ecf20Sopenharmony_ci	struct ath_vif *avp = (void *)vif->drv_priv;
1998c2ecf20Sopenharmony_ci	int slot;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list);
2028c2ecf20Sopenharmony_ci	list_del(&avp->av_bcbuf->list);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	for (slot = 0; slot < ATH_BCBUF; slot++) {
2058c2ecf20Sopenharmony_ci		if (sc->beacon.bslot[slot] == NULL) {
2068c2ecf20Sopenharmony_ci			avp->av_bslot = slot;
2078c2ecf20Sopenharmony_ci			break;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	sc->beacon.bslot[avp->av_bslot] = vif;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
2148c2ecf20Sopenharmony_ci		avp->av_bslot);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_civoid ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2208c2ecf20Sopenharmony_ci	struct ath_vif *avp = (void *)vif->drv_priv;
2218c2ecf20Sopenharmony_ci	struct ath_buf *bf = avp->av_bcbuf;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
2248c2ecf20Sopenharmony_ci		avp->av_bslot);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	tasklet_disable(&sc->bcon_tasklet);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (bf && bf->bf_mpdu) {
2298c2ecf20Sopenharmony_ci		struct sk_buff *skb = bf->bf_mpdu;
2308c2ecf20Sopenharmony_ci		dma_unmap_single(sc->dev, bf->bf_buf_addr,
2318c2ecf20Sopenharmony_ci				 skb->len, DMA_TO_DEVICE);
2328c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
2338c2ecf20Sopenharmony_ci		bf->bf_mpdu = NULL;
2348c2ecf20Sopenharmony_ci		bf->bf_buf_addr = 0;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	avp->av_bcbuf = NULL;
2388c2ecf20Sopenharmony_ci	sc->beacon.bslot[avp->av_bslot] = NULL;
2398c2ecf20Sopenharmony_ci	list_add_tail(&bf->list, &sc->beacon.bbuf);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	tasklet_enable(&sc->bcon_tasklet);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_civoid ath9k_beacon_ensure_primary_slot(struct ath_softc *sc)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2478c2ecf20Sopenharmony_ci	struct ieee80211_vif *vif;
2488c2ecf20Sopenharmony_ci	struct ath_vif *avp;
2498c2ecf20Sopenharmony_ci	s64 tsfadjust;
2508c2ecf20Sopenharmony_ci	u32 offset;
2518c2ecf20Sopenharmony_ci	int first_slot = ATH_BCBUF;
2528c2ecf20Sopenharmony_ci	int slot;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	tasklet_disable(&sc->bcon_tasklet);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* Find first taken slot. */
2578c2ecf20Sopenharmony_ci	for (slot = 0; slot < ATH_BCBUF; slot++) {
2588c2ecf20Sopenharmony_ci		if (sc->beacon.bslot[slot]) {
2598c2ecf20Sopenharmony_ci			first_slot = slot;
2608c2ecf20Sopenharmony_ci			break;
2618c2ecf20Sopenharmony_ci		}
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci	if (first_slot == 0)
2648c2ecf20Sopenharmony_ci		goto out;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* Re-enumarate all slots, moving them forward. */
2678c2ecf20Sopenharmony_ci	for (slot = 0; slot < ATH_BCBUF; slot++) {
2688c2ecf20Sopenharmony_ci		if (slot + first_slot < ATH_BCBUF) {
2698c2ecf20Sopenharmony_ci			vif = sc->beacon.bslot[slot + first_slot];
2708c2ecf20Sopenharmony_ci			sc->beacon.bslot[slot] = vif;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci			if (vif) {
2738c2ecf20Sopenharmony_ci				avp = (void *)vif->drv_priv;
2748c2ecf20Sopenharmony_ci				avp->av_bslot = slot;
2758c2ecf20Sopenharmony_ci			}
2768c2ecf20Sopenharmony_ci		} else {
2778c2ecf20Sopenharmony_ci			sc->beacon.bslot[slot] = NULL;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	vif = sc->beacon.bslot[0];
2828c2ecf20Sopenharmony_ci	if (WARN_ON(!vif))
2838c2ecf20Sopenharmony_ci		goto out;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* Get the tsf_adjust value for the new first slot. */
2868c2ecf20Sopenharmony_ci	avp = (void *)vif->drv_priv;
2878c2ecf20Sopenharmony_ci	tsfadjust = le64_to_cpu(avp->tsf_adjust);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	ath_dbg(common, CONFIG,
2908c2ecf20Sopenharmony_ci		"Adjusting global TSF after beacon slot reassignment: %lld\n",
2918c2ecf20Sopenharmony_ci		(signed long long)tsfadjust);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* Modify TSF as required and update the HW. */
2948c2ecf20Sopenharmony_ci	avp->chanctx->tsf_val += tsfadjust;
2958c2ecf20Sopenharmony_ci	if (sc->cur_chan == avp->chanctx) {
2968c2ecf20Sopenharmony_ci		offset = ath9k_hw_get_tsf_offset(&avp->chanctx->tsf_ts, NULL);
2978c2ecf20Sopenharmony_ci		ath9k_hw_settsf64(sc->sc_ah, avp->chanctx->tsf_val + offset);
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* The slots tsf_adjust will be updated by ath9k_beacon_config later. */
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ciout:
3038c2ecf20Sopenharmony_ci	tasklet_enable(&sc->bcon_tasklet);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int ath9k_beacon_choose_slot(struct ath_softc *sc)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
3098c2ecf20Sopenharmony_ci	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
3108c2ecf20Sopenharmony_ci	u16 intval;
3118c2ecf20Sopenharmony_ci	u32 tsftu;
3128c2ecf20Sopenharmony_ci	u64 tsf;
3138c2ecf20Sopenharmony_ci	int slot;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (sc->sc_ah->opmode != NL80211_IFTYPE_AP &&
3168c2ecf20Sopenharmony_ci	    sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) {
3178c2ecf20Sopenharmony_ci		ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
3188c2ecf20Sopenharmony_ci			ath9k_hw_gettsf64(sc->sc_ah));
3198c2ecf20Sopenharmony_ci		return 0;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL;
3238c2ecf20Sopenharmony_ci	tsf = ath9k_hw_gettsf64(sc->sc_ah);
3248c2ecf20Sopenharmony_ci	tsf += TU_TO_USEC(sc->sc_ah->config.sw_beacon_response_time);
3258c2ecf20Sopenharmony_ci	tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF);
3268c2ecf20Sopenharmony_ci	slot = (tsftu % (intval * ATH_BCBUF)) / intval;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	ath_dbg(common, BEACON, "slot: %d tsf: %llu tsftu: %u\n",
3298c2ecf20Sopenharmony_ci		slot, tsf, tsftu / ATH_BCBUF);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return slot;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic void ath9k_set_tsfadjust(struct ath_softc *sc,
3358c2ecf20Sopenharmony_ci				struct ath_beacon_config *cur_conf)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
3388c2ecf20Sopenharmony_ci	s64 tsfadjust;
3398c2ecf20Sopenharmony_ci	int slot;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	for (slot = 0; slot < ATH_BCBUF; slot++) {
3428c2ecf20Sopenharmony_ci		struct ath_vif *avp;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		if (!sc->beacon.bslot[slot])
3458c2ecf20Sopenharmony_ci			continue;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		avp = (void *)sc->beacon.bslot[slot]->drv_priv;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		/* tsf_adjust is added to the TSF value. We send out the
3508c2ecf20Sopenharmony_ci		 * beacon late, so need to adjust the TSF starting point to be
3518c2ecf20Sopenharmony_ci		 * later in time (i.e. the theoretical first beacon has a TSF
3528c2ecf20Sopenharmony_ci		 * of 0 after correction).
3538c2ecf20Sopenharmony_ci		 */
3548c2ecf20Sopenharmony_ci		tsfadjust = cur_conf->beacon_interval * avp->av_bslot;
3558c2ecf20Sopenharmony_ci		tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF;
3568c2ecf20Sopenharmony_ci		avp->tsf_adjust = cpu_to_le64(tsfadjust);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n",
3598c2ecf20Sopenharmony_ci			(signed long long)tsfadjust, avp->av_bslot);
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cibool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	if (!vif || !vif->csa_active)
3668c2ecf20Sopenharmony_ci		return false;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (!ieee80211_beacon_cntdwn_is_complete(vif))
3698c2ecf20Sopenharmony_ci		return false;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	ieee80211_csa_finish(vif);
3728c2ecf20Sopenharmony_ci	return true;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic void ath9k_csa_update_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct ath_softc *sc = data;
3788c2ecf20Sopenharmony_ci	ath9k_csa_is_finished(sc, vif);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_civoid ath9k_csa_update(struct ath_softc *sc)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(sc->hw,
3848c2ecf20Sopenharmony_ci						   IEEE80211_IFACE_ITER_NORMAL,
3858c2ecf20Sopenharmony_ci						   ath9k_csa_update_vif, sc);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_civoid ath9k_beacon_tasklet(struct tasklet_struct *t)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct ath_softc *sc = from_tasklet(sc, t, bcon_tasklet);
3918c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
3928c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
3938c2ecf20Sopenharmony_ci	struct ath_buf *bf = NULL;
3948c2ecf20Sopenharmony_ci	struct ieee80211_vif *vif;
3958c2ecf20Sopenharmony_ci	bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
3968c2ecf20Sopenharmony_ci	int slot;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
3998c2ecf20Sopenharmony_ci		ath_dbg(common, RESET,
4008c2ecf20Sopenharmony_ci			"reset work is pending, skip beaconing now\n");
4018c2ecf20Sopenharmony_ci		return;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/*
4058c2ecf20Sopenharmony_ci	 * Check if the previous beacon has gone out.  If
4068c2ecf20Sopenharmony_ci	 * not don't try to post another, skip this period
4078c2ecf20Sopenharmony_ci	 * and wait for the next.  Missed beacons indicate
4088c2ecf20Sopenharmony_ci	 * a problem and should not occur.  If we miss too
4098c2ecf20Sopenharmony_ci	 * many consecutive beacons reset the device.
4108c2ecf20Sopenharmony_ci	 */
4118c2ecf20Sopenharmony_ci	if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) {
4128c2ecf20Sopenharmony_ci		sc->beacon.bmisscnt++;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		ath9k_hw_check_nav(ah);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		/*
4178c2ecf20Sopenharmony_ci		 * If the previous beacon has not been transmitted
4188c2ecf20Sopenharmony_ci		 * and a MAC/BB hang has been identified, return
4198c2ecf20Sopenharmony_ci		 * here because a chip reset would have been
4208c2ecf20Sopenharmony_ci		 * initiated.
4218c2ecf20Sopenharmony_ci		 */
4228c2ecf20Sopenharmony_ci		if (!ath_hw_check(sc))
4238c2ecf20Sopenharmony_ci			return;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) {
4268c2ecf20Sopenharmony_ci			ath_dbg(common, BSTUCK,
4278c2ecf20Sopenharmony_ci				"missed %u consecutive beacons\n",
4288c2ecf20Sopenharmony_ci				sc->beacon.bmisscnt);
4298c2ecf20Sopenharmony_ci			ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq);
4308c2ecf20Sopenharmony_ci			if (sc->beacon.bmisscnt > 3)
4318c2ecf20Sopenharmony_ci				ath9k_hw_bstuck_nfcal(ah);
4328c2ecf20Sopenharmony_ci		} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
4338c2ecf20Sopenharmony_ci			ath_dbg(common, BSTUCK, "beacon is officially stuck\n");
4348c2ecf20Sopenharmony_ci			sc->beacon.bmisscnt = 0;
4358c2ecf20Sopenharmony_ci			ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK);
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		return;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	slot = ath9k_beacon_choose_slot(sc);
4428c2ecf20Sopenharmony_ci	vif = sc->beacon.bslot[slot];
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* EDMA devices check that in the tx completion function. */
4458c2ecf20Sopenharmony_ci	if (!edma) {
4468c2ecf20Sopenharmony_ci		if (ath9k_is_chanctx_enabled()) {
4478c2ecf20Sopenharmony_ci			ath_chanctx_beacon_sent_ev(sc,
4488c2ecf20Sopenharmony_ci					  ATH_CHANCTX_EVENT_BEACON_SENT);
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		if (ath9k_csa_is_finished(sc, vif))
4528c2ecf20Sopenharmony_ci			return;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (!vif || !vif->bss_conf.enable_beacon)
4568c2ecf20Sopenharmony_ci		return;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (ath9k_is_chanctx_enabled()) {
4598c2ecf20Sopenharmony_ci		ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE);
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	bf = ath9k_beacon_generate(sc->hw, vif);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (sc->beacon.bmisscnt != 0) {
4658c2ecf20Sopenharmony_ci		ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n",
4668c2ecf20Sopenharmony_ci			sc->beacon.bmisscnt);
4678c2ecf20Sopenharmony_ci		sc->beacon.bmisscnt = 0;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/*
4718c2ecf20Sopenharmony_ci	 * Handle slot time change when a non-ERP station joins/leaves
4728c2ecf20Sopenharmony_ci	 * an 11g network.  The 802.11 layer notifies us via callback,
4738c2ecf20Sopenharmony_ci	 * we mark updateslot, then wait one beacon before effecting
4748c2ecf20Sopenharmony_ci	 * the change.  This gives associated stations at least one
4758c2ecf20Sopenharmony_ci	 * beacon interval to note the state change.
4768c2ecf20Sopenharmony_ci	 *
4778c2ecf20Sopenharmony_ci	 * NB: The slot time change state machine is clocked according
4788c2ecf20Sopenharmony_ci	 *     to whether we are bursting or staggering beacons.  We
4798c2ecf20Sopenharmony_ci	 *     recognize the request to update and record the current
4808c2ecf20Sopenharmony_ci	 *     slot then don't transition until that slot is reached
4818c2ecf20Sopenharmony_ci	 *     again.  If we miss a beacon for that slot then we'll be
4828c2ecf20Sopenharmony_ci	 *     slow to transition but we'll be sure at least one beacon
4838c2ecf20Sopenharmony_ci	 *     interval has passed.  When bursting slot is always left
4848c2ecf20Sopenharmony_ci	 *     set to ATH_BCBUF so this check is a noop.
4858c2ecf20Sopenharmony_ci	 */
4868c2ecf20Sopenharmony_ci	if (sc->beacon.updateslot == UPDATE) {
4878c2ecf20Sopenharmony_ci		sc->beacon.updateslot = COMMIT;
4888c2ecf20Sopenharmony_ci		sc->beacon.slotupdate = slot;
4898c2ecf20Sopenharmony_ci	} else if (sc->beacon.updateslot == COMMIT &&
4908c2ecf20Sopenharmony_ci		   sc->beacon.slotupdate == slot) {
4918c2ecf20Sopenharmony_ci		ah->slottime = sc->beacon.slottime;
4928c2ecf20Sopenharmony_ci		ath9k_hw_init_global_settings(ah);
4938c2ecf20Sopenharmony_ci		sc->beacon.updateslot = OK;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (bf) {
4978c2ecf20Sopenharmony_ci		ath9k_reset_beacon_status(sc);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		ath_dbg(common, BEACON,
5008c2ecf20Sopenharmony_ci			"Transmitting beacon for slot: %d\n", slot);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		/* NB: cabq traffic should already be queued and primed */
5038c2ecf20Sopenharmony_ci		ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		if (!edma)
5068c2ecf20Sopenharmony_ci			ath9k_hw_txstart(ah, sc->beacon.beaconq);
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci/*
5118c2ecf20Sopenharmony_ci * Both nexttbtt and intval have to be in usecs.
5128c2ecf20Sopenharmony_ci */
5138c2ecf20Sopenharmony_cistatic void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt,
5148c2ecf20Sopenharmony_ci			      u32 intval)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	ath9k_hw_disable_interrupts(ah);
5198c2ecf20Sopenharmony_ci	ath9k_beaconq_config(sc);
5208c2ecf20Sopenharmony_ci	ath9k_hw_beaconinit(ah, nexttbtt, intval);
5218c2ecf20Sopenharmony_ci	ah->imask |= ATH9K_INT_SWBA;
5228c2ecf20Sopenharmony_ci	sc->beacon.bmisscnt = 0;
5238c2ecf20Sopenharmony_ci	ath9k_hw_set_interrupts(ah);
5248c2ecf20Sopenharmony_ci	ath9k_hw_enable_interrupts(ah);
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic void ath9k_beacon_stop(struct ath_softc *sc)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	ath9k_hw_disable_interrupts(sc->sc_ah);
5308c2ecf20Sopenharmony_ci	sc->sc_ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
5318c2ecf20Sopenharmony_ci	sc->beacon.bmisscnt = 0;
5328c2ecf20Sopenharmony_ci	ath9k_hw_set_interrupts(sc->sc_ah);
5338c2ecf20Sopenharmony_ci	ath9k_hw_enable_interrupts(sc->sc_ah);
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci/*
5378c2ecf20Sopenharmony_ci * For multi-bss ap support beacons are either staggered evenly over N slots or
5388c2ecf20Sopenharmony_ci * burst together.  For the former arrange for the SWBA to be delivered for each
5398c2ecf20Sopenharmony_ci * slot. Slots that are not occupied will generate nothing.
5408c2ecf20Sopenharmony_ci */
5418c2ecf20Sopenharmony_cistatic void ath9k_beacon_config_ap(struct ath_softc *sc,
5428c2ecf20Sopenharmony_ci				   struct ath_beacon_config *conf)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF);
5478c2ecf20Sopenharmony_ci	ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void ath9k_beacon_config_sta(struct ath_hw *ah,
5518c2ecf20Sopenharmony_ci				    struct ath_beacon_config *conf)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct ath9k_beacon_state bs;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (ath9k_cmn_beacon_config_sta(ah, conf, &bs) == -EPERM)
5568c2ecf20Sopenharmony_ci		return;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	ath9k_hw_disable_interrupts(ah);
5598c2ecf20Sopenharmony_ci	ath9k_hw_set_sta_beacon_timers(ah, &bs);
5608c2ecf20Sopenharmony_ci	ah->imask |= ATH9K_INT_BMISS;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	ath9k_hw_set_interrupts(ah);
5638c2ecf20Sopenharmony_ci	ath9k_hw_enable_interrupts(ah);
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic void ath9k_beacon_config_adhoc(struct ath_softc *sc,
5678c2ecf20Sopenharmony_ci				      struct ath_beacon_config *conf)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
5708c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	ath9k_reset_beacon_status(sc);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	ath9k_cmn_beacon_config_adhoc(ah, conf);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/*
5798c2ecf20Sopenharmony_ci	 * Set the global 'beacon has been configured' flag for the
5808c2ecf20Sopenharmony_ci	 * joiner case in IBSS mode.
5818c2ecf20Sopenharmony_ci	 */
5828c2ecf20Sopenharmony_ci	if (!conf->ibss_creator && conf->enable_beacon)
5838c2ecf20Sopenharmony_ci		set_bit(ATH_OP_BEACONS, &common->op_flags);
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cistatic void ath9k_cache_beacon_config(struct ath_softc *sc,
5878c2ecf20Sopenharmony_ci				      struct ath_chanctx *ctx,
5888c2ecf20Sopenharmony_ci				      struct ieee80211_bss_conf *bss_conf)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
5918c2ecf20Sopenharmony_ci	struct ath_beacon_config *cur_conf = &ctx->beacon;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ath_dbg(common, BEACON,
5948c2ecf20Sopenharmony_ci		"Caching beacon data for BSS: %pM\n", bss_conf->bssid);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	cur_conf->beacon_interval = bss_conf->beacon_int;
5978c2ecf20Sopenharmony_ci	cur_conf->dtim_period = bss_conf->dtim_period;
5988c2ecf20Sopenharmony_ci	cur_conf->dtim_count = 1;
5998c2ecf20Sopenharmony_ci	cur_conf->ibss_creator = bss_conf->ibss_creator;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/*
6028c2ecf20Sopenharmony_ci	 * It looks like mac80211 may end up using beacon interval of zero in
6038c2ecf20Sopenharmony_ci	 * some cases (at least for mesh point). Avoid getting into an
6048c2ecf20Sopenharmony_ci	 * infinite loop by using a bit safer value instead. To be safe,
6058c2ecf20Sopenharmony_ci	 * do sanity check on beacon interval for all operating modes.
6068c2ecf20Sopenharmony_ci	 */
6078c2ecf20Sopenharmony_ci	if (cur_conf->beacon_interval == 0)
6088c2ecf20Sopenharmony_ci		cur_conf->beacon_interval = 100;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	cur_conf->bmiss_timeout =
6118c2ecf20Sopenharmony_ci		ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	/*
6148c2ecf20Sopenharmony_ci	 * We don't parse dtim period from mac80211 during the driver
6158c2ecf20Sopenharmony_ci	 * initialization as it breaks association with hidden-ssid
6168c2ecf20Sopenharmony_ci	 * AP and it causes latency in roaming
6178c2ecf20Sopenharmony_ci	 */
6188c2ecf20Sopenharmony_ci	if (cur_conf->dtim_period == 0)
6198c2ecf20Sopenharmony_ci		cur_conf->dtim_period = 1;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	ath9k_set_tsfadjust(sc, cur_conf);
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_civoid ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif,
6258c2ecf20Sopenharmony_ci			 bool beacons)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
6288c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
6298c2ecf20Sopenharmony_ci	struct ath_vif *avp;
6308c2ecf20Sopenharmony_ci	struct ath_chanctx *ctx;
6318c2ecf20Sopenharmony_ci	struct ath_beacon_config *cur_conf;
6328c2ecf20Sopenharmony_ci	unsigned long flags;
6338c2ecf20Sopenharmony_ci	bool enabled;
6348c2ecf20Sopenharmony_ci	bool skip_beacon = false;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (!beacons) {
6378c2ecf20Sopenharmony_ci		clear_bit(ATH_OP_BEACONS, &common->op_flags);
6388c2ecf20Sopenharmony_ci		ath9k_beacon_stop(sc);
6398c2ecf20Sopenharmony_ci		return;
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (WARN_ON(!main_vif))
6438c2ecf20Sopenharmony_ci		return;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	avp = (void *)main_vif->drv_priv;
6468c2ecf20Sopenharmony_ci	ctx = avp->chanctx;
6478c2ecf20Sopenharmony_ci	cur_conf = &ctx->beacon;
6488c2ecf20Sopenharmony_ci	enabled = cur_conf->enable_beacon;
6498c2ecf20Sopenharmony_ci	cur_conf->enable_beacon = beacons;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
6528c2ecf20Sopenharmony_ci		ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci		ath9k_set_beacon(sc);
6558c2ecf20Sopenharmony_ci		set_bit(ATH_OP_BEACONS, &common->op_flags);
6568c2ecf20Sopenharmony_ci		return;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	/* Update the beacon configuration. */
6608c2ecf20Sopenharmony_ci	ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/*
6638c2ecf20Sopenharmony_ci	 * Configure the HW beacon registers only when we have a valid
6648c2ecf20Sopenharmony_ci	 * beacon interval.
6658c2ecf20Sopenharmony_ci	 */
6668c2ecf20Sopenharmony_ci	if (cur_conf->beacon_interval) {
6678c2ecf20Sopenharmony_ci		/* Special case to sync the TSF when joining an existing IBSS.
6688c2ecf20Sopenharmony_ci		 * This is only done if no AP interface is active.
6698c2ecf20Sopenharmony_ci		 * Note that mac80211 always resets the TSF when creating a new
6708c2ecf20Sopenharmony_ci		 * IBSS interface.
6718c2ecf20Sopenharmony_ci		 */
6728c2ecf20Sopenharmony_ci		if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC &&
6738c2ecf20Sopenharmony_ci		    !enabled && beacons && !main_vif->bss_conf.ibss_creator) {
6748c2ecf20Sopenharmony_ci			spin_lock_irqsave(&sc->sc_pm_lock, flags);
6758c2ecf20Sopenharmony_ci			sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
6768c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
6778c2ecf20Sopenharmony_ci			skip_beacon = true;
6788c2ecf20Sopenharmony_ci		}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		/*
6818c2ecf20Sopenharmony_ci		 * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode
6828c2ecf20Sopenharmony_ci		 * here, it is done in ath9k_beacon_config_adhoc().
6838c2ecf20Sopenharmony_ci		 */
6848c2ecf20Sopenharmony_ci		if (beacons && !skip_beacon) {
6858c2ecf20Sopenharmony_ci			set_bit(ATH_OP_BEACONS, &common->op_flags);
6868c2ecf20Sopenharmony_ci			ath9k_set_beacon(sc);
6878c2ecf20Sopenharmony_ci		} else {
6888c2ecf20Sopenharmony_ci			clear_bit(ATH_OP_BEACONS, &common->op_flags);
6898c2ecf20Sopenharmony_ci			ath9k_beacon_stop(sc);
6908c2ecf20Sopenharmony_ci		}
6918c2ecf20Sopenharmony_ci	} else {
6928c2ecf20Sopenharmony_ci		clear_bit(ATH_OP_BEACONS, &common->op_flags);
6938c2ecf20Sopenharmony_ci		ath9k_beacon_stop(sc);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_civoid ath9k_set_beacon(struct ath_softc *sc)
6988c2ecf20Sopenharmony_ci{
6998c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
7008c2ecf20Sopenharmony_ci	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	switch (sc->sc_ah->opmode) {
7038c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
7048c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
7058c2ecf20Sopenharmony_ci		ath9k_beacon_config_ap(sc, cur_conf);
7068c2ecf20Sopenharmony_ci		break;
7078c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
7088c2ecf20Sopenharmony_ci		ath9k_beacon_config_adhoc(sc, cur_conf);
7098c2ecf20Sopenharmony_ci		break;
7108c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
7118c2ecf20Sopenharmony_ci		ath9k_beacon_config_sta(sc->sc_ah, cur_conf);
7128c2ecf20Sopenharmony_ci		break;
7138c2ecf20Sopenharmony_ci	default:
7148c2ecf20Sopenharmony_ci		ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
7158c2ecf20Sopenharmony_ci		return;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci}
718