1 | /* |
2 | * Copyright (c) 2008-2011 Atheros Communications Inc. |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include <linux/dma-mapping.h> |
18 | #include "ath9k.h" |
19 | |
20 | #define FUDGE 2 |
21 | |
22 | static void ath9k_reset_beacon_status(struct ath_softc *sc) |
23 | { |
24 | sc->beacon.tx_processed = false; |
25 | sc->beacon.tx_last = false; |
26 | } |
27 | |
28 | /* |
29 | * This function will modify certain transmit queue properties depending on |
30 | * the operating mode of the station (AP or AdHoc). Parameters are AIFS |
31 | * settings and channel width min/max |
32 | */ |
33 | static void ath9k_beaconq_config(struct ath_softc *sc) |
34 | { |
35 | struct ath_hw *ah = sc->sc_ah; |
36 | struct ath_common *common = ath9k_hw_common(ah); |
37 | struct ath9k_tx_queue_info qi, qi_be; |
38 | struct ath_txq *txq; |
39 | |
40 | ath9k_hw_get_txq_props(ah, q: sc->beacon.beaconq, qinfo: &qi); |
41 | |
42 | if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || |
43 | sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) { |
44 | /* Always burst out beacon and CAB traffic. */ |
45 | qi.tqi_aifs = 1; |
46 | qi.tqi_cwmin = 0; |
47 | qi.tqi_cwmax = 0; |
48 | } else { |
49 | /* Adhoc mode; important thing is to use 2x cwmin. */ |
50 | txq = sc->tx.txq_map[IEEE80211_AC_BE]; |
51 | ath9k_hw_get_txq_props(ah, q: txq->axq_qnum, qinfo: &qi_be); |
52 | qi.tqi_aifs = qi_be.tqi_aifs; |
53 | if (ah->slottime == 20) |
54 | qi.tqi_cwmin = 2*qi_be.tqi_cwmin; |
55 | else |
56 | qi.tqi_cwmin = 4*qi_be.tqi_cwmin; |
57 | qi.tqi_cwmax = qi_be.tqi_cwmax; |
58 | } |
59 | |
60 | if (!ath9k_hw_set_txq_props(ah, q: sc->beacon.beaconq, qinfo: &qi)) { |
61 | ath_err(common, "Unable to update h/w beacon queue parameters\n" ); |
62 | } else { |
63 | ath9k_hw_resettxqueue(ah, q: sc->beacon.beaconq); |
64 | } |
65 | } |
66 | |
67 | /* |
68 | * Associates the beacon frame buffer with a transmit descriptor. Will set |
69 | * up rate codes, and channel flags. Beacons are always sent out at the |
70 | * lowest rate, and are not retried. |
71 | */ |
72 | static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, |
73 | struct ath_buf *bf, int rateidx) |
74 | { |
75 | struct sk_buff *skb = bf->bf_mpdu; |
76 | struct ath_hw *ah = sc->sc_ah; |
77 | struct ath_common *common = ath9k_hw_common(ah); |
78 | struct ath_tx_info info; |
79 | struct ieee80211_supported_band *sband; |
80 | u8 chainmask = ah->txchainmask; |
81 | u8 i, rate = 0; |
82 | |
83 | sband = &common->sbands[sc->cur_chandef.chan->band]; |
84 | rate = sband->bitrates[rateidx].hw_value; |
85 | if (vif->bss_conf.use_short_preamble) |
86 | rate |= sband->bitrates[rateidx].hw_value_short; |
87 | |
88 | memset(&info, 0, sizeof(info)); |
89 | info.pkt_len = skb->len + FCS_LEN; |
90 | info.type = ATH9K_PKT_TYPE_BEACON; |
91 | for (i = 0; i < 4; i++) |
92 | info.txpower[i] = MAX_RATE_POWER; |
93 | info.keyix = ATH9K_TXKEYIX_INVALID; |
94 | info.keytype = ATH9K_KEY_TYPE_CLEAR; |
95 | info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK; |
96 | |
97 | info.buf_addr[0] = bf->bf_buf_addr; |
98 | info.buf_len[0] = roundup(skb->len, 4); |
99 | |
100 | info.is_first = true; |
101 | info.is_last = true; |
102 | |
103 | info.qcu = sc->beacon.beaconq; |
104 | |
105 | info.rates[0].Tries = 1; |
106 | info.rates[0].Rate = rate; |
107 | info.rates[0].ChSel = ath_txchainmask_reduction(sc, chainmask, rate); |
108 | |
109 | ath9k_hw_set_txdesc(ah, ds: bf->bf_desc, i: &info); |
110 | } |
111 | |
112 | static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, |
113 | struct ieee80211_vif *vif) |
114 | { |
115 | struct ath_softc *sc = hw->priv; |
116 | struct ath_common *common = ath9k_hw_common(ah: sc->sc_ah); |
117 | struct ath_buf *bf; |
118 | struct ath_vif *avp = (void *)vif->drv_priv; |
119 | struct sk_buff *skb; |
120 | struct ath_txq *cabq = sc->beacon.cabq; |
121 | struct ieee80211_tx_info *info; |
122 | struct ieee80211_mgmt *mgmt_hdr; |
123 | int cabq_depth; |
124 | |
125 | if (avp->av_bcbuf == NULL) |
126 | return NULL; |
127 | |
128 | bf = avp->av_bcbuf; |
129 | skb = bf->bf_mpdu; |
130 | if (skb) { |
131 | dma_unmap_single(sc->dev, bf->bf_buf_addr, |
132 | skb->len, DMA_TO_DEVICE); |
133 | dev_kfree_skb_any(skb); |
134 | bf->bf_buf_addr = 0; |
135 | bf->bf_mpdu = NULL; |
136 | } |
137 | |
138 | skb = ieee80211_beacon_get(hw, vif, link_id: 0); |
139 | if (skb == NULL) |
140 | return NULL; |
141 | |
142 | bf->bf_mpdu = skb; |
143 | |
144 | mgmt_hdr = (struct ieee80211_mgmt *)skb->data; |
145 | mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust; |
146 | |
147 | info = IEEE80211_SKB_CB(skb); |
148 | |
149 | ath_assign_seq(common, skb); |
150 | |
151 | /* Always assign NOA attr when MCC enabled */ |
152 | if (ath9k_is_chanctx_enabled()) |
153 | ath9k_beacon_add_noa(sc, avp, skb); |
154 | |
155 | bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, |
156 | skb->len, DMA_TO_DEVICE); |
157 | if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { |
158 | dev_kfree_skb_any(skb); |
159 | bf->bf_mpdu = NULL; |
160 | bf->bf_buf_addr = 0; |
161 | ath_err(common, "dma_mapping_error on beaconing\n" ); |
162 | return NULL; |
163 | } |
164 | |
165 | skb = ieee80211_get_buffered_bc(hw, vif); |
166 | |
167 | /* |
168 | * if the CABQ traffic from previous DTIM is pending and the current |
169 | * beacon is also a DTIM. |
170 | * 1) if there is only one vif let the cab traffic continue. |
171 | * 2) if there are more than one vif and we are using staggered |
172 | * beacons, then drain the cabq by dropping all the frames in |
173 | * the cabq so that the current vifs cab traffic can be scheduled. |
174 | */ |
175 | spin_lock_bh(lock: &cabq->axq_lock); |
176 | cabq_depth = cabq->axq_depth; |
177 | spin_unlock_bh(lock: &cabq->axq_lock); |
178 | |
179 | if (skb && cabq_depth) { |
180 | if (sc->cur_chan->nvifs > 1) { |
181 | ath_dbg(common, BEACON, |
182 | "Flushing previous cabq traffic\n" ); |
183 | ath_draintxq(sc, txq: cabq); |
184 | } |
185 | } |
186 | |
187 | ath9k_beacon_setup(sc, vif, bf, rateidx: info->control.rates[0].idx); |
188 | |
189 | if (skb) |
190 | ath_tx_cabq(hw, vif, skb); |
191 | |
192 | return bf; |
193 | } |
194 | |
195 | void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif) |
196 | { |
197 | struct ath_common *common = ath9k_hw_common(ah: sc->sc_ah); |
198 | struct ath_vif *avp = (void *)vif->drv_priv; |
199 | int slot; |
200 | |
201 | avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list); |
202 | list_del(entry: &avp->av_bcbuf->list); |
203 | |
204 | for (slot = 0; slot < ATH_BCBUF; slot++) { |
205 | if (sc->beacon.bslot[slot] == NULL) { |
206 | avp->av_bslot = slot; |
207 | break; |
208 | } |
209 | } |
210 | |
211 | sc->beacon.bslot[avp->av_bslot] = vif; |
212 | |
213 | ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n" , |
214 | avp->av_bslot); |
215 | } |
216 | |
217 | void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) |
218 | { |
219 | struct ath_common *common = ath9k_hw_common(ah: sc->sc_ah); |
220 | struct ath_vif *avp = (void *)vif->drv_priv; |
221 | struct ath_buf *bf = avp->av_bcbuf; |
222 | |
223 | ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n" , |
224 | avp->av_bslot); |
225 | |
226 | tasklet_disable(t: &sc->bcon_tasklet); |
227 | |
228 | if (bf && bf->bf_mpdu) { |
229 | struct sk_buff *skb = bf->bf_mpdu; |
230 | dma_unmap_single(sc->dev, bf->bf_buf_addr, |
231 | skb->len, DMA_TO_DEVICE); |
232 | dev_kfree_skb_any(skb); |
233 | bf->bf_mpdu = NULL; |
234 | bf->bf_buf_addr = 0; |
235 | } |
236 | |
237 | avp->av_bcbuf = NULL; |
238 | sc->beacon.bslot[avp->av_bslot] = NULL; |
239 | list_add_tail(new: &bf->list, head: &sc->beacon.bbuf); |
240 | |
241 | tasklet_enable(t: &sc->bcon_tasklet); |
242 | } |
243 | |
244 | void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc) |
245 | { |
246 | struct ath_common *common = ath9k_hw_common(ah: sc->sc_ah); |
247 | struct ieee80211_vif *vif; |
248 | struct ath_vif *avp; |
249 | s64 tsfadjust; |
250 | u32 offset; |
251 | int first_slot = ATH_BCBUF; |
252 | int slot; |
253 | |
254 | tasklet_disable_in_atomic(t: &sc->bcon_tasklet); |
255 | |
256 | /* Find first taken slot. */ |
257 | for (slot = 0; slot < ATH_BCBUF; slot++) { |
258 | if (sc->beacon.bslot[slot]) { |
259 | first_slot = slot; |
260 | break; |
261 | } |
262 | } |
263 | if (first_slot == 0) |
264 | goto out; |
265 | |
266 | /* Re-enumarate all slots, moving them forward. */ |
267 | for (slot = 0; slot < ATH_BCBUF; slot++) { |
268 | if (slot + first_slot < ATH_BCBUF) { |
269 | vif = sc->beacon.bslot[slot + first_slot]; |
270 | sc->beacon.bslot[slot] = vif; |
271 | |
272 | if (vif) { |
273 | avp = (void *)vif->drv_priv; |
274 | avp->av_bslot = slot; |
275 | } |
276 | } else { |
277 | sc->beacon.bslot[slot] = NULL; |
278 | } |
279 | } |
280 | |
281 | vif = sc->beacon.bslot[0]; |
282 | if (WARN_ON(!vif)) |
283 | goto out; |
284 | |
285 | /* Get the tsf_adjust value for the new first slot. */ |
286 | avp = (void *)vif->drv_priv; |
287 | tsfadjust = le64_to_cpu(avp->tsf_adjust); |
288 | |
289 | ath_dbg(common, CONFIG, |
290 | "Adjusting global TSF after beacon slot reassignment: %lld\n" , |
291 | (signed long long)tsfadjust); |
292 | |
293 | /* Modify TSF as required and update the HW. */ |
294 | avp->chanctx->tsf_val += tsfadjust; |
295 | if (sc->cur_chan == avp->chanctx) { |
296 | offset = ath9k_hw_get_tsf_offset(last: &avp->chanctx->tsf_ts, NULL); |
297 | ath9k_hw_settsf64(ah: sc->sc_ah, tsf64: avp->chanctx->tsf_val + offset); |
298 | } |
299 | |
300 | /* The slots tsf_adjust will be updated by ath9k_beacon_config later. */ |
301 | |
302 | out: |
303 | tasklet_enable(t: &sc->bcon_tasklet); |
304 | } |
305 | |
306 | static int ath9k_beacon_choose_slot(struct ath_softc *sc) |
307 | { |
308 | struct ath_common *common = ath9k_hw_common(ah: sc->sc_ah); |
309 | struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; |
310 | u16 intval; |
311 | u32 tsftu; |
312 | u64 tsf; |
313 | int slot; |
314 | |
315 | if (sc->sc_ah->opmode != NL80211_IFTYPE_AP && |
316 | sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) { |
317 | ath_dbg(common, BEACON, "slot 0, tsf: %llu\n" , |
318 | ath9k_hw_gettsf64(sc->sc_ah)); |
319 | return 0; |
320 | } |
321 | |
322 | intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL; |
323 | tsf = ath9k_hw_gettsf64(ah: sc->sc_ah); |
324 | tsf += TU_TO_USEC(sc->sc_ah->config.sw_beacon_response_time); |
325 | tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF); |
326 | slot = (tsftu % (intval * ATH_BCBUF)) / intval; |
327 | |
328 | ath_dbg(common, BEACON, "slot: %d tsf: %llu tsftu: %u\n" , |
329 | slot, tsf, tsftu / ATH_BCBUF); |
330 | |
331 | return slot; |
332 | } |
333 | |
334 | static void ath9k_set_tsfadjust(struct ath_softc *sc, |
335 | struct ath_beacon_config *cur_conf) |
336 | { |
337 | struct ath_common *common = ath9k_hw_common(ah: sc->sc_ah); |
338 | s64 tsfadjust; |
339 | int slot; |
340 | |
341 | for (slot = 0; slot < ATH_BCBUF; slot++) { |
342 | struct ath_vif *avp; |
343 | |
344 | if (!sc->beacon.bslot[slot]) |
345 | continue; |
346 | |
347 | avp = (void *)sc->beacon.bslot[slot]->drv_priv; |
348 | |
349 | /* tsf_adjust is added to the TSF value. We send out the |
350 | * beacon late, so need to adjust the TSF starting point to be |
351 | * later in time (i.e. the theoretical first beacon has a TSF |
352 | * of 0 after correction). |
353 | */ |
354 | tsfadjust = cur_conf->beacon_interval * avp->av_bslot; |
355 | tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF; |
356 | avp->tsf_adjust = cpu_to_le64(tsfadjust); |
357 | |
358 | ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n" , |
359 | (signed long long)tsfadjust, avp->av_bslot); |
360 | } |
361 | } |
362 | |
363 | bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif) |
364 | { |
365 | if (!vif || !vif->bss_conf.csa_active) |
366 | return false; |
367 | |
368 | if (!ieee80211_beacon_cntdwn_is_complete(vif, link_id: 0)) |
369 | return false; |
370 | |
371 | ieee80211_csa_finish(vif, link_id: 0); |
372 | return true; |
373 | } |
374 | |
375 | static void ath9k_csa_update_vif(void *data, u8 *mac, struct ieee80211_vif *vif) |
376 | { |
377 | struct ath_softc *sc = data; |
378 | ath9k_csa_is_finished(sc, vif); |
379 | } |
380 | |
381 | void ath9k_csa_update(struct ath_softc *sc) |
382 | { |
383 | ieee80211_iterate_active_interfaces_atomic(hw: sc->hw, |
384 | iter_flags: IEEE80211_IFACE_ITER_NORMAL, |
385 | iterator: ath9k_csa_update_vif, data: sc); |
386 | } |
387 | |
388 | void ath9k_beacon_tasklet(struct tasklet_struct *t) |
389 | { |
390 | struct ath_softc *sc = from_tasklet(sc, t, bcon_tasklet); |
391 | struct ath_hw *ah = sc->sc_ah; |
392 | struct ath_common *common = ath9k_hw_common(ah); |
393 | struct ath_buf *bf = NULL; |
394 | struct ieee80211_vif *vif; |
395 | bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); |
396 | int slot; |
397 | |
398 | if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) { |
399 | ath_dbg(common, RESET, |
400 | "reset work is pending, skip beaconing now\n" ); |
401 | return; |
402 | } |
403 | |
404 | /* |
405 | * Check if the previous beacon has gone out. If |
406 | * not don't try to post another, skip this period |
407 | * and wait for the next. Missed beacons indicate |
408 | * a problem and should not occur. If we miss too |
409 | * many consecutive beacons reset the device. |
410 | */ |
411 | if (ath9k_hw_numtxpending(ah, q: sc->beacon.beaconq) != 0) { |
412 | sc->beacon.bmisscnt++; |
413 | |
414 | ath9k_hw_check_nav(ah); |
415 | |
416 | /* |
417 | * If the previous beacon has not been transmitted |
418 | * and a MAC/BB hang has been identified, return |
419 | * here because a chip reset would have been |
420 | * initiated. |
421 | */ |
422 | if (!ath_hw_check(sc)) |
423 | return; |
424 | |
425 | if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) { |
426 | ath_dbg(common, BSTUCK, |
427 | "missed %u consecutive beacons\n" , |
428 | sc->beacon.bmisscnt); |
429 | ath9k_hw_stop_dma_queue(ah, q: sc->beacon.beaconq); |
430 | if (sc->beacon.bmisscnt > 3) |
431 | ath9k_hw_bstuck_nfcal(ah); |
432 | } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { |
433 | ath_dbg(common, BSTUCK, "beacon is officially stuck\n" ); |
434 | sc->beacon.bmisscnt = 0; |
435 | ath9k_queue_reset(sc, type: RESET_TYPE_BEACON_STUCK); |
436 | } |
437 | |
438 | return; |
439 | } |
440 | |
441 | slot = ath9k_beacon_choose_slot(sc); |
442 | vif = sc->beacon.bslot[slot]; |
443 | |
444 | /* EDMA devices check that in the tx completion function. */ |
445 | if (!edma) { |
446 | if (ath9k_is_chanctx_enabled()) { |
447 | ath_chanctx_beacon_sent_ev(sc, |
448 | ev: ATH_CHANCTX_EVENT_BEACON_SENT); |
449 | } |
450 | |
451 | if (ath9k_csa_is_finished(sc, vif)) |
452 | return; |
453 | } |
454 | |
455 | if (!vif || !vif->bss_conf.enable_beacon) |
456 | return; |
457 | |
458 | if (ath9k_is_chanctx_enabled()) { |
459 | ath_chanctx_event(sc, vif, ev: ATH_CHANCTX_EVENT_BEACON_PREPARE); |
460 | } |
461 | |
462 | bf = ath9k_beacon_generate(hw: sc->hw, vif); |
463 | |
464 | if (sc->beacon.bmisscnt != 0) { |
465 | ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n" , |
466 | sc->beacon.bmisscnt); |
467 | sc->beacon.bmisscnt = 0; |
468 | } |
469 | |
470 | /* |
471 | * Handle slot time change when a non-ERP station joins/leaves |
472 | * an 11g network. The 802.11 layer notifies us via callback, |
473 | * we mark updateslot, then wait one beacon before effecting |
474 | * the change. This gives associated stations at least one |
475 | * beacon interval to note the state change. |
476 | * |
477 | * NB: The slot time change state machine is clocked according |
478 | * to whether we are bursting or staggering beacons. We |
479 | * recognize the request to update and record the current |
480 | * slot then don't transition until that slot is reached |
481 | * again. If we miss a beacon for that slot then we'll be |
482 | * slow to transition but we'll be sure at least one beacon |
483 | * interval has passed. When bursting slot is always left |
484 | * set to ATH_BCBUF so this check is a noop. |
485 | */ |
486 | if (sc->beacon.updateslot == UPDATE) { |
487 | sc->beacon.updateslot = COMMIT; |
488 | sc->beacon.slotupdate = slot; |
489 | } else if (sc->beacon.updateslot == COMMIT && |
490 | sc->beacon.slotupdate == slot) { |
491 | ah->slottime = sc->beacon.slottime; |
492 | ath9k_hw_init_global_settings(ah); |
493 | sc->beacon.updateslot = OK; |
494 | } |
495 | |
496 | if (bf) { |
497 | ath9k_reset_beacon_status(sc); |
498 | |
499 | ath_dbg(common, BEACON, |
500 | "Transmitting beacon for slot: %d\n" , slot); |
501 | |
502 | /* NB: cabq traffic should already be queued and primed */ |
503 | ath9k_hw_puttxbuf(ah, q: sc->beacon.beaconq, txdp: bf->bf_daddr); |
504 | |
505 | if (!edma) |
506 | ath9k_hw_txstart(ah, q: sc->beacon.beaconq); |
507 | } |
508 | } |
509 | |
510 | /* |
511 | * Both nexttbtt and intval have to be in usecs. |
512 | */ |
513 | static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, |
514 | u32 intval) |
515 | { |
516 | struct ath_hw *ah = sc->sc_ah; |
517 | |
518 | ath9k_hw_disable_interrupts(ah); |
519 | ath9k_beaconq_config(sc); |
520 | ath9k_hw_beaconinit(ah, next_beacon: nexttbtt, beacon_period: intval); |
521 | ah->imask |= ATH9K_INT_SWBA; |
522 | sc->beacon.bmisscnt = 0; |
523 | ath9k_hw_set_interrupts(ah); |
524 | ath9k_hw_enable_interrupts(ah); |
525 | } |
526 | |
527 | static void ath9k_beacon_stop(struct ath_softc *sc) |
528 | { |
529 | ath9k_hw_disable_interrupts(ah: sc->sc_ah); |
530 | sc->sc_ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); |
531 | sc->beacon.bmisscnt = 0; |
532 | ath9k_hw_set_interrupts(ah: sc->sc_ah); |
533 | ath9k_hw_enable_interrupts(ah: sc->sc_ah); |
534 | } |
535 | |
536 | /* |
537 | * For multi-bss ap support beacons are either staggered evenly over N slots or |
538 | * burst together. For the former arrange for the SWBA to be delivered for each |
539 | * slot. Slots that are not occupied will generate nothing. |
540 | */ |
541 | static void ath9k_beacon_config_ap(struct ath_softc *sc, |
542 | struct ath_beacon_config *conf) |
543 | { |
544 | struct ath_hw *ah = sc->sc_ah; |
545 | |
546 | ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF); |
547 | ath9k_beacon_init(sc, nexttbtt: conf->nexttbtt, intval: conf->intval); |
548 | } |
549 | |
550 | static void ath9k_beacon_config_sta(struct ath_hw *ah, |
551 | struct ath_beacon_config *conf) |
552 | { |
553 | struct ath9k_beacon_state bs; |
554 | |
555 | if (ath9k_cmn_beacon_config_sta(ah, conf, bs: &bs) == -EPERM) |
556 | return; |
557 | |
558 | ath9k_hw_disable_interrupts(ah); |
559 | ath9k_hw_set_sta_beacon_timers(ah, bs: &bs); |
560 | ah->imask |= ATH9K_INT_BMISS; |
561 | |
562 | ath9k_hw_set_interrupts(ah); |
563 | ath9k_hw_enable_interrupts(ah); |
564 | } |
565 | |
566 | static void ath9k_beacon_config_adhoc(struct ath_softc *sc, |
567 | struct ath_beacon_config *conf) |
568 | { |
569 | struct ath_hw *ah = sc->sc_ah; |
570 | struct ath_common *common = ath9k_hw_common(ah); |
571 | |
572 | ath9k_reset_beacon_status(sc); |
573 | |
574 | ath9k_cmn_beacon_config_adhoc(ah, conf); |
575 | |
576 | ath9k_beacon_init(sc, nexttbtt: conf->nexttbtt, intval: conf->intval); |
577 | |
578 | /* |
579 | * Set the global 'beacon has been configured' flag for the |
580 | * joiner case in IBSS mode. |
581 | */ |
582 | if (!conf->ibss_creator && conf->enable_beacon) |
583 | set_bit(nr: ATH_OP_BEACONS, addr: &common->op_flags); |
584 | } |
585 | |
586 | static void ath9k_cache_beacon_config(struct ath_softc *sc, |
587 | struct ath_chanctx *ctx, |
588 | struct ieee80211_vif *vif) |
589 | { |
590 | struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; |
591 | struct ath_common *common = ath9k_hw_common(ah: sc->sc_ah); |
592 | struct ath_beacon_config *cur_conf = &ctx->beacon; |
593 | |
594 | ath_dbg(common, BEACON, |
595 | "Caching beacon data for BSS: %pM\n" , bss_conf->bssid); |
596 | |
597 | cur_conf->beacon_interval = bss_conf->beacon_int; |
598 | cur_conf->dtim_period = bss_conf->dtim_period; |
599 | cur_conf->dtim_count = 1; |
600 | cur_conf->ibss_creator = vif->cfg.ibss_creator; |
601 | |
602 | /* |
603 | * It looks like mac80211 may end up using beacon interval of zero in |
604 | * some cases (at least for mesh point). Avoid getting into an |
605 | * infinite loop by using a bit safer value instead. To be safe, |
606 | * do sanity check on beacon interval for all operating modes. |
607 | */ |
608 | if (cur_conf->beacon_interval == 0) |
609 | cur_conf->beacon_interval = 100; |
610 | |
611 | cur_conf->bmiss_timeout = |
612 | ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; |
613 | |
614 | /* |
615 | * We don't parse dtim period from mac80211 during the driver |
616 | * initialization as it breaks association with hidden-ssid |
617 | * AP and it causes latency in roaming |
618 | */ |
619 | if (cur_conf->dtim_period == 0) |
620 | cur_conf->dtim_period = 1; |
621 | |
622 | ath9k_set_tsfadjust(sc, cur_conf); |
623 | } |
624 | |
625 | void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif, |
626 | bool beacons) |
627 | { |
628 | struct ath_hw *ah = sc->sc_ah; |
629 | struct ath_common *common = ath9k_hw_common(ah); |
630 | struct ath_vif *avp; |
631 | struct ath_chanctx *ctx; |
632 | struct ath_beacon_config *cur_conf; |
633 | unsigned long flags; |
634 | bool enabled; |
635 | bool skip_beacon = false; |
636 | |
637 | if (!beacons) { |
638 | clear_bit(nr: ATH_OP_BEACONS, addr: &common->op_flags); |
639 | ath9k_beacon_stop(sc); |
640 | return; |
641 | } |
642 | |
643 | if (WARN_ON(!main_vif)) |
644 | return; |
645 | |
646 | avp = (void *)main_vif->drv_priv; |
647 | ctx = avp->chanctx; |
648 | cur_conf = &ctx->beacon; |
649 | enabled = cur_conf->enable_beacon; |
650 | cur_conf->enable_beacon = beacons; |
651 | |
652 | if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { |
653 | ath9k_cache_beacon_config(sc, ctx, vif: main_vif); |
654 | |
655 | ath9k_set_beacon(sc); |
656 | set_bit(nr: ATH_OP_BEACONS, addr: &common->op_flags); |
657 | return; |
658 | } |
659 | |
660 | /* Update the beacon configuration. */ |
661 | ath9k_cache_beacon_config(sc, ctx, vif: main_vif); |
662 | |
663 | /* |
664 | * Configure the HW beacon registers only when we have a valid |
665 | * beacon interval. |
666 | */ |
667 | if (cur_conf->beacon_interval) { |
668 | /* Special case to sync the TSF when joining an existing IBSS. |
669 | * This is only done if no AP interface is active. |
670 | * Note that mac80211 always resets the TSF when creating a new |
671 | * IBSS interface. |
672 | */ |
673 | if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC && |
674 | !enabled && beacons && !main_vif->cfg.ibss_creator) { |
675 | spin_lock_irqsave(&sc->sc_pm_lock, flags); |
676 | sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; |
677 | spin_unlock_irqrestore(lock: &sc->sc_pm_lock, flags); |
678 | skip_beacon = true; |
679 | } |
680 | |
681 | /* |
682 | * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode |
683 | * here, it is done in ath9k_beacon_config_adhoc(). |
684 | */ |
685 | if (beacons && !skip_beacon) { |
686 | set_bit(nr: ATH_OP_BEACONS, addr: &common->op_flags); |
687 | ath9k_set_beacon(sc); |
688 | } else { |
689 | clear_bit(nr: ATH_OP_BEACONS, addr: &common->op_flags); |
690 | ath9k_beacon_stop(sc); |
691 | } |
692 | } else { |
693 | clear_bit(nr: ATH_OP_BEACONS, addr: &common->op_flags); |
694 | ath9k_beacon_stop(sc); |
695 | } |
696 | } |
697 | |
698 | void ath9k_set_beacon(struct ath_softc *sc) |
699 | { |
700 | struct ath_common *common = ath9k_hw_common(ah: sc->sc_ah); |
701 | struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; |
702 | |
703 | switch (sc->sc_ah->opmode) { |
704 | case NL80211_IFTYPE_AP: |
705 | case NL80211_IFTYPE_MESH_POINT: |
706 | ath9k_beacon_config_ap(sc, conf: cur_conf); |
707 | break; |
708 | case NL80211_IFTYPE_ADHOC: |
709 | ath9k_beacon_config_adhoc(sc, conf: cur_conf); |
710 | break; |
711 | case NL80211_IFTYPE_STATION: |
712 | ath9k_beacon_config_sta(ah: sc->sc_ah, conf: cur_conf); |
713 | break; |
714 | default: |
715 | ath_dbg(common, CONFIG, "Unsupported beaconing mode\n" ); |
716 | return; |
717 | } |
718 | } |
719 | |