Commit | Line | Data |
---|---|---|
f078f209 LR |
1 | /* |
2 | * Copyright (c) 2008 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 | ||
394cf0a1 | 17 | #include "ath9k.h" |
f078f209 | 18 | |
5379c8a2 S |
19 | #define FUDGE 2 |
20 | ||
f078f209 | 21 | /* |
f078f209 LR |
22 | * This function will modify certain transmit queue properties depending on |
23 | * the operating mode of the station (AP or AdHoc). Parameters are AIFS | |
24 | * settings and channel width min/max | |
25 | */ | |
f078f209 LR |
26 | static int ath_beaconq_config(struct ath_softc *sc) |
27 | { | |
cbe61d8a | 28 | struct ath_hw *ah = sc->sc_ah; |
ea9880fb | 29 | struct ath9k_tx_queue_info qi; |
f078f209 | 30 | |
b77f483f | 31 | ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); |
2660b81a | 32 | if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { |
f078f209 LR |
33 | /* Always burst out beacon and CAB traffic. */ |
34 | qi.tqi_aifs = 1; | |
35 | qi.tqi_cwmin = 0; | |
36 | qi.tqi_cwmax = 0; | |
37 | } else { | |
38 | /* Adhoc mode; important thing is to use 2x cwmin. */ | |
b77f483f S |
39 | qi.tqi_aifs = sc->beacon.beacon_qi.tqi_aifs; |
40 | qi.tqi_cwmin = 2*sc->beacon.beacon_qi.tqi_cwmin; | |
41 | qi.tqi_cwmax = sc->beacon.beacon_qi.tqi_cwmax; | |
f078f209 LR |
42 | } |
43 | ||
b77f483f | 44 | if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) { |
f078f209 | 45 | DPRINTF(sc, ATH_DBG_FATAL, |
04bd4638 | 46 | "unable to update h/w beacon queue parameters\n"); |
f078f209 LR |
47 | return 0; |
48 | } else { | |
9fc9ab0a | 49 | ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); |
f078f209 LR |
50 | return 1; |
51 | } | |
52 | } | |
53 | ||
54 | /* | |
f078f209 LR |
55 | * Associates the beacon frame buffer with a transmit descriptor. Will set |
56 | * up all required antenna switch parameters, rate codes, and channel flags. | |
57 | * Beacons are always sent out at the lowest rate, and are not retried. | |
58 | */ | |
9fc9ab0a S |
59 | static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, |
60 | struct ath_buf *bf) | |
f078f209 LR |
61 | { |
62 | struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; | |
cbe61d8a | 63 | struct ath_hw *ah = sc->sc_ah; |
f078f209 | 64 | struct ath_desc *ds; |
980b24da | 65 | struct ath9k_11n_rate_series series[4]; |
e63835b0 | 66 | struct ath_rate_table *rt; |
9fc9ab0a S |
67 | int flags, antenna, ctsrate = 0, ctsduration = 0; |
68 | u8 rate; | |
f078f209 | 69 | |
f078f209 | 70 | ds = bf->bf_desc; |
f078f209 LR |
71 | flags = ATH9K_TXDESC_NOACK; |
72 | ||
2660b81a S |
73 | if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC && |
74 | (ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { | |
f078f209 LR |
75 | ds->ds_link = bf->bf_daddr; /* self-linked */ |
76 | flags |= ATH9K_TXDESC_VEOL; | |
77 | /* Let hardware handle antenna switching. */ | |
78 | antenna = 0; | |
79 | } else { | |
80 | ds->ds_link = 0; | |
81 | /* | |
82 | * Switch antenna every beacon. | |
9fc9ab0a S |
83 | * Should only switch every beacon period, not for every SWBA |
84 | * XXX assumes two antennae | |
f078f209 | 85 | */ |
17d7904d | 86 | antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1); |
f078f209 LR |
87 | } |
88 | ||
89 | ds->ds_data = bf->bf_buf_addr; | |
90 | ||
3706de6f | 91 | rt = sc->cur_rate_table; |
9fc9ab0a | 92 | rate = rt->info[0].ratecode; |
672840ac | 93 | if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) |
9fc9ab0a S |
94 | rate |= rt->info[0].short_preamble; |
95 | ||
96 | ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN, | |
97 | ATH9K_PKT_TYPE_BEACON, | |
98 | MAX_RATE_POWER, | |
99 | ATH9K_TXKEYIX_INVALID, | |
100 | ATH9K_KEY_TYPE_CLEAR, | |
101 | flags); | |
f078f209 LR |
102 | |
103 | /* NB: beacon's BufLen must be a multiple of 4 bytes */ | |
9fc9ab0a S |
104 | ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4), |
105 | true, true, ds); | |
f078f209 | 106 | |
0345f37b | 107 | memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4); |
f078f209 LR |
108 | series[0].Tries = 1; |
109 | series[0].Rate = rate; | |
17d7904d | 110 | series[0].ChSel = sc->tx_chainmask; |
f078f209 | 111 | series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; |
9fc9ab0a S |
112 | ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration, |
113 | series, 4, 0); | |
f078f209 LR |
114 | } |
115 | ||
c52f33d0 | 116 | static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, |
2c3db3d5 | 117 | struct ieee80211_vif *vif) |
f078f209 | 118 | { |
c52f33d0 JM |
119 | struct ath_wiphy *aphy = hw->priv; |
120 | struct ath_softc *sc = aphy->sc; | |
f078f209 | 121 | struct ath_buf *bf; |
17d7904d | 122 | struct ath_vif *avp; |
f078f209 | 123 | struct sk_buff *skb; |
f078f209 | 124 | struct ath_txq *cabq; |
147583c0 | 125 | struct ieee80211_tx_info *info; |
980b24da S |
126 | int cabq_depth; |
127 | ||
5640b08e | 128 | avp = (void *)vif->drv_priv; |
b77f483f | 129 | cabq = sc->beacon.cabq; |
f078f209 | 130 | |
f078f209 | 131 | if (avp->av_bcbuf == NULL) { |
04bd4638 S |
132 | DPRINTF(sc, ATH_DBG_BEACON, "avp=%p av_bcbuf=%p\n", |
133 | avp, avp->av_bcbuf); | |
f078f209 LR |
134 | return NULL; |
135 | } | |
980b24da | 136 | |
9fc9ab0a S |
137 | /* Release the old beacon first */ |
138 | ||
f078f209 | 139 | bf = avp->av_bcbuf; |
980b24da | 140 | skb = (struct sk_buff *)bf->bf_mpdu; |
a8fff50e | 141 | if (skb) { |
7da3c55c | 142 | dma_unmap_single(sc->dev, bf->bf_dmacontext, |
9fc9ab0a | 143 | skb->len, DMA_TO_DEVICE); |
3fbb9d95 | 144 | dev_kfree_skb_any(skb); |
a8fff50e | 145 | } |
f078f209 | 146 | |
9fc9ab0a S |
147 | /* Get a new beacon from mac80211 */ |
148 | ||
c52f33d0 | 149 | skb = ieee80211_beacon_get(hw, vif); |
a8fff50e JM |
150 | bf->bf_mpdu = skb; |
151 | if (skb == NULL) | |
152 | return NULL; | |
980b24da | 153 | |
147583c0 JM |
154 | info = IEEE80211_SKB_CB(skb); |
155 | if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { | |
156 | /* | |
157 | * TODO: make sure the seq# gets assigned properly (vs. other | |
158 | * TX frames) | |
159 | */ | |
980b24da | 160 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
b77f483f | 161 | sc->tx.seq_no += 0x10; |
147583c0 | 162 | hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); |
b77f483f | 163 | hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); |
147583c0 | 164 | } |
980b24da | 165 | |
a8fff50e | 166 | bf->bf_buf_addr = bf->bf_dmacontext = |
7da3c55c | 167 | dma_map_single(sc->dev, skb->data, |
9fc9ab0a | 168 | skb->len, DMA_TO_DEVICE); |
7da3c55c | 169 | if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { |
f8316df1 LR |
170 | dev_kfree_skb_any(skb); |
171 | bf->bf_mpdu = NULL; | |
9fc9ab0a | 172 | DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error on beaconing\n"); |
f8316df1 LR |
173 | return NULL; |
174 | } | |
f078f209 | 175 | |
c52f33d0 | 176 | skb = ieee80211_get_buffered_bc(hw, vif); |
f078f209 | 177 | |
f078f209 LR |
178 | /* |
179 | * if the CABQ traffic from previous DTIM is pending and the current | |
180 | * beacon is also a DTIM. | |
17d7904d S |
181 | * 1) if there is only one vif let the cab traffic continue. |
182 | * 2) if there are more than one vif and we are using staggered | |
f078f209 | 183 | * beacons, then drain the cabq by dropping all the frames in |
17d7904d | 184 | * the cabq so that the current vifs cab traffic can be scheduled. |
f078f209 LR |
185 | */ |
186 | spin_lock_bh(&cabq->axq_lock); | |
187 | cabq_depth = cabq->axq_depth; | |
188 | spin_unlock_bh(&cabq->axq_lock); | |
189 | ||
e022edbd | 190 | if (skb && cabq_depth) { |
17d7904d | 191 | if (sc->nvifs > 1) { |
f078f209 | 192 | DPRINTF(sc, ATH_DBG_BEACON, |
9fc9ab0a S |
193 | "Flushing previous cabq traffic\n"); |
194 | ath_draintxq(sc, cabq, false); | |
f078f209 LR |
195 | } |
196 | } | |
197 | ||
f078f209 LR |
198 | ath_beacon_setup(sc, avp, bf); |
199 | ||
e022edbd | 200 | while (skb) { |
c52f33d0 JM |
201 | ath_tx_cabq(hw, skb); |
202 | skb = ieee80211_get_buffered_bc(hw, vif); | |
e022edbd | 203 | } |
f078f209 | 204 | |
f078f209 LR |
205 | return bf; |
206 | } | |
207 | ||
208 | /* | |
209 | * Startup beacon transmission for adhoc mode when they are sent entirely | |
210 | * by the hardware using the self-linked descriptor + veol trick. | |
211 | */ | |
2c3db3d5 JM |
212 | static void ath_beacon_start_adhoc(struct ath_softc *sc, |
213 | struct ieee80211_vif *vif) | |
f078f209 | 214 | { |
cbe61d8a | 215 | struct ath_hw *ah = sc->sc_ah; |
f078f209 | 216 | struct ath_buf *bf; |
17d7904d | 217 | struct ath_vif *avp; |
f078f209 LR |
218 | struct sk_buff *skb; |
219 | ||
5640b08e | 220 | avp = (void *)vif->drv_priv; |
f078f209 | 221 | |
9fc9ab0a | 222 | if (avp->av_bcbuf == NULL) |
f078f209 | 223 | return; |
9fc9ab0a | 224 | |
f078f209 LR |
225 | bf = avp->av_bcbuf; |
226 | skb = (struct sk_buff *) bf->bf_mpdu; | |
227 | ||
f078f209 LR |
228 | ath_beacon_setup(sc, avp, bf); |
229 | ||
230 | /* NB: caller is known to have already stopped tx dma */ | |
b77f483f S |
231 | ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); |
232 | ath9k_hw_txstart(ah, sc->beacon.beaconq); | |
04bd4638 | 233 | DPRINTF(sc, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n", |
b77f483f | 234 | sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc); |
f078f209 LR |
235 | } |
236 | ||
cbe61d8a | 237 | int ath_beaconq_setup(struct ath_hw *ah) |
f078f209 | 238 | { |
ea9880fb | 239 | struct ath9k_tx_queue_info qi; |
f078f209 | 240 | |
0345f37b | 241 | memset(&qi, 0, sizeof(qi)); |
f078f209 LR |
242 | qi.tqi_aifs = 1; |
243 | qi.tqi_cwmin = 0; | |
244 | qi.tqi_cwmax = 0; | |
245 | /* NB: don't enable any interrupts */ | |
246 | return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); | |
247 | } | |
248 | ||
c52f33d0 | 249 | int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) |
f078f209 | 250 | { |
c52f33d0 | 251 | struct ath_softc *sc = aphy->sc; |
17d7904d | 252 | struct ath_vif *avp; |
980b24da | 253 | struct ieee80211_hdr *hdr; |
f078f209 LR |
254 | struct ath_buf *bf; |
255 | struct sk_buff *skb; | |
459f5f90 | 256 | __le64 tstamp; |
f078f209 | 257 | |
5640b08e | 258 | avp = (void *)vif->drv_priv; |
f078f209 LR |
259 | |
260 | /* Allocate a beacon descriptor if we haven't done so. */ | |
261 | if (!avp->av_bcbuf) { | |
980b24da S |
262 | /* Allocate beacon state for hostap/ibss. We know |
263 | * a buffer is available. */ | |
b77f483f | 264 | avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, |
980b24da | 265 | struct ath_buf, list); |
f078f209 LR |
266 | list_del(&avp->av_bcbuf->list); |
267 | ||
2660b81a S |
268 | if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || |
269 | !(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { | |
f078f209 LR |
270 | int slot; |
271 | /* | |
17d7904d | 272 | * Assign the vif to a beacon xmit slot. As |
f078f209 LR |
273 | * above, this cannot fail to find one. |
274 | */ | |
275 | avp->av_bslot = 0; | |
276 | for (slot = 0; slot < ATH_BCBUF; slot++) | |
2c3db3d5 | 277 | if (sc->beacon.bslot[slot] == NULL) { |
f078f209 LR |
278 | /* |
279 | * XXX hack, space out slots to better | |
280 | * deal with misses | |
281 | */ | |
282 | if (slot+1 < ATH_BCBUF && | |
2c3db3d5 | 283 | sc->beacon.bslot[slot+1] == NULL) { |
f078f209 LR |
284 | avp->av_bslot = slot+1; |
285 | break; | |
286 | } | |
287 | avp->av_bslot = slot; | |
288 | /* NB: keep looking for a double slot */ | |
289 | } | |
2c3db3d5 JM |
290 | BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL); |
291 | sc->beacon.bslot[avp->av_bslot] = vif; | |
c52f33d0 | 292 | sc->beacon.bslot_aphy[avp->av_bslot] = aphy; |
17d7904d | 293 | sc->nbcnvifs++; |
f078f209 LR |
294 | } |
295 | } | |
296 | ||
9fc9ab0a | 297 | /* release the previous beacon frame, if it already exists. */ |
f078f209 LR |
298 | bf = avp->av_bcbuf; |
299 | if (bf->bf_mpdu != NULL) { | |
300 | skb = (struct sk_buff *)bf->bf_mpdu; | |
7da3c55c | 301 | dma_unmap_single(sc->dev, bf->bf_dmacontext, |
9fc9ab0a | 302 | skb->len, DMA_TO_DEVICE); |
f078f209 LR |
303 | dev_kfree_skb_any(skb); |
304 | bf->bf_mpdu = NULL; | |
305 | } | |
306 | ||
9fc9ab0a | 307 | /* NB: the beacon data buffer must be 32-bit aligned. */ |
5640b08e | 308 | skb = ieee80211_beacon_get(sc->hw, vif); |
f078f209 | 309 | if (skb == NULL) { |
04bd4638 | 310 | DPRINTF(sc, ATH_DBG_BEACON, "cannot get skb\n"); |
f078f209 LR |
311 | return -ENOMEM; |
312 | } | |
313 | ||
459f5f90 | 314 | tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; |
b77f483f | 315 | sc->beacon.bc_tstamp = le64_to_cpu(tstamp); |
459f5f90 | 316 | |
f078f209 LR |
317 | /* |
318 | * Calculate a TSF adjustment factor required for | |
319 | * staggered beacons. Note that we assume the format | |
320 | * of the beacon frame leaves the tstamp field immediately | |
321 | * following the header. | |
322 | */ | |
323 | if (avp->av_bslot > 0) { | |
324 | u64 tsfadjust; | |
325 | __le64 val; | |
326 | int intval; | |
327 | ||
a8fff50e JM |
328 | intval = sc->hw->conf.beacon_int ? |
329 | sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; | |
f078f209 LR |
330 | |
331 | /* | |
332 | * The beacon interval is in TU's; the TSF in usecs. | |
333 | * We figure out how many TU's to add to align the | |
334 | * timestamp then convert to TSF units and handle | |
335 | * byte swapping before writing it in the frame. | |
336 | * The hardware will then add this each time a beacon | |
17d7904d S |
337 | * frame is sent. Note that we align vif's 1..N |
338 | * and leave vif 0 untouched. This means vap 0 | |
f078f209 LR |
339 | * has a timestamp in one beacon interval while the |
340 | * others get a timestamp aligned to the next interval. | |
341 | */ | |
342 | tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF; | |
343 | val = cpu_to_le64(tsfadjust << 10); /* TU->TSF */ | |
344 | ||
345 | DPRINTF(sc, ATH_DBG_BEACON, | |
04bd4638 | 346 | "stagger beacons, bslot %d intval %u tsfadjust %llu\n", |
f078f209 LR |
347 | avp->av_bslot, intval, (unsigned long long)tsfadjust); |
348 | ||
980b24da S |
349 | hdr = (struct ieee80211_hdr *)skb->data; |
350 | memcpy(&hdr[1], &val, sizeof(val)); | |
f078f209 LR |
351 | } |
352 | ||
f8316df1 | 353 | bf->bf_mpdu = skb; |
a8fff50e | 354 | bf->bf_buf_addr = bf->bf_dmacontext = |
7da3c55c | 355 | dma_map_single(sc->dev, skb->data, |
9fc9ab0a | 356 | skb->len, DMA_TO_DEVICE); |
7da3c55c | 357 | if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { |
f8316df1 LR |
358 | dev_kfree_skb_any(skb); |
359 | bf->bf_mpdu = NULL; | |
9fc9ab0a S |
360 | DPRINTF(sc, ATH_DBG_FATAL, |
361 | "dma_mapping_error on beacon alloc\n"); | |
f8316df1 LR |
362 | return -ENOMEM; |
363 | } | |
f078f209 LR |
364 | |
365 | return 0; | |
366 | } | |
367 | ||
17d7904d | 368 | void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp) |
f078f209 LR |
369 | { |
370 | if (avp->av_bcbuf != NULL) { | |
371 | struct ath_buf *bf; | |
372 | ||
373 | if (avp->av_bslot != -1) { | |
2c3db3d5 | 374 | sc->beacon.bslot[avp->av_bslot] = NULL; |
c52f33d0 | 375 | sc->beacon.bslot_aphy[avp->av_bslot] = NULL; |
17d7904d | 376 | sc->nbcnvifs--; |
f078f209 LR |
377 | } |
378 | ||
379 | bf = avp->av_bcbuf; | |
380 | if (bf->bf_mpdu != NULL) { | |
381 | struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; | |
7da3c55c | 382 | dma_unmap_single(sc->dev, bf->bf_dmacontext, |
9fc9ab0a | 383 | skb->len, DMA_TO_DEVICE); |
f078f209 LR |
384 | dev_kfree_skb_any(skb); |
385 | bf->bf_mpdu = NULL; | |
386 | } | |
b77f483f | 387 | list_add_tail(&bf->list, &sc->beacon.bbuf); |
f078f209 LR |
388 | |
389 | avp->av_bcbuf = NULL; | |
390 | } | |
391 | } | |
392 | ||
9fc9ab0a | 393 | void ath_beacon_tasklet(unsigned long data) |
f078f209 | 394 | { |
f078f209 | 395 | struct ath_softc *sc = (struct ath_softc *)data; |
cbe61d8a | 396 | struct ath_hw *ah = sc->sc_ah; |
f078f209 | 397 | struct ath_buf *bf = NULL; |
2c3db3d5 | 398 | struct ieee80211_vif *vif; |
c52f33d0 | 399 | struct ath_wiphy *aphy; |
2c3db3d5 | 400 | int slot; |
9546aae0 | 401 | u32 bfaddr, bc = 0, tsftu; |
f078f209 | 402 | u64 tsf; |
f078f209 LR |
403 | u16 intval; |
404 | ||
f078f209 LR |
405 | /* |
406 | * Check if the previous beacon has gone out. If | |
407 | * not don't try to post another, skip this period | |
408 | * and wait for the next. Missed beacons indicate | |
409 | * a problem and should not occur. If we miss too | |
410 | * many consecutive beacons reset the device. | |
411 | */ | |
b77f483f S |
412 | if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { |
413 | sc->beacon.bmisscnt++; | |
9546aae0 | 414 | |
b77f483f | 415 | if (sc->beacon.bmisscnt < BSTUCK_THRESH) { |
9546aae0 S |
416 | DPRINTF(sc, ATH_DBG_BEACON, |
417 | "missed %u consecutive beacons\n", | |
418 | sc->beacon.bmisscnt); | |
b77f483f | 419 | } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { |
9546aae0 S |
420 | DPRINTF(sc, ATH_DBG_BEACON, |
421 | "beacon is officially stuck\n"); | |
422 | ath_reset(sc, false); | |
f078f209 | 423 | } |
9546aae0 | 424 | |
f078f209 LR |
425 | return; |
426 | } | |
980b24da | 427 | |
b77f483f | 428 | if (sc->beacon.bmisscnt != 0) { |
9546aae0 S |
429 | DPRINTF(sc, ATH_DBG_BEACON, |
430 | "resume beacon xmit after %u misses\n", | |
431 | sc->beacon.bmisscnt); | |
b77f483f | 432 | sc->beacon.bmisscnt = 0; |
f078f209 LR |
433 | } |
434 | ||
435 | /* | |
436 | * Generate beacon frames. we are sending frames | |
437 | * staggered so calculate the slot for this frame based | |
438 | * on the tsf to safeguard against missing an swba. | |
439 | */ | |
440 | ||
a8fff50e JM |
441 | intval = sc->hw->conf.beacon_int ? |
442 | sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; | |
f078f209 LR |
443 | |
444 | tsf = ath9k_hw_gettsf64(ah); | |
445 | tsftu = TSF_TO_TU(tsf>>32, tsf); | |
446 | slot = ((tsftu % intval) * ATH_BCBUF) / intval; | |
2c3db3d5 | 447 | vif = sc->beacon.bslot[(slot + 1) % ATH_BCBUF]; |
c52f33d0 | 448 | aphy = sc->beacon.bslot_aphy[(slot + 1) % ATH_BCBUF]; |
980b24da | 449 | |
f078f209 | 450 | DPRINTF(sc, ATH_DBG_BEACON, |
2c3db3d5 JM |
451 | "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", |
452 | slot, tsf, tsftu, intval, vif); | |
980b24da | 453 | |
f078f209 | 454 | bfaddr = 0; |
2c3db3d5 | 455 | if (vif) { |
c52f33d0 | 456 | bf = ath_beacon_generate(aphy->hw, vif); |
f078f209 LR |
457 | if (bf != NULL) { |
458 | bfaddr = bf->bf_daddr; | |
459 | bc = 1; | |
460 | } | |
461 | } | |
9546aae0 | 462 | |
f078f209 LR |
463 | /* |
464 | * Handle slot time change when a non-ERP station joins/leaves | |
465 | * an 11g network. The 802.11 layer notifies us via callback, | |
466 | * we mark updateslot, then wait one beacon before effecting | |
467 | * the change. This gives associated stations at least one | |
468 | * beacon interval to note the state change. | |
469 | * | |
470 | * NB: The slot time change state machine is clocked according | |
471 | * to whether we are bursting or staggering beacons. We | |
472 | * recognize the request to update and record the current | |
473 | * slot then don't transition until that slot is reached | |
474 | * again. If we miss a beacon for that slot then we'll be | |
475 | * slow to transition but we'll be sure at least one beacon | |
476 | * interval has passed. When bursting slot is always left | |
477 | * set to ATH_BCBUF so this check is a noop. | |
478 | */ | |
b77f483f S |
479 | if (sc->beacon.updateslot == UPDATE) { |
480 | sc->beacon.updateslot = COMMIT; /* commit next beacon */ | |
481 | sc->beacon.slotupdate = slot; | |
482 | } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) { | |
483 | ath9k_hw_setslottime(sc->sc_ah, sc->beacon.slottime); | |
484 | sc->beacon.updateslot = OK; | |
ff37e337 | 485 | } |
f078f209 LR |
486 | if (bfaddr != 0) { |
487 | /* | |
488 | * Stop any current dma and put the new frame(s) on the queue. | |
489 | * This should never fail since we check above that no frames | |
490 | * are still pending on the queue. | |
491 | */ | |
b77f483f | 492 | if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) { |
f078f209 | 493 | DPRINTF(sc, ATH_DBG_FATAL, |
b77f483f | 494 | "beacon queue %u did not stop?\n", sc->beacon.beaconq); |
f078f209 LR |
495 | } |
496 | ||
497 | /* NB: cabq traffic should already be queued and primed */ | |
b77f483f S |
498 | ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr); |
499 | ath9k_hw_txstart(ah, sc->beacon.beaconq); | |
f078f209 | 500 | |
17d7904d | 501 | sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */ |
f078f209 | 502 | } |
f078f209 LR |
503 | } |
504 | ||
f078f209 | 505 | /* |
5379c8a2 S |
506 | * For multi-bss ap support beacons are either staggered evenly over N slots or |
507 | * burst together. For the former arrange for the SWBA to be delivered for each | |
508 | * slot. Slots that are not occupied will generate nothing. | |
f078f209 | 509 | */ |
5379c8a2 S |
510 | static void ath_beacon_config_ap(struct ath_softc *sc, |
511 | struct ath_beacon_config *conf, | |
512 | struct ath_vif *avp) | |
f078f209 | 513 | { |
980b24da | 514 | u32 nexttbtt, intval; |
f078f209 | 515 | |
b238e90e S |
516 | /* Configure the timers only when the TSF has to be reset */ |
517 | ||
518 | if (!(sc->sc_flags & SC_OP_TSF_RESET)) | |
519 | return; | |
520 | ||
5379c8a2 S |
521 | /* NB: the beacon interval is kept internally in TU's */ |
522 | intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; | |
523 | intval /= ATH_BCBUF; /* for staggered beacons */ | |
524 | nexttbtt = intval; | |
525 | intval |= ATH9K_BEACON_RESET_TSF; | |
f078f209 | 526 | |
5379c8a2 S |
527 | /* |
528 | * In AP mode we enable the beacon timers and SWBA interrupts to | |
529 | * prepare beacon frames. | |
530 | */ | |
531 | intval |= ATH9K_BEACON_ENA; | |
532 | sc->imask |= ATH9K_INT_SWBA; | |
533 | ath_beaconq_config(sc); | |
f078f209 | 534 | |
5379c8a2 | 535 | /* Set the computed AP beacon timers */ |
f078f209 | 536 | |
5379c8a2 S |
537 | ath9k_hw_set_interrupts(sc->sc_ah, 0); |
538 | ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval); | |
539 | sc->beacon.bmisscnt = 0; | |
540 | ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); | |
b238e90e S |
541 | |
542 | /* Clear the reset TSF flag, so that subsequent beacon updation | |
543 | will not reset the HW TSF. */ | |
544 | ||
545 | sc->sc_flags &= ~SC_OP_TSF_RESET; | |
5379c8a2 | 546 | } |
459f5f90 | 547 | |
5379c8a2 S |
548 | /* |
549 | * This sets up the beacon timers according to the timestamp of the last | |
550 | * received beacon and the current TSF, configures PCF and DTIM | |
551 | * handling, programs the sleep registers so the hardware will wakeup in | |
552 | * time to receive beacons, and configures the beacon miss handling so | |
553 | * we'll receive a BMISS interrupt when we stop seeing beacons from the AP | |
554 | * we've associated with. | |
555 | */ | |
556 | static void ath_beacon_config_sta(struct ath_softc *sc, | |
557 | struct ath_beacon_config *conf, | |
558 | struct ath_vif *avp) | |
559 | { | |
560 | struct ath9k_beacon_state bs; | |
561 | int dtimperiod, dtimcount, sleepduration; | |
562 | int cfpperiod, cfpcount; | |
563 | u32 nexttbtt = 0, intval, tsftu; | |
564 | u64 tsf; | |
565 | ||
566 | memset(&bs, 0, sizeof(bs)); | |
567 | intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; | |
568 | ||
569 | /* | |
570 | * Setup dtim and cfp parameters according to | |
571 | * last beacon we received (which may be none). | |
572 | */ | |
573 | dtimperiod = conf->dtim_period; | |
574 | if (dtimperiod <= 0) /* NB: 0 if not known */ | |
575 | dtimperiod = 1; | |
576 | dtimcount = conf->dtim_count; | |
577 | if (dtimcount >= dtimperiod) /* NB: sanity check */ | |
578 | dtimcount = 0; | |
579 | cfpperiod = 1; /* NB: no PCF support yet */ | |
580 | cfpcount = 0; | |
581 | ||
582 | sleepduration = conf->listen_interval * intval; | |
583 | if (sleepduration <= 0) | |
584 | sleepduration = intval; | |
585 | ||
586 | /* | |
587 | * Pull nexttbtt forward to reflect the current | |
588 | * TSF and calculate dtim+cfp state for the result. | |
589 | */ | |
590 | tsf = ath9k_hw_gettsf64(sc->sc_ah); | |
591 | tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; | |
592 | do { | |
593 | nexttbtt += intval; | |
594 | if (--dtimcount < 0) { | |
595 | dtimcount = dtimperiod - 1; | |
596 | if (--cfpcount < 0) | |
597 | cfpcount = cfpperiod - 1; | |
598 | } | |
599 | } while (nexttbtt < tsftu); | |
600 | ||
601 | bs.bs_intval = intval; | |
602 | bs.bs_nexttbtt = nexttbtt; | |
603 | bs.bs_dtimperiod = dtimperiod*intval; | |
604 | bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; | |
605 | bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; | |
606 | bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; | |
607 | bs.bs_cfpmaxduration = 0; | |
608 | ||
609 | /* | |
610 | * Calculate the number of consecutive beacons to miss* before taking | |
611 | * a BMISS interrupt. The configuration is specified in TU so we only | |
612 | * need calculate based on the beacon interval. Note that we clamp the | |
613 | * result to at most 15 beacons. | |
614 | */ | |
615 | if (sleepduration > intval) { | |
616 | bs.bs_bmissthreshold = conf->listen_interval * | |
617 | ATH_DEFAULT_BMISS_LIMIT / 2; | |
f078f209 | 618 | } else { |
5379c8a2 S |
619 | bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval); |
620 | if (bs.bs_bmissthreshold > 15) | |
621 | bs.bs_bmissthreshold = 15; | |
622 | else if (bs.bs_bmissthreshold <= 0) | |
623 | bs.bs_bmissthreshold = 1; | |
f078f209 LR |
624 | } |
625 | ||
5379c8a2 S |
626 | /* |
627 | * Calculate sleep duration. The configuration is given in ms. | |
628 | * We ensure a multiple of the beacon period is used. Also, if the sleep | |
629 | * duration is greater than the DTIM period then it makes senses | |
630 | * to make it a multiple of that. | |
631 | * | |
632 | * XXX fixed at 100ms | |
633 | */ | |
980b24da | 634 | |
5379c8a2 S |
635 | bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); |
636 | if (bs.bs_sleepduration > bs.bs_dtimperiod) | |
637 | bs.bs_sleepduration = bs.bs_dtimperiod; | |
980b24da | 638 | |
5379c8a2 S |
639 | /* TSF out of range threshold fixed at 1 second */ |
640 | bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; | |
f078f209 | 641 | |
5379c8a2 S |
642 | DPRINTF(sc, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); |
643 | DPRINTF(sc, ATH_DBG_BEACON, | |
644 | "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", | |
645 | bs.bs_bmissthreshold, bs.bs_sleepduration, | |
646 | bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); | |
f078f209 | 647 | |
5379c8a2 | 648 | /* Set the computed STA beacon timers */ |
980b24da | 649 | |
5379c8a2 S |
650 | ath9k_hw_set_interrupts(sc->sc_ah, 0); |
651 | ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs); | |
652 | sc->imask |= ATH9K_INT_BMISS; | |
653 | ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); | |
654 | } | |
f078f209 | 655 | |
5379c8a2 S |
656 | static void ath_beacon_config_adhoc(struct ath_softc *sc, |
657 | struct ath_beacon_config *conf, | |
2c3db3d5 JM |
658 | struct ath_vif *avp, |
659 | struct ieee80211_vif *vif) | |
5379c8a2 S |
660 | { |
661 | u64 tsf; | |
662 | u32 tsftu, intval, nexttbtt; | |
f078f209 | 663 | |
5379c8a2 | 664 | intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; |
f078f209 | 665 | |
5379c8a2 | 666 | /* Pull nexttbtt forward to reflect the current TSF */ |
4af9cf4f | 667 | |
5379c8a2 S |
668 | nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp); |
669 | if (nexttbtt == 0) | |
670 | nexttbtt = intval; | |
671 | else if (intval) | |
672 | nexttbtt = roundup(nexttbtt, intval); | |
9fc9ab0a | 673 | |
5379c8a2 S |
674 | tsf = ath9k_hw_gettsf64(sc->sc_ah); |
675 | tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE; | |
676 | do { | |
677 | nexttbtt += intval; | |
678 | } while (nexttbtt < tsftu); | |
f078f209 | 679 | |
5379c8a2 S |
680 | DPRINTF(sc, ATH_DBG_BEACON, |
681 | "IBSS nexttbtt %u intval %u (%u)\n", | |
682 | nexttbtt, intval, conf->beacon_interval); | |
9fc9ab0a | 683 | |
5379c8a2 S |
684 | /* |
685 | * In IBSS mode enable the beacon timers but only enable SWBA interrupts | |
686 | * if we need to manually prepare beacon frames. Otherwise we use a | |
687 | * self-linked tx descriptor and let the hardware deal with things. | |
688 | */ | |
689 | intval |= ATH9K_BEACON_ENA; | |
690 | if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) | |
691 | sc->imask |= ATH9K_INT_SWBA; | |
9fc9ab0a | 692 | |
5379c8a2 S |
693 | ath_beaconq_config(sc); |
694 | ||
695 | /* Set the computed ADHOC beacon timers */ | |
696 | ||
697 | ath9k_hw_set_interrupts(sc->sc_ah, 0); | |
698 | ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval); | |
699 | sc->beacon.bmisscnt = 0; | |
700 | ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); | |
701 | ||
702 | if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL) | |
2c3db3d5 | 703 | ath_beacon_start_adhoc(sc, vif); |
f078f209 LR |
704 | } |
705 | ||
2c3db3d5 | 706 | void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) |
f078f209 | 707 | { |
5379c8a2 | 708 | struct ath_beacon_config conf; |
5379c8a2 S |
709 | |
710 | /* Setup the beacon configuration parameters */ | |
711 | ||
712 | memset(&conf, 0, sizeof(struct ath_beacon_config)); | |
713 | conf.beacon_interval = sc->hw->conf.beacon_int ? | |
714 | sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; | |
715 | conf.listen_interval = 1; | |
716 | conf.dtim_period = conf.beacon_interval; | |
717 | conf.dtim_count = 1; | |
718 | conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval; | |
719 | ||
2c3db3d5 JM |
720 | if (vif) { |
721 | struct ath_vif *avp = (struct ath_vif *)vif->drv_priv; | |
5379c8a2 S |
722 | |
723 | switch(avp->av_opmode) { | |
724 | case NL80211_IFTYPE_AP: | |
725 | ath_beacon_config_ap(sc, &conf, avp); | |
726 | break; | |
727 | case NL80211_IFTYPE_ADHOC: | |
2c3db3d5 | 728 | ath_beacon_config_adhoc(sc, &conf, avp, vif); |
5379c8a2 S |
729 | break; |
730 | case NL80211_IFTYPE_STATION: | |
731 | ath_beacon_config_sta(sc, &conf, avp); | |
732 | break; | |
733 | default: | |
734 | DPRINTF(sc, ATH_DBG_CONFIG, | |
735 | "Unsupported beaconing mode\n"); | |
736 | return; | |
737 | } | |
738 | ||
739 | sc->sc_flags |= SC_OP_BEACONS; | |
740 | } | |
f078f209 | 741 | } |