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