xref: /openbmc/linux/drivers/net/wireless/ath/ath9k/beacon.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1203c4805SLuis R. Rodriguez /*
25b68138eSSujith Manoharan  * Copyright (c) 2008-2011 Atheros Communications Inc.
3203c4805SLuis R. Rodriguez  *
4203c4805SLuis R. Rodriguez  * Permission to use, copy, modify, and/or distribute this software for any
5203c4805SLuis R. Rodriguez  * purpose with or without fee is hereby granted, provided that the above
6203c4805SLuis R. Rodriguez  * copyright notice and this permission notice appear in all copies.
7203c4805SLuis R. Rodriguez  *
8203c4805SLuis R. Rodriguez  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9203c4805SLuis R. Rodriguez  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10203c4805SLuis R. Rodriguez  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11203c4805SLuis R. Rodriguez  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12203c4805SLuis R. Rodriguez  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13203c4805SLuis R. Rodriguez  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14203c4805SLuis R. Rodriguez  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15203c4805SLuis R. Rodriguez  */
16203c4805SLuis R. Rodriguez 
17b7f080cfSAlexey Dobriyan #include <linux/dma-mapping.h>
18203c4805SLuis R. Rodriguez #include "ath9k.h"
19203c4805SLuis R. Rodriguez 
20203c4805SLuis R. Rodriguez #define FUDGE 2
21203c4805SLuis R. Rodriguez 
ath9k_reset_beacon_status(struct ath_softc * sc)22ba4903f9SFelix Fietkau static void ath9k_reset_beacon_status(struct ath_softc *sc)
23ba4903f9SFelix Fietkau {
24ba4903f9SFelix Fietkau 	sc->beacon.tx_processed = false;
25ba4903f9SFelix Fietkau 	sc->beacon.tx_last = false;
26ba4903f9SFelix Fietkau }
27ba4903f9SFelix Fietkau 
28203c4805SLuis R. Rodriguez /*
29203c4805SLuis R. Rodriguez  *  This function will modify certain transmit queue properties depending on
30203c4805SLuis R. Rodriguez  *  the operating mode of the station (AP or AdHoc).  Parameters are AIFS
31203c4805SLuis R. Rodriguez  *  settings and channel width min/max
32203c4805SLuis R. Rodriguez */
ath9k_beaconq_config(struct ath_softc * sc)337e52c8aaSSujith Manoharan static void ath9k_beaconq_config(struct ath_softc *sc)
34203c4805SLuis R. Rodriguez {
35203c4805SLuis R. Rodriguez 	struct ath_hw *ah = sc->sc_ah;
36c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
3794db2936SVivek Natarajan 	struct ath9k_tx_queue_info qi, qi_be;
38066dae93SFelix Fietkau 	struct ath_txq *txq;
39203c4805SLuis R. Rodriguez 
40203c4805SLuis R. Rodriguez 	ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
417e52c8aaSSujith Manoharan 
422664d666SThomas Pedersen 	if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
432664d666SThomas Pedersen 	    sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
44203c4805SLuis R. Rodriguez 		/* Always burst out beacon and CAB traffic. */
45203c4805SLuis R. Rodriguez 		qi.tqi_aifs = 1;
46203c4805SLuis R. Rodriguez 		qi.tqi_cwmin = 0;
47203c4805SLuis R. Rodriguez 		qi.tqi_cwmax = 0;
48203c4805SLuis R. Rodriguez 	} else {
49203c4805SLuis R. Rodriguez 		/* Adhoc mode; important thing is to use 2x cwmin. */
50bea843c7SSujith Manoharan 		txq = sc->tx.txq_map[IEEE80211_AC_BE];
51066dae93SFelix Fietkau 		ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be);
5294db2936SVivek Natarajan 		qi.tqi_aifs = qi_be.tqi_aifs;
5311b0ac2eSBenjamin Berg 		if (ah->slottime == 20)
54d202caffSVivek Natarajan 			qi.tqi_cwmin = 2*qi_be.tqi_cwmin;
55d202caffSVivek Natarajan 		else
5694db2936SVivek Natarajan 			qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
5794db2936SVivek Natarajan 		qi.tqi_cwmax = qi_be.tqi_cwmax;
58203c4805SLuis R. Rodriguez 	}
59203c4805SLuis R. Rodriguez 
60203c4805SLuis R. Rodriguez 	if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
617e52c8aaSSujith Manoharan 		ath_err(common, "Unable to update h/w beacon queue parameters\n");
62203c4805SLuis R. Rodriguez 	} else {
63203c4805SLuis R. Rodriguez 		ath9k_hw_resettxqueue(ah, sc->beacon.beaconq);
64203c4805SLuis R. Rodriguez 	}
65203c4805SLuis R. Rodriguez }
66203c4805SLuis R. Rodriguez 
67203c4805SLuis R. Rodriguez /*
68203c4805SLuis R. Rodriguez  *  Associates the beacon frame buffer with a transmit descriptor.  Will set
69dd347f2fSFelix Fietkau  *  up rate codes, and channel flags. Beacons are always sent out at the
70dd347f2fSFelix Fietkau  *  lowest rate, and are not retried.
71203c4805SLuis R. Rodriguez */
ath9k_beacon_setup(struct ath_softc * sc,struct ieee80211_vif * vif,struct ath_buf * bf,int rateidx)72fb6e252fSSujith Manoharan static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
7364b84010SJouni Malinen 			     struct ath_buf *bf, int rateidx)
74203c4805SLuis R. Rodriguez {
75203c4805SLuis R. Rodriguez 	struct sk_buff *skb = bf->bf_mpdu;
76203c4805SLuis R. Rodriguez 	struct ath_hw *ah = sc->sc_ah;
7743c27613SLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
78493cf04fSFelix Fietkau 	struct ath_tx_info info;
79545750d3SFelix Fietkau 	struct ieee80211_supported_band *sband;
80493cf04fSFelix Fietkau 	u8 chainmask = ah->txchainmask;
818b537686SLorenzo Bianconi 	u8 i, rate = 0;
82203c4805SLuis R. Rodriguez 
83bff11766SFelix Fietkau 	sband = &common->sbands[sc->cur_chandef.chan->band];
8464b84010SJouni Malinen 	rate = sband->bitrates[rateidx].hw_value;
85d47a61aaSSujith Manoharan 	if (vif->bss_conf.use_short_preamble)
8664b84010SJouni Malinen 		rate |= sband->bitrates[rateidx].hw_value_short;
87203c4805SLuis R. Rodriguez 
88493cf04fSFelix Fietkau 	memset(&info, 0, sizeof(info));
89493cf04fSFelix Fietkau 	info.pkt_len = skb->len + FCS_LEN;
90493cf04fSFelix Fietkau 	info.type = ATH9K_PKT_TYPE_BEACON;
918b537686SLorenzo Bianconi 	for (i = 0; i < 4; i++)
928b537686SLorenzo Bianconi 		info.txpower[i] = MAX_RATE_POWER;
93493cf04fSFelix Fietkau 	info.keyix = ATH9K_TXKEYIX_INVALID;
94493cf04fSFelix Fietkau 	info.keytype = ATH9K_KEY_TYPE_CLEAR;
95cd484aebSRajkumar Manoharan 	info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK;
96203c4805SLuis R. Rodriguez 
97493cf04fSFelix Fietkau 	info.buf_addr[0] = bf->bf_buf_addr;
98493cf04fSFelix Fietkau 	info.buf_len[0] = roundup(skb->len, 4);
99203c4805SLuis R. Rodriguez 
100493cf04fSFelix Fietkau 	info.is_first = true;
101493cf04fSFelix Fietkau 	info.is_last = true;
102493cf04fSFelix Fietkau 
103493cf04fSFelix Fietkau 	info.qcu = sc->beacon.beaconq;
104493cf04fSFelix Fietkau 
105493cf04fSFelix Fietkau 	info.rates[0].Tries = 1;
106493cf04fSFelix Fietkau 	info.rates[0].Rate = rate;
107493cf04fSFelix Fietkau 	info.rates[0].ChSel = ath_txchainmask_reduction(sc, chainmask, rate);
108493cf04fSFelix Fietkau 
109493cf04fSFelix Fietkau 	ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
110203c4805SLuis R. Rodriguez }
111203c4805SLuis R. Rodriguez 
ath9k_beacon_generate(struct ieee80211_hw * hw,struct ieee80211_vif * vif)112fb6e252fSSujith Manoharan static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
113203c4805SLuis R. Rodriguez 					     struct ieee80211_vif *vif)
114203c4805SLuis R. Rodriguez {
1159ac58615SFelix Fietkau 	struct ath_softc *sc = hw->priv;
116c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
117203c4805SLuis R. Rodriguez 	struct ath_buf *bf;
118aa45fe96SSujith Manoharan 	struct ath_vif *avp = (void *)vif->drv_priv;
119203c4805SLuis R. Rodriguez 	struct sk_buff *skb;
120aa45fe96SSujith Manoharan 	struct ath_txq *cabq = sc->beacon.cabq;
121203c4805SLuis R. Rodriguez 	struct ieee80211_tx_info *info;
122fb6e252fSSujith Manoharan 	struct ieee80211_mgmt *mgmt_hdr;
123203c4805SLuis R. Rodriguez 	int cabq_depth;
124203c4805SLuis R. Rodriguez 
125aa45fe96SSujith Manoharan 	if (avp->av_bcbuf == NULL)
126203c4805SLuis R. Rodriguez 		return NULL;
127203c4805SLuis R. Rodriguez 
128203c4805SLuis R. Rodriguez 	bf = avp->av_bcbuf;
129203c4805SLuis R. Rodriguez 	skb = bf->bf_mpdu;
130203c4805SLuis R. Rodriguez 	if (skb) {
131c1739eb3SBen Greear 		dma_unmap_single(sc->dev, bf->bf_buf_addr,
132203c4805SLuis R. Rodriguez 				 skb->len, DMA_TO_DEVICE);
133203c4805SLuis R. Rodriguez 		dev_kfree_skb_any(skb);
1346cf9e995SBen Greear 		bf->bf_buf_addr = 0;
1351adb2e2bSFelix Fietkau 		bf->bf_mpdu = NULL;
136203c4805SLuis R. Rodriguez 	}
137203c4805SLuis R. Rodriguez 
138*6e8912a5SShaul Triebitz 	skb = ieee80211_beacon_get(hw, vif, 0);
139203c4805SLuis R. Rodriguez 	if (skb == NULL)
140203c4805SLuis R. Rodriguez 		return NULL;
141fb6e252fSSujith Manoharan 
142fb6e252fSSujith Manoharan 	bf->bf_mpdu = skb;
143fb6e252fSSujith Manoharan 
144fb6e252fSSujith Manoharan 	mgmt_hdr = (struct ieee80211_mgmt *)skb->data;
145fb6e252fSSujith Manoharan 	mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust;
146203c4805SLuis R. Rodriguez 
147203c4805SLuis R. Rodriguez 	info = IEEE80211_SKB_CB(skb);
148ca14405eSSujith Manoharan 
149ca14405eSSujith Manoharan 	ath_assign_seq(common, skb);
150203c4805SLuis R. Rodriguez 
151a64d876eSJanusz Dziedzic 	/* Always assign NOA attr when MCC enabled */
152a64d876eSJanusz Dziedzic 	if (ath9k_is_chanctx_enabled())
1533ae07d39SFelix Fietkau 		ath9k_beacon_add_noa(sc, avp, skb);
1543ae07d39SFelix Fietkau 
155c1739eb3SBen Greear 	bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
156203c4805SLuis R. Rodriguez 					 skb->len, DMA_TO_DEVICE);
157203c4805SLuis R. Rodriguez 	if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
158203c4805SLuis R. Rodriguez 		dev_kfree_skb_any(skb);
159203c4805SLuis R. Rodriguez 		bf->bf_mpdu = NULL;
1606cf9e995SBen Greear 		bf->bf_buf_addr = 0;
1613800276aSJoe Perches 		ath_err(common, "dma_mapping_error on beaconing\n");
162203c4805SLuis R. Rodriguez 		return NULL;
163203c4805SLuis R. Rodriguez 	}
164203c4805SLuis R. Rodriguez 
165203c4805SLuis R. Rodriguez 	skb = ieee80211_get_buffered_bc(hw, vif);
166203c4805SLuis R. Rodriguez 
167203c4805SLuis R. Rodriguez 	/*
168203c4805SLuis R. Rodriguez 	 * if the CABQ traffic from previous DTIM is pending and the current
169203c4805SLuis R. Rodriguez 	 *  beacon is also a DTIM.
170203c4805SLuis R. Rodriguez 	 *  1) if there is only one vif let the cab traffic continue.
171203c4805SLuis R. Rodriguez 	 *  2) if there are more than one vif and we are using staggered
172203c4805SLuis R. Rodriguez 	 *     beacons, then drain the cabq by dropping all the frames in
173203c4805SLuis R. Rodriguez 	 *     the cabq so that the current vifs cab traffic can be scheduled.
174203c4805SLuis R. Rodriguez 	 */
175203c4805SLuis R. Rodriguez 	spin_lock_bh(&cabq->axq_lock);
176203c4805SLuis R. Rodriguez 	cabq_depth = cabq->axq_depth;
177203c4805SLuis R. Rodriguez 	spin_unlock_bh(&cabq->axq_lock);
178203c4805SLuis R. Rodriguez 
179203c4805SLuis R. Rodriguez 	if (skb && cabq_depth) {
180ca529c93SSujith Manoharan 		if (sc->cur_chan->nvifs > 1) {
181d2182b69SJoe Perches 			ath_dbg(common, BEACON,
182203c4805SLuis R. Rodriguez 				"Flushing previous cabq traffic\n");
1831381559bSFelix Fietkau 			ath_draintxq(sc, cabq);
184203c4805SLuis R. Rodriguez 		}
185203c4805SLuis R. Rodriguez 	}
186203c4805SLuis R. Rodriguez 
187fb6e252fSSujith Manoharan 	ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
188203c4805SLuis R. Rodriguez 
18959505c02SFelix Fietkau 	if (skb)
19059505c02SFelix Fietkau 		ath_tx_cabq(hw, vif, skb);
191203c4805SLuis R. Rodriguez 
192203c4805SLuis R. Rodriguez 	return bf;
193203c4805SLuis R. Rodriguez }
194203c4805SLuis R. Rodriguez 
ath9k_beacon_assign_slot(struct ath_softc * sc,struct ieee80211_vif * vif)195130ef6e9SSujith Manoharan void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
196203c4805SLuis R. Rodriguez {
197c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
198130ef6e9SSujith Manoharan 	struct ath_vif *avp = (void *)vif->drv_priv;
199130ef6e9SSujith Manoharan 	int slot;
200203c4805SLuis R. Rodriguez 
201130ef6e9SSujith Manoharan 	avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list);
202203c4805SLuis R. Rodriguez 	list_del(&avp->av_bcbuf->list);
203203c4805SLuis R. Rodriguez 
204130ef6e9SSujith Manoharan 	for (slot = 0; slot < ATH_BCBUF; slot++) {
205203c4805SLuis R. Rodriguez 		if (sc->beacon.bslot[slot] == NULL) {
206203c4805SLuis R. Rodriguez 			avp->av_bslot = slot;
207774610e4SFelix Fietkau 			break;
208203c4805SLuis R. Rodriguez 		}
209130ef6e9SSujith Manoharan 	}
210130ef6e9SSujith Manoharan 
211203c4805SLuis R. Rodriguez 	sc->beacon.bslot[avp->av_bslot] = vif;
212130ef6e9SSujith Manoharan 
213130ef6e9SSujith Manoharan 	ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
214130ef6e9SSujith Manoharan 		avp->av_bslot);
215203c4805SLuis R. Rodriguez }
216203c4805SLuis R. Rodriguez 
ath9k_beacon_remove_slot(struct ath_softc * sc,struct ieee80211_vif * vif)217130ef6e9SSujith Manoharan void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
218203c4805SLuis R. Rodriguez {
219130ef6e9SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
220130ef6e9SSujith Manoharan 	struct ath_vif *avp = (void *)vif->drv_priv;
221130ef6e9SSujith Manoharan 	struct ath_buf *bf = avp->av_bcbuf;
222203c4805SLuis R. Rodriguez 
223130ef6e9SSujith Manoharan 	ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
224130ef6e9SSujith Manoharan 		avp->av_bslot);
225203c4805SLuis R. Rodriguez 
226130ef6e9SSujith Manoharan 	tasklet_disable(&sc->bcon_tasklet);
227130ef6e9SSujith Manoharan 
228130ef6e9SSujith Manoharan 	if (bf && bf->bf_mpdu) {
229203c4805SLuis R. Rodriguez 		struct sk_buff *skb = bf->bf_mpdu;
230c1739eb3SBen Greear 		dma_unmap_single(sc->dev, bf->bf_buf_addr,
231203c4805SLuis R. Rodriguez 				 skb->len, DMA_TO_DEVICE);
232203c4805SLuis R. Rodriguez 		dev_kfree_skb_any(skb);
233203c4805SLuis R. Rodriguez 		bf->bf_mpdu = NULL;
2346cf9e995SBen Greear 		bf->bf_buf_addr = 0;
235203c4805SLuis R. Rodriguez 	}
236203c4805SLuis R. Rodriguez 
237203c4805SLuis R. Rodriguez 	avp->av_bcbuf = NULL;
238130ef6e9SSujith Manoharan 	sc->beacon.bslot[avp->av_bslot] = NULL;
239130ef6e9SSujith Manoharan 	list_add_tail(&bf->list, &sc->beacon.bbuf);
240130ef6e9SSujith Manoharan 
241130ef6e9SSujith Manoharan 	tasklet_enable(&sc->bcon_tasklet);
242203c4805SLuis R. Rodriguez }
243203c4805SLuis R. Rodriguez 
ath9k_beacon_ensure_primary_slot(struct ath_softc * sc)244cfda2d8eSBenjamin Berg void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc)
245cfda2d8eSBenjamin Berg {
246cfda2d8eSBenjamin Berg 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
247cfda2d8eSBenjamin Berg 	struct ieee80211_vif *vif;
248cfda2d8eSBenjamin Berg 	struct ath_vif *avp;
249cfda2d8eSBenjamin Berg 	s64 tsfadjust;
250cfda2d8eSBenjamin Berg 	u32 offset;
251cfda2d8eSBenjamin Berg 	int first_slot = ATH_BCBUF;
252cfda2d8eSBenjamin Berg 	int slot;
253cfda2d8eSBenjamin Berg 
2543250aa8aSSebastian Andrzej Siewior 	tasklet_disable_in_atomic(&sc->bcon_tasklet);
255cfda2d8eSBenjamin Berg 
256cfda2d8eSBenjamin Berg 	/* Find first taken slot. */
257cfda2d8eSBenjamin Berg 	for (slot = 0; slot < ATH_BCBUF; slot++) {
258cfda2d8eSBenjamin Berg 		if (sc->beacon.bslot[slot]) {
259cfda2d8eSBenjamin Berg 			first_slot = slot;
260cfda2d8eSBenjamin Berg 			break;
261cfda2d8eSBenjamin Berg 		}
262cfda2d8eSBenjamin Berg 	}
263cfda2d8eSBenjamin Berg 	if (first_slot == 0)
264cfda2d8eSBenjamin Berg 		goto out;
265cfda2d8eSBenjamin Berg 
266cfda2d8eSBenjamin Berg 	/* Re-enumarate all slots, moving them forward. */
267cfda2d8eSBenjamin Berg 	for (slot = 0; slot < ATH_BCBUF; slot++) {
268cfda2d8eSBenjamin Berg 		if (slot + first_slot < ATH_BCBUF) {
269cfda2d8eSBenjamin Berg 			vif = sc->beacon.bslot[slot + first_slot];
270cfda2d8eSBenjamin Berg 			sc->beacon.bslot[slot] = vif;
271cfda2d8eSBenjamin Berg 
272cfda2d8eSBenjamin Berg 			if (vif) {
273cfda2d8eSBenjamin Berg 				avp = (void *)vif->drv_priv;
274cfda2d8eSBenjamin Berg 				avp->av_bslot = slot;
275cfda2d8eSBenjamin Berg 			}
276cfda2d8eSBenjamin Berg 		} else {
277cfda2d8eSBenjamin Berg 			sc->beacon.bslot[slot] = NULL;
278cfda2d8eSBenjamin Berg 		}
279cfda2d8eSBenjamin Berg 	}
280cfda2d8eSBenjamin Berg 
281cfda2d8eSBenjamin Berg 	vif = sc->beacon.bslot[0];
282cfda2d8eSBenjamin Berg 	if (WARN_ON(!vif))
283cfda2d8eSBenjamin Berg 		goto out;
284cfda2d8eSBenjamin Berg 
285cfda2d8eSBenjamin Berg 	/* Get the tsf_adjust value for the new first slot. */
286cfda2d8eSBenjamin Berg 	avp = (void *)vif->drv_priv;
287cfda2d8eSBenjamin Berg 	tsfadjust = le64_to_cpu(avp->tsf_adjust);
288cfda2d8eSBenjamin Berg 
289cfda2d8eSBenjamin Berg 	ath_dbg(common, CONFIG,
290cfda2d8eSBenjamin Berg 		"Adjusting global TSF after beacon slot reassignment: %lld\n",
291cfda2d8eSBenjamin Berg 		(signed long long)tsfadjust);
292cfda2d8eSBenjamin Berg 
293cfda2d8eSBenjamin Berg 	/* Modify TSF as required and update the HW. */
294cfda2d8eSBenjamin Berg 	avp->chanctx->tsf_val += tsfadjust;
295cfda2d8eSBenjamin Berg 	if (sc->cur_chan == avp->chanctx) {
296cfda2d8eSBenjamin Berg 		offset = ath9k_hw_get_tsf_offset(&avp->chanctx->tsf_ts, NULL);
297cfda2d8eSBenjamin Berg 		ath9k_hw_settsf64(sc->sc_ah, avp->chanctx->tsf_val + offset);
298cfda2d8eSBenjamin Berg 	}
299cfda2d8eSBenjamin Berg 
300cfda2d8eSBenjamin Berg 	/* The slots tsf_adjust will be updated by ath9k_beacon_config later. */
301cfda2d8eSBenjamin Berg 
302cfda2d8eSBenjamin Berg out:
303cfda2d8eSBenjamin Berg 	tasklet_enable(&sc->bcon_tasklet);
304cfda2d8eSBenjamin Berg }
305cfda2d8eSBenjamin Berg 
ath9k_beacon_choose_slot(struct ath_softc * sc)306fb6e252fSSujith Manoharan static int ath9k_beacon_choose_slot(struct ath_softc *sc)
307fb6e252fSSujith Manoharan {
308fb6e252fSSujith Manoharan 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
309ca900ac9SRajkumar Manoharan 	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
310fb6e252fSSujith Manoharan 	u16 intval;
311fb6e252fSSujith Manoharan 	u32 tsftu;
312fb6e252fSSujith Manoharan 	u64 tsf;
313fb6e252fSSujith Manoharan 	int slot;
314fb6e252fSSujith Manoharan 
3152664d666SThomas Pedersen 	if (sc->sc_ah->opmode != NL80211_IFTYPE_AP &&
3162664d666SThomas Pedersen 	    sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) {
317fb6e252fSSujith Manoharan 		ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
318fb6e252fSSujith Manoharan 			ath9k_hw_gettsf64(sc->sc_ah));
319fb6e252fSSujith Manoharan 		return 0;
320fb6e252fSSujith Manoharan 	}
321fb6e252fSSujith Manoharan 
322fb6e252fSSujith Manoharan 	intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL;
323fb6e252fSSujith Manoharan 	tsf = ath9k_hw_gettsf64(sc->sc_ah);
324fb6e252fSSujith Manoharan 	tsf += TU_TO_USEC(sc->sc_ah->config.sw_beacon_response_time);
325fb6e252fSSujith Manoharan 	tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF);
326fb6e252fSSujith Manoharan 	slot = (tsftu % (intval * ATH_BCBUF)) / intval;
327fb6e252fSSujith Manoharan 
328fb6e252fSSujith Manoharan 	ath_dbg(common, BEACON, "slot: %d tsf: %llu tsftu: %u\n",
329fb6e252fSSujith Manoharan 		slot, tsf, tsftu / ATH_BCBUF);
330fb6e252fSSujith Manoharan 
331fb6e252fSSujith Manoharan 	return slot;
332fb6e252fSSujith Manoharan }
333fb6e252fSSujith Manoharan 
ath9k_set_tsfadjust(struct ath_softc * sc,struct ath_beacon_config * cur_conf)334cfda2d8eSBenjamin Berg static void ath9k_set_tsfadjust(struct ath_softc *sc,
335cfda2d8eSBenjamin Berg 				struct ath_beacon_config *cur_conf)
3362f8e82e8SSujith Manoharan {
3372f8e82e8SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
338fa5106e1SBenjamin Berg 	s64 tsfadjust;
339cfda2d8eSBenjamin Berg 	int slot;
3402f8e82e8SSujith Manoharan 
341cfda2d8eSBenjamin Berg 	for (slot = 0; slot < ATH_BCBUF; slot++) {
342cfda2d8eSBenjamin Berg 		struct ath_vif *avp;
3432f8e82e8SSujith Manoharan 
344cfda2d8eSBenjamin Berg 		if (!sc->beacon.bslot[slot])
345cfda2d8eSBenjamin Berg 			continue;
346cfda2d8eSBenjamin Berg 
347cfda2d8eSBenjamin Berg 		avp = (void *)sc->beacon.bslot[slot]->drv_priv;
348cfda2d8eSBenjamin Berg 
349cfda2d8eSBenjamin Berg 		/* tsf_adjust is added to the TSF value. We send out the
350cfda2d8eSBenjamin Berg 		 * beacon late, so need to adjust the TSF starting point to be
351cfda2d8eSBenjamin Berg 		 * later in time (i.e. the theoretical first beacon has a TSF
352cfda2d8eSBenjamin Berg 		 * of 0 after correction).
353fa5106e1SBenjamin Berg 		 */
35463ded3f0SFelix Fietkau 		tsfadjust = cur_conf->beacon_interval * avp->av_bslot;
355fa5106e1SBenjamin Berg 		tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF;
35663ded3f0SFelix Fietkau 		avp->tsf_adjust = cpu_to_le64(tsfadjust);
3572f8e82e8SSujith Manoharan 
358fa5106e1SBenjamin Berg 		ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n",
359fa5106e1SBenjamin Berg 			(signed long long)tsfadjust, avp->av_bslot);
3602f8e82e8SSujith Manoharan 	}
361cfda2d8eSBenjamin Berg }
3622f8e82e8SSujith Manoharan 
ath9k_csa_is_finished(struct ath_softc * sc,struct ieee80211_vif * vif)3634effc6fdSMichal Kazior bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif)
364d074e8d5SSimon Wunderlich {
365d0a9123eSJohannes Berg 	if (!vif || !vif->bss_conf.csa_active)
366d074e8d5SSimon Wunderlich 		return false;
367d074e8d5SSimon Wunderlich 
3688552a434SJohn Crispin 	if (!ieee80211_beacon_cntdwn_is_complete(vif))
369d074e8d5SSimon Wunderlich 		return false;
370d074e8d5SSimon Wunderlich 
371d074e8d5SSimon Wunderlich 	ieee80211_csa_finish(vif);
372d074e8d5SSimon Wunderlich 	return true;
373d074e8d5SSimon Wunderlich }
374d074e8d5SSimon Wunderlich 
ath9k_csa_update_vif(void * data,u8 * mac,struct ieee80211_vif * vif)3754effc6fdSMichal Kazior static void ath9k_csa_update_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
3764effc6fdSMichal Kazior {
3774effc6fdSMichal Kazior 	struct ath_softc *sc = data;
3784effc6fdSMichal Kazior 	ath9k_csa_is_finished(sc, vif);
3794effc6fdSMichal Kazior }
3804effc6fdSMichal Kazior 
ath9k_csa_update(struct ath_softc * sc)3814effc6fdSMichal Kazior void ath9k_csa_update(struct ath_softc *sc)
3824effc6fdSMichal Kazior {
3835869e795SFelix Fietkau 	ieee80211_iterate_active_interfaces_atomic(sc->hw,
3844effc6fdSMichal Kazior 						   IEEE80211_IFACE_ITER_NORMAL,
3855869e795SFelix Fietkau 						   ath9k_csa_update_vif, sc);
3864effc6fdSMichal Kazior }
3874effc6fdSMichal Kazior 
ath9k_beacon_tasklet(struct tasklet_struct * t)388f91a35b4SAllen Pais void ath9k_beacon_tasklet(struct tasklet_struct *t)
389203c4805SLuis R. Rodriguez {
390f91a35b4SAllen Pais 	struct ath_softc *sc = from_tasklet(sc, t, bcon_tasklet);
391203c4805SLuis R. Rodriguez 	struct ath_hw *ah = sc->sc_ah;
392c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
393203c4805SLuis R. Rodriguez 	struct ath_buf *bf = NULL;
394203c4805SLuis R. Rodriguez 	struct ieee80211_vif *vif;
39598f0a5ebSMohammed Shafi Shajakhan 	bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
396203c4805SLuis R. Rodriguez 	int slot;
397203c4805SLuis R. Rodriguez 
398eefa01ddSOleksij Rempel 	if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
3994e7fb718SRajkumar Manoharan 		ath_dbg(common, RESET,
4004e7fb718SRajkumar Manoharan 			"reset work is pending, skip beaconing now\n");
4014e7fb718SRajkumar Manoharan 		return;
4024e7fb718SRajkumar Manoharan 	}
403124b979bSRajkumar Manoharan 
404203c4805SLuis R. Rodriguez 	/*
405203c4805SLuis R. Rodriguez 	 * Check if the previous beacon has gone out.  If
406203c4805SLuis R. Rodriguez 	 * not don't try to post another, skip this period
407203c4805SLuis R. Rodriguez 	 * and wait for the next.  Missed beacons indicate
408203c4805SLuis R. Rodriguez 	 * a problem and should not occur.  If we miss too
409203c4805SLuis R. Rodriguez 	 * many consecutive beacons reset the device.
410203c4805SLuis R. Rodriguez 	 */
411203c4805SLuis R. Rodriguez 	if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) {
412203c4805SLuis R. Rodriguez 		sc->beacon.bmisscnt++;
413203c4805SLuis R. Rodriguez 
4141e516ca7SSujith Manoharan 		ath9k_hw_check_nav(ah);
4151e516ca7SSujith Manoharan 
416415ec61bSSujith Manoharan 		/*
417415ec61bSSujith Manoharan 		 * If the previous beacon has not been transmitted
418415ec61bSSujith Manoharan 		 * and a MAC/BB hang has been identified, return
419415ec61bSSujith Manoharan 		 * here because a chip reset would have been
420415ec61bSSujith Manoharan 		 * initiated.
421415ec61bSSujith Manoharan 		 */
422415ec61bSSujith Manoharan 		if (!ath_hw_check(sc))
423415ec61bSSujith Manoharan 			return;
424b381fa32SFelix Fietkau 
425c944daf4SFelix Fietkau 		if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) {
426d2182b69SJoe Perches 			ath_dbg(common, BSTUCK,
427203c4805SLuis R. Rodriguez 				"missed %u consecutive beacons\n",
428203c4805SLuis R. Rodriguez 				sc->beacon.bmisscnt);
429efff395eSFelix Fietkau 			ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq);
43087c510feSFelix Fietkau 			if (sc->beacon.bmisscnt > 3)
43170cf1533SFelix Fietkau 				ath9k_hw_bstuck_nfcal(ah);
432203c4805SLuis R. Rodriguez 		} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
433d2182b69SJoe Perches 			ath_dbg(common, BSTUCK, "beacon is officially stuck\n");
4344e7fb718SRajkumar Manoharan 			sc->beacon.bmisscnt = 0;
435124b979bSRajkumar Manoharan 			ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK);
436203c4805SLuis R. Rodriguez 		}
437203c4805SLuis R. Rodriguez 
438203c4805SLuis R. Rodriguez 		return;
439203c4805SLuis R. Rodriguez 	}
440203c4805SLuis R. Rodriguez 
441fb6e252fSSujith Manoharan 	slot = ath9k_beacon_choose_slot(sc);
442203c4805SLuis R. Rodriguez 	vif = sc->beacon.bslot[slot];
443203c4805SLuis R. Rodriguez 
4444effc6fdSMichal Kazior 	/* EDMA devices check that in the tx completion function. */
445748299f2SFelix Fietkau 	if (!edma) {
44627babf9fSSujith Manoharan 		if (ath9k_is_chanctx_enabled()) {
44770b06dacSSujith Manoharan 			ath_chanctx_beacon_sent_ev(sc,
448748299f2SFelix Fietkau 					  ATH_CHANCTX_EVENT_BEACON_SENT);
44927babf9fSSujith Manoharan 		}
450748299f2SFelix Fietkau 
451748299f2SFelix Fietkau 		if (ath9k_csa_is_finished(sc, vif))
4524effc6fdSMichal Kazior 			return;
453748299f2SFelix Fietkau 	}
4544effc6fdSMichal Kazior 
455fb6e252fSSujith Manoharan 	if (!vif || !vif->bss_conf.enable_beacon)
456fb6e252fSSujith Manoharan 		return;
457c6820f1eSFelix Fietkau 
45827babf9fSSujith Manoharan 	if (ath9k_is_chanctx_enabled()) {
459748299f2SFelix Fietkau 		ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE);
46027babf9fSSujith Manoharan 	}
46127babf9fSSujith Manoharan 
462fb6e252fSSujith Manoharan 	bf = ath9k_beacon_generate(sc->hw, vif);
463c944daf4SFelix Fietkau 
464c944daf4SFelix Fietkau 	if (sc->beacon.bmisscnt != 0) {
465fb6e252fSSujith Manoharan 		ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n",
466c944daf4SFelix Fietkau 			sc->beacon.bmisscnt);
467c944daf4SFelix Fietkau 		sc->beacon.bmisscnt = 0;
468c944daf4SFelix Fietkau 	}
469203c4805SLuis R. Rodriguez 
470203c4805SLuis R. Rodriguez 	/*
471203c4805SLuis R. Rodriguez 	 * Handle slot time change when a non-ERP station joins/leaves
472203c4805SLuis R. Rodriguez 	 * an 11g network.  The 802.11 layer notifies us via callback,
473203c4805SLuis R. Rodriguez 	 * we mark updateslot, then wait one beacon before effecting
474203c4805SLuis R. Rodriguez 	 * the change.  This gives associated stations at least one
475203c4805SLuis R. Rodriguez 	 * beacon interval to note the state change.
476203c4805SLuis R. Rodriguez 	 *
477203c4805SLuis R. Rodriguez 	 * NB: The slot time change state machine is clocked according
478203c4805SLuis R. Rodriguez 	 *     to whether we are bursting or staggering beacons.  We
479203c4805SLuis R. Rodriguez 	 *     recognize the request to update and record the current
480203c4805SLuis R. Rodriguez 	 *     slot then don't transition until that slot is reached
481203c4805SLuis R. Rodriguez 	 *     again.  If we miss a beacon for that slot then we'll be
482203c4805SLuis R. Rodriguez 	 *     slow to transition but we'll be sure at least one beacon
483203c4805SLuis R. Rodriguez 	 *     interval has passed.  When bursting slot is always left
484203c4805SLuis R. Rodriguez 	 *     set to ATH_BCBUF so this check is a noop.
485203c4805SLuis R. Rodriguez 	 */
486203c4805SLuis R. Rodriguez 	if (sc->beacon.updateslot == UPDATE) {
487fb6e252fSSujith Manoharan 		sc->beacon.updateslot = COMMIT;
488203c4805SLuis R. Rodriguez 		sc->beacon.slotupdate = slot;
489fb6e252fSSujith Manoharan 	} else if (sc->beacon.updateslot == COMMIT &&
490fb6e252fSSujith Manoharan 		   sc->beacon.slotupdate == slot) {
4910005baf4SFelix Fietkau 		ah->slottime = sc->beacon.slottime;
4920005baf4SFelix Fietkau 		ath9k_hw_init_global_settings(ah);
493203c4805SLuis R. Rodriguez 		sc->beacon.updateslot = OK;
494203c4805SLuis R. Rodriguez 	}
495fb6e252fSSujith Manoharan 
496fb6e252fSSujith Manoharan 	if (bf) {
497fb6e252fSSujith Manoharan 		ath9k_reset_beacon_status(sc);
498fb6e252fSSujith Manoharan 
499fb6e252fSSujith Manoharan 		ath_dbg(common, BEACON,
500fb6e252fSSujith Manoharan 			"Transmitting beacon for slot: %d\n", slot);
501fb6e252fSSujith Manoharan 
502203c4805SLuis R. Rodriguez 		/* NB: cabq traffic should already be queued and primed */
503fb6e252fSSujith Manoharan 		ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
50498f0a5ebSMohammed Shafi Shajakhan 
50598f0a5ebSMohammed Shafi Shajakhan 		if (!edma)
506203c4805SLuis R. Rodriguez 			ath9k_hw_txstart(ah, sc->beacon.beaconq);
507203c4805SLuis R. Rodriguez 	}
508203c4805SLuis R. Rodriguez }
509203c4805SLuis R. Rodriguez 
5101a6404a1SSujith Manoharan /*
5111a6404a1SSujith Manoharan  * Both nexttbtt and intval have to be in usecs.
5121a6404a1SSujith Manoharan  */
ath9k_beacon_init(struct ath_softc * sc,u32 nexttbtt,u32 intval)5131a6404a1SSujith Manoharan static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt,
514cfda2d8eSBenjamin Berg 			      u32 intval)
51521526d57SLuis R. Rodriguez {
516ef4ad633SSujith Manoharan 	struct ath_hw *ah = sc->sc_ah;
51721526d57SLuis R. Rodriguez 
518ef4ad633SSujith Manoharan 	ath9k_hw_disable_interrupts(ah);
5197e52c8aaSSujith Manoharan 	ath9k_beaconq_config(sc);
520ef4ad633SSujith Manoharan 	ath9k_hw_beaconinit(ah, nexttbtt, intval);
521cfda2d8eSBenjamin Berg 	ah->imask |= ATH9K_INT_SWBA;
522ef4ad633SSujith Manoharan 	sc->beacon.bmisscnt = 0;
523ef4ad633SSujith Manoharan 	ath9k_hw_set_interrupts(ah);
524ef4ad633SSujith Manoharan 	ath9k_hw_enable_interrupts(ah);
52521526d57SLuis R. Rodriguez }
52621526d57SLuis R. Rodriguez 
ath9k_beacon_stop(struct ath_softc * sc)527cfda2d8eSBenjamin Berg static void ath9k_beacon_stop(struct ath_softc *sc)
528cfda2d8eSBenjamin Berg {
529cfda2d8eSBenjamin Berg 	ath9k_hw_disable_interrupts(sc->sc_ah);
530cfda2d8eSBenjamin Berg 	sc->sc_ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
531cfda2d8eSBenjamin Berg 	sc->beacon.bmisscnt = 0;
532cfda2d8eSBenjamin Berg 	ath9k_hw_set_interrupts(sc->sc_ah);
533cfda2d8eSBenjamin Berg 	ath9k_hw_enable_interrupts(sc->sc_ah);
534cfda2d8eSBenjamin Berg }
535cfda2d8eSBenjamin Berg 
536203c4805SLuis R. Rodriguez /*
537203c4805SLuis R. Rodriguez  * For multi-bss ap support beacons are either staggered evenly over N slots or
538203c4805SLuis R. Rodriguez  * burst together.  For the former arrange for the SWBA to be delivered for each
539203c4805SLuis R. Rodriguez  * slot. Slots that are not occupied will generate nothing.
540203c4805SLuis R. Rodriguez  */
ath9k_beacon_config_ap(struct ath_softc * sc,struct ath_beacon_config * conf)541ef4ad633SSujith Manoharan static void ath9k_beacon_config_ap(struct ath_softc *sc,
542d31e20afSVasanthakumar Thiagarajan 				   struct ath_beacon_config *conf)
543203c4805SLuis R. Rodriguez {
5443069168cSPavel Roskin 	struct ath_hw *ah = sc->sc_ah;
545203c4805SLuis R. Rodriguez 
546fa7b52faSOleksij Rempel 	ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF);
547cfda2d8eSBenjamin Berg 	ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
548203c4805SLuis R. Rodriguez }
549203c4805SLuis R. Rodriguez 
ath9k_beacon_config_sta(struct ath_hw * ah,struct ath_beacon_config * conf)550cbbdf2aeSOleksij Rempel static void ath9k_beacon_config_sta(struct ath_hw *ah,
551d31e20afSVasanthakumar Thiagarajan 				    struct ath_beacon_config *conf)
552203c4805SLuis R. Rodriguez {
553203c4805SLuis R. Rodriguez 	struct ath9k_beacon_state bs;
554203c4805SLuis R. Rodriguez 
555cbbdf2aeSOleksij Rempel 	if (ath9k_cmn_beacon_config_sta(ah, conf, &bs) == -EPERM)
5561a20034aSSenthil Balasubramanian 		return;
557203c4805SLuis R. Rodriguez 
5584df3071eSFelix Fietkau 	ath9k_hw_disable_interrupts(ah);
5593069168cSPavel Roskin 	ath9k_hw_set_sta_beacon_timers(ah, &bs);
5603069168cSPavel Roskin 	ah->imask |= ATH9K_INT_BMISS;
561deb75188SRajkumar Manoharan 
56272d874c6SFelix Fietkau 	ath9k_hw_set_interrupts(ah);
563b037b693SRajkumar Manoharan 	ath9k_hw_enable_interrupts(ah);
564b037b693SRajkumar Manoharan }
565203c4805SLuis R. Rodriguez 
ath9k_beacon_config_adhoc(struct ath_softc * sc,struct ath_beacon_config * conf)566ef4ad633SSujith Manoharan static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
567ee832d3eSMohammed Shafi Shajakhan 				      struct ath_beacon_config *conf)
568203c4805SLuis R. Rodriguez {
5693069168cSPavel Roskin 	struct ath_hw *ah = sc->sc_ah;
5703069168cSPavel Roskin 	struct ath_common *common = ath9k_hw_common(ah);
571203c4805SLuis R. Rodriguez 
572ba4903f9SFelix Fietkau 	ath9k_reset_beacon_status(sc);
573ba4903f9SFelix Fietkau 
5744c9a1f32SOleksij Rempel 	ath9k_cmn_beacon_config_adhoc(ah, conf);
5751a6404a1SSujith Manoharan 
576cfda2d8eSBenjamin Berg 	ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
5771a6404a1SSujith Manoharan 
5781a6404a1SSujith Manoharan 	/*
5791a6404a1SSujith Manoharan 	 * Set the global 'beacon has been configured' flag for the
5801a6404a1SSujith Manoharan 	 * joiner case in IBSS mode.
5811a6404a1SSujith Manoharan 	 */
5821a6404a1SSujith Manoharan 	if (!conf->ibss_creator && conf->enable_beacon)
583eefa01ddSOleksij Rempel 		set_bit(ATH_OP_BEACONS, &common->op_flags);
584b037b693SRajkumar Manoharan }
585203c4805SLuis R. Rodriguez 
ath9k_cache_beacon_config(struct ath_softc * sc,struct ath_chanctx * ctx,struct ieee80211_vif * vif)586ef4ad633SSujith Manoharan static void ath9k_cache_beacon_config(struct ath_softc *sc,
5879a9c4fbcSRajkumar Manoharan 				      struct ath_chanctx *ctx,
588f276e20bSJohannes Berg 				      struct ieee80211_vif *vif)
58999e4d43aSRajkumar Manoharan {
590f276e20bSJohannes Berg 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
591ef4ad633SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
5929a9c4fbcSRajkumar Manoharan 	struct ath_beacon_config *cur_conf = &ctx->beacon;
59399e4d43aSRajkumar Manoharan 
594ef4ad633SSujith Manoharan 	ath_dbg(common, BEACON,
595ef4ad633SSujith Manoharan 		"Caching beacon data for BSS: %pM\n", bss_conf->bssid);
59699e4d43aSRajkumar Manoharan 
59799e4d43aSRajkumar Manoharan 	cur_conf->beacon_interval = bss_conf->beacon_int;
59899e4d43aSRajkumar Manoharan 	cur_conf->dtim_period = bss_conf->dtim_period;
5996b96f93eSVasanthakumar Thiagarajan 	cur_conf->dtim_count = 1;
600f276e20bSJohannes Berg 	cur_conf->ibss_creator = vif->cfg.ibss_creator;
6016b96f93eSVasanthakumar Thiagarajan 
602c4f9f16bSVasanthakumar Thiagarajan 	/*
603c4f9f16bSVasanthakumar Thiagarajan 	 * It looks like mac80211 may end up using beacon interval of zero in
604c4f9f16bSVasanthakumar Thiagarajan 	 * some cases (at least for mesh point). Avoid getting into an
605c4f9f16bSVasanthakumar Thiagarajan 	 * infinite loop by using a bit safer value instead. To be safe,
606c4f9f16bSVasanthakumar Thiagarajan 	 * do sanity check on beacon interval for all operating modes.
607c4f9f16bSVasanthakumar Thiagarajan 	 */
608c4f9f16bSVasanthakumar Thiagarajan 	if (cur_conf->beacon_interval == 0)
609c4f9f16bSVasanthakumar Thiagarajan 		cur_conf->beacon_interval = 100;
6106b96f93eSVasanthakumar Thiagarajan 
61176c93983SBen Greear 	cur_conf->bmiss_timeout =
61276c93983SBen Greear 		ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
61376c93983SBen Greear 
614ee832d3eSMohammed Shafi Shajakhan 	/*
6153a2329f2SMohammed Shafi Shajakhan 	 * We don't parse dtim period from mac80211 during the driver
6163a2329f2SMohammed Shafi Shajakhan 	 * initialization as it breaks association with hidden-ssid
6173a2329f2SMohammed Shafi Shajakhan 	 * AP and it causes latency in roaming
618ee832d3eSMohammed Shafi Shajakhan 	 */
619ee832d3eSMohammed Shafi Shajakhan 	if (cur_conf->dtim_period == 0)
620ee832d3eSMohammed Shafi Shajakhan 		cur_conf->dtim_period = 1;
621ee832d3eSMohammed Shafi Shajakhan 
622cfda2d8eSBenjamin Berg 	ath9k_set_tsfadjust(sc, cur_conf);
62399e4d43aSRajkumar Manoharan }
62499e4d43aSRajkumar Manoharan 
ath9k_beacon_config(struct ath_softc * sc,struct ieee80211_vif * main_vif,bool beacons)625cfda2d8eSBenjamin Berg void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif,
626cfda2d8eSBenjamin Berg 			 bool beacons)
6278e22ad32SRajkumar Manoharan {
628eefa01ddSOleksij Rempel 	struct ath_hw *ah = sc->sc_ah;
629eefa01ddSOleksij Rempel 	struct ath_common *common = ath9k_hw_common(ah);
630cfda2d8eSBenjamin Berg 	struct ath_vif *avp;
631cfda2d8eSBenjamin Berg 	struct ath_chanctx *ctx;
6329a9c4fbcSRajkumar Manoharan 	struct ath_beacon_config *cur_conf;
6331a6404a1SSujith Manoharan 	unsigned long flags;
634cfda2d8eSBenjamin Berg 	bool enabled;
6351a6404a1SSujith Manoharan 	bool skip_beacon = false;
6368e22ad32SRajkumar Manoharan 
637cfda2d8eSBenjamin Berg 	if (!beacons) {
638cfda2d8eSBenjamin Berg 		clear_bit(ATH_OP_BEACONS, &common->op_flags);
639cfda2d8eSBenjamin Berg 		ath9k_beacon_stop(sc);
640cfda2d8eSBenjamin Berg 		return;
641cfda2d8eSBenjamin Berg 	}
642cfda2d8eSBenjamin Berg 
643cfda2d8eSBenjamin Berg 	if (WARN_ON(!main_vif))
6449a9c4fbcSRajkumar Manoharan 		return;
6459a9c4fbcSRajkumar Manoharan 
646cfda2d8eSBenjamin Berg 	avp = (void *)main_vif->drv_priv;
647cfda2d8eSBenjamin Berg 	ctx = avp->chanctx;
648cfda2d8eSBenjamin Berg 	cur_conf = &ctx->beacon;
649cfda2d8eSBenjamin Berg 	enabled = cur_conf->enable_beacon;
650cfda2d8eSBenjamin Berg 	cur_conf->enable_beacon = beacons;
651c32e4e51SFelix Fietkau 
652cfda2d8eSBenjamin Berg 	if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
653f276e20bSJohannes Berg 		ath9k_cache_beacon_config(sc, ctx, main_vif);
6549a9c4fbcSRajkumar Manoharan 
655ef4ad633SSujith Manoharan 		ath9k_set_beacon(sc);
656eefa01ddSOleksij Rempel 		set_bit(ATH_OP_BEACONS, &common->op_flags);
6571a6404a1SSujith Manoharan 		return;
6581a6404a1SSujith Manoharan 	}
6591a6404a1SSujith Manoharan 
660cfda2d8eSBenjamin Berg 	/* Update the beacon configuration. */
661f276e20bSJohannes Berg 	ath9k_cache_beacon_config(sc, ctx, main_vif);
6629a9c4fbcSRajkumar Manoharan 
6631a6404a1SSujith Manoharan 	/*
6641a6404a1SSujith Manoharan 	 * Configure the HW beacon registers only when we have a valid
6651a6404a1SSujith Manoharan 	 * beacon interval.
6661a6404a1SSujith Manoharan 	 */
667ef4ad633SSujith Manoharan 	if (cur_conf->beacon_interval) {
668cfda2d8eSBenjamin Berg 		/* Special case to sync the TSF when joining an existing IBSS.
669cfda2d8eSBenjamin Berg 		 * This is only done if no AP interface is active.
670cfda2d8eSBenjamin Berg 		 * Note that mac80211 always resets the TSF when creating a new
671cfda2d8eSBenjamin Berg 		 * IBSS interface.
6721a6404a1SSujith Manoharan 		 */
673cfda2d8eSBenjamin Berg 		if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC &&
674f276e20bSJohannes Berg 		    !enabled && beacons && !main_vif->cfg.ibss_creator) {
6751a6404a1SSujith Manoharan 			spin_lock_irqsave(&sc->sc_pm_lock, flags);
6761a6404a1SSujith Manoharan 			sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
6771a6404a1SSujith Manoharan 			spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
6781a6404a1SSujith Manoharan 			skip_beacon = true;
6791a6404a1SSujith Manoharan 		}
6808e22ad32SRajkumar Manoharan 
6811a6404a1SSujith Manoharan 		/*
682eefa01ddSOleksij Rempel 		 * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode
6831a6404a1SSujith Manoharan 		 * here, it is done in ath9k_beacon_config_adhoc().
6841a6404a1SSujith Manoharan 		 */
685cfda2d8eSBenjamin Berg 		if (beacons && !skip_beacon) {
686eefa01ddSOleksij Rempel 			set_bit(ATH_OP_BEACONS, &common->op_flags);
687cfda2d8eSBenjamin Berg 			ath9k_set_beacon(sc);
688cfda2d8eSBenjamin Berg 		} else {
689eefa01ddSOleksij Rempel 			clear_bit(ATH_OP_BEACONS, &common->op_flags);
690cfda2d8eSBenjamin Berg 			ath9k_beacon_stop(sc);
691cfda2d8eSBenjamin Berg 		}
692cfda2d8eSBenjamin Berg 	} else {
693cfda2d8eSBenjamin Berg 		clear_bit(ATH_OP_BEACONS, &common->op_flags);
694cfda2d8eSBenjamin Berg 		ath9k_beacon_stop(sc);
695ef4ad633SSujith Manoharan 	}
696ef4ad633SSujith Manoharan }
697ef4ad633SSujith Manoharan 
ath9k_set_beacon(struct ath_softc * sc)698ef4ad633SSujith Manoharan void ath9k_set_beacon(struct ath_softc *sc)
69999e4d43aSRajkumar Manoharan {
70099e4d43aSRajkumar Manoharan 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
701ca900ac9SRajkumar Manoharan 	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
70299e4d43aSRajkumar Manoharan 
70326cd322bSFelix Fietkau 	switch (sc->sc_ah->opmode) {
704203c4805SLuis R. Rodriguez 	case NL80211_IFTYPE_AP:
7052664d666SThomas Pedersen 	case NL80211_IFTYPE_MESH_POINT:
706ef4ad633SSujith Manoharan 		ath9k_beacon_config_ap(sc, cur_conf);
707203c4805SLuis R. Rodriguez 		break;
708203c4805SLuis R. Rodriguez 	case NL80211_IFTYPE_ADHOC:
709ef4ad633SSujith Manoharan 		ath9k_beacon_config_adhoc(sc, cur_conf);
710203c4805SLuis R. Rodriguez 		break;
711203c4805SLuis R. Rodriguez 	case NL80211_IFTYPE_STATION:
712cbbdf2aeSOleksij Rempel 		ath9k_beacon_config_sta(sc->sc_ah, cur_conf);
713203c4805SLuis R. Rodriguez 		break;
714203c4805SLuis R. Rodriguez 	default:
715d2182b69SJoe Perches 		ath_dbg(common, CONFIG, "Unsupported beaconing mode\n");
716203c4805SLuis R. Rodriguez 		return;
717203c4805SLuis R. Rodriguez 	}
718014cf3bbSRajkumar Manoharan }
719