1f078f209SLuis R. Rodriguez /* 25b68138eSSujith Manoharan * Copyright (c) 2008-2011 Atheros Communications Inc. 3f078f209SLuis R. Rodriguez * 4f078f209SLuis R. Rodriguez * Permission to use, copy, modify, and/or distribute this software for any 5f078f209SLuis R. Rodriguez * purpose with or without fee is hereby granted, provided that the above 6f078f209SLuis R. Rodriguez * copyright notice and this permission notice appear in all copies. 7f078f209SLuis R. Rodriguez * 8f078f209SLuis R. Rodriguez * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9f078f209SLuis R. Rodriguez * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10f078f209SLuis R. Rodriguez * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11f078f209SLuis R. Rodriguez * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12f078f209SLuis R. Rodriguez * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13f078f209SLuis R. Rodriguez * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14f078f209SLuis R. Rodriguez * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15f078f209SLuis R. Rodriguez */ 16f078f209SLuis R. Rodriguez 17b7f080cfSAlexey Dobriyan #include <linux/dma-mapping.h> 18394cf0a1SSujith #include "ath9k.h" 19f078f209SLuis R. Rodriguez 205379c8a2SSujith #define FUDGE 2 215379c8a2SSujith 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 28f078f209SLuis R. Rodriguez /* 29f078f209SLuis R. Rodriguez * This function will modify certain transmit queue properties depending on 30f078f209SLuis R. Rodriguez * the operating mode of the station (AP or AdHoc). Parameters are AIFS 31f078f209SLuis R. Rodriguez * settings and channel width min/max 32f078f209SLuis R. Rodriguez */ 337e52c8aaSSujith Manoharan static void ath9k_beaconq_config(struct ath_softc *sc) 34f078f209SLuis R. Rodriguez { 35cbe61d8aSSujith 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; 39f078f209SLuis R. Rodriguez 40b77f483fSSujith 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) { 44f078f209SLuis R. Rodriguez /* Always burst out beacon and CAB traffic. */ 45f078f209SLuis R. Rodriguez qi.tqi_aifs = 1; 46f078f209SLuis R. Rodriguez qi.tqi_cwmin = 0; 47f078f209SLuis R. Rodriguez qi.tqi_cwmax = 0; 48f078f209SLuis R. Rodriguez } else { 49f078f209SLuis 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; 58f078f209SLuis R. Rodriguez } 59f078f209SLuis R. Rodriguez 60b77f483fSSujith 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"); 62f078f209SLuis R. Rodriguez } else { 639fc9ab0aSSujith ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); 64f078f209SLuis R. Rodriguez } 65f078f209SLuis R. Rodriguez } 66f078f209SLuis R. Rodriguez 67f078f209SLuis R. Rodriguez /* 68f078f209SLuis 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. 71f078f209SLuis R. Rodriguez */ 72fb6e252fSSujith Manoharan static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, 7364b84010SJouni Malinen struct ath_buf *bf, int rateidx) 74f078f209SLuis R. Rodriguez { 75a22be22aSSujith struct sk_buff *skb = bf->bf_mpdu; 76cbe61d8aSSujith 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; 82f078f209SLuis 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; 87f078f209SLuis 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; 96f078f209SLuis R. Rodriguez 97493cf04fSFelix Fietkau info.buf_addr[0] = bf->bf_buf_addr; 98493cf04fSFelix Fietkau info.buf_len[0] = roundup(skb->len, 4); 99f078f209SLuis 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); 110f078f209SLuis R. Rodriguez } 111f078f209SLuis R. Rodriguez 112fb6e252fSSujith Manoharan static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, 1132c3db3d5SJouni Malinen struct ieee80211_vif *vif) 114f078f209SLuis R. Rodriguez { 1159ac58615SFelix Fietkau struct ath_softc *sc = hw->priv; 116c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 117f078f209SLuis R. Rodriguez struct ath_buf *bf; 118aa45fe96SSujith Manoharan struct ath_vif *avp = (void *)vif->drv_priv; 119f078f209SLuis R. Rodriguez struct sk_buff *skb; 120aa45fe96SSujith Manoharan struct ath_txq *cabq = sc->beacon.cabq; 121147583c0SJouni Malinen struct ieee80211_tx_info *info; 122fb6e252fSSujith Manoharan struct ieee80211_mgmt *mgmt_hdr; 123980b24daSSujith int cabq_depth; 124980b24daSSujith 125aa45fe96SSujith Manoharan if (avp->av_bcbuf == NULL) 126f078f209SLuis R. Rodriguez return NULL; 127980b24daSSujith 128f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 129a22be22aSSujith skb = bf->bf_mpdu; 130a8fff50eSJouni Malinen if (skb) { 131c1739eb3SBen Greear dma_unmap_single(sc->dev, bf->bf_buf_addr, 1329fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 1333fbb9d95SJouni Malinen dev_kfree_skb_any(skb); 1346cf9e995SBen Greear bf->bf_buf_addr = 0; 1351adb2e2bSFelix Fietkau bf->bf_mpdu = NULL; 136a8fff50eSJouni Malinen } 137f078f209SLuis R. Rodriguez 138c52f33d0SJouni Malinen skb = ieee80211_beacon_get(hw, vif); 139a8fff50eSJouni Malinen if (skb == NULL) 140a8fff50eSJouni Malinen 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; 146980b24daSSujith 147147583c0SJouni Malinen info = IEEE80211_SKB_CB(skb); 148ca14405eSSujith Manoharan 149ca14405eSSujith Manoharan ath_assign_seq(common, skb); 150980b24daSSujith 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, 1569fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 1577da3c55cSGabor Juhos if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { 158f8316df1SLuis R. Rodriguez dev_kfree_skb_any(skb); 159f8316df1SLuis R. Rodriguez bf->bf_mpdu = NULL; 1606cf9e995SBen Greear bf->bf_buf_addr = 0; 1613800276aSJoe Perches ath_err(common, "dma_mapping_error on beaconing\n"); 162f8316df1SLuis R. Rodriguez return NULL; 163f8316df1SLuis R. Rodriguez } 164f078f209SLuis R. Rodriguez 165c52f33d0SJouni Malinen skb = ieee80211_get_buffered_bc(hw, vif); 166f078f209SLuis R. Rodriguez 167f078f209SLuis R. Rodriguez /* 168f078f209SLuis R. Rodriguez * if the CABQ traffic from previous DTIM is pending and the current 169f078f209SLuis R. Rodriguez * beacon is also a DTIM. 17017d7904dSSujith * 1) if there is only one vif let the cab traffic continue. 17117d7904dSSujith * 2) if there are more than one vif and we are using staggered 172f078f209SLuis R. Rodriguez * beacons, then drain the cabq by dropping all the frames in 17317d7904dSSujith * the cabq so that the current vifs cab traffic can be scheduled. 174f078f209SLuis R. Rodriguez */ 175f078f209SLuis R. Rodriguez spin_lock_bh(&cabq->axq_lock); 176f078f209SLuis R. Rodriguez cabq_depth = cabq->axq_depth; 177f078f209SLuis R. Rodriguez spin_unlock_bh(&cabq->axq_lock); 178f078f209SLuis R. Rodriguez 179e022edbdSJouni Malinen if (skb && cabq_depth) { 180ca529c93SSujith Manoharan if (sc->cur_chan->nvifs > 1) { 181d2182b69SJoe Perches ath_dbg(common, BEACON, 1829fc9ab0aSSujith "Flushing previous cabq traffic\n"); 1831381559bSFelix Fietkau ath_draintxq(sc, cabq); 184f078f209SLuis R. Rodriguez } 185f078f209SLuis R. Rodriguez } 186f078f209SLuis R. Rodriguez 187fb6e252fSSujith Manoharan ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx); 188f078f209SLuis R. Rodriguez 18959505c02SFelix Fietkau if (skb) 19059505c02SFelix Fietkau ath_tx_cabq(hw, vif, skb); 191f078f209SLuis R. Rodriguez 192f078f209SLuis R. Rodriguez return bf; 193f078f209SLuis R. Rodriguez } 194f078f209SLuis R. Rodriguez 195130ef6e9SSujith Manoharan void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif) 196f078f209SLuis 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; 200f078f209SLuis R. Rodriguez 201130ef6e9SSujith Manoharan avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list); 202f078f209SLuis R. Rodriguez list_del(&avp->av_bcbuf->list); 203f078f209SLuis R. Rodriguez 204130ef6e9SSujith Manoharan for (slot = 0; slot < ATH_BCBUF; slot++) { 2052c3db3d5SJouni Malinen if (sc->beacon.bslot[slot] == NULL) { 206f078f209SLuis R. Rodriguez avp->av_bslot = slot; 207774610e4SFelix Fietkau break; 208f078f209SLuis R. Rodriguez } 209130ef6e9SSujith Manoharan } 210130ef6e9SSujith Manoharan 2112c3db3d5SJouni Malinen 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); 215f078f209SLuis R. Rodriguez } 216f078f209SLuis R. Rodriguez 217130ef6e9SSujith Manoharan void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) 218f078f209SLuis 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; 222f078f209SLuis R. Rodriguez 223130ef6e9SSujith Manoharan ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n", 224130ef6e9SSujith Manoharan avp->av_bslot); 225f078f209SLuis R. Rodriguez 226130ef6e9SSujith Manoharan tasklet_disable(&sc->bcon_tasklet); 227130ef6e9SSujith Manoharan 228130ef6e9SSujith Manoharan if (bf && bf->bf_mpdu) { 229a22be22aSSujith struct sk_buff *skb = bf->bf_mpdu; 230c1739eb3SBen Greear dma_unmap_single(sc->dev, bf->bf_buf_addr, 2319fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 232f078f209SLuis R. Rodriguez dev_kfree_skb_any(skb); 233f078f209SLuis R. Rodriguez bf->bf_mpdu = NULL; 2346cf9e995SBen Greear bf->bf_buf_addr = 0; 235f078f209SLuis R. Rodriguez } 236f078f209SLuis R. Rodriguez 237f078f209SLuis 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); 242f078f209SLuis R. Rodriguez } 243f078f209SLuis R. Rodriguez 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 254cfda2d8eSBenjamin Berg tasklet_disable(&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 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 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 3634effc6fdSMichal Kazior bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif) 364d074e8d5SSimon Wunderlich { 365d074e8d5SSimon Wunderlich if (!vif || !vif->csa_active) 366d074e8d5SSimon Wunderlich return false; 367d074e8d5SSimon Wunderlich 368*8552a434SJohn 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 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 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 388f91a35b4SAllen Pais void ath9k_beacon_tasklet(struct tasklet_struct *t) 389f078f209SLuis R. Rodriguez { 390f91a35b4SAllen Pais struct ath_softc *sc = from_tasklet(sc, t, bcon_tasklet); 391cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 392c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(ah); 393f078f209SLuis R. Rodriguez struct ath_buf *bf = NULL; 3942c3db3d5SJouni Malinen struct ieee80211_vif *vif; 39598f0a5ebSMohammed Shafi Shajakhan bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); 3962c3db3d5SJouni Malinen int slot; 397f078f209SLuis 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 404f078f209SLuis R. Rodriguez /* 405f078f209SLuis R. Rodriguez * Check if the previous beacon has gone out. If 406f078f209SLuis R. Rodriguez * not don't try to post another, skip this period 407f078f209SLuis R. Rodriguez * and wait for the next. Missed beacons indicate 408f078f209SLuis R. Rodriguez * a problem and should not occur. If we miss too 409f078f209SLuis R. Rodriguez * many consecutive beacons reset the device. 410f078f209SLuis R. Rodriguez */ 411b77f483fSSujith if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { 412b77f483fSSujith sc->beacon.bmisscnt++; 4139546aae0SSujith 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, 42704bd4638SSujith "missed %u consecutive beacons\n", 428b77f483fSSujith 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); 432b77f483fSSujith } 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); 436f078f209SLuis R. Rodriguez } 4379546aae0SSujith 438f078f209SLuis R. Rodriguez return; 439f078f209SLuis R. Rodriguez } 440980b24daSSujith 441fb6e252fSSujith Manoharan slot = ath9k_beacon_choose_slot(sc); 4424ed96f04SJouni Malinen vif = sc->beacon.bslot[slot]; 443980b24daSSujith 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 } 4699546aae0SSujith 470f078f209SLuis R. Rodriguez /* 471f078f209SLuis R. Rodriguez * Handle slot time change when a non-ERP station joins/leaves 472f078f209SLuis R. Rodriguez * an 11g network. The 802.11 layer notifies us via callback, 473f078f209SLuis R. Rodriguez * we mark updateslot, then wait one beacon before effecting 474f078f209SLuis R. Rodriguez * the change. This gives associated stations at least one 475f078f209SLuis R. Rodriguez * beacon interval to note the state change. 476f078f209SLuis R. Rodriguez * 477f078f209SLuis R. Rodriguez * NB: The slot time change state machine is clocked according 478f078f209SLuis R. Rodriguez * to whether we are bursting or staggering beacons. We 479f078f209SLuis R. Rodriguez * recognize the request to update and record the current 480f078f209SLuis R. Rodriguez * slot then don't transition until that slot is reached 481f078f209SLuis R. Rodriguez * again. If we miss a beacon for that slot then we'll be 482f078f209SLuis R. Rodriguez * slow to transition but we'll be sure at least one beacon 483f078f209SLuis R. Rodriguez * interval has passed. When bursting slot is always left 484f078f209SLuis R. Rodriguez * set to ATH_BCBUF so this check is a noop. 485f078f209SLuis R. Rodriguez */ 486b77f483fSSujith if (sc->beacon.updateslot == UPDATE) { 487fb6e252fSSujith Manoharan sc->beacon.updateslot = COMMIT; 488b77f483fSSujith 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); 493b77f483fSSujith sc->beacon.updateslot = OK; 494ff37e337SSujith } 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 502f078f209SLuis 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) 506b77f483fSSujith ath9k_hw_txstart(ah, sc->beacon.beaconq); 507f078f209SLuis R. Rodriguez } 508f078f209SLuis R. Rodriguez } 509f078f209SLuis R. Rodriguez 5101a6404a1SSujith Manoharan /* 5111a6404a1SSujith Manoharan * Both nexttbtt and intval have to be in usecs. 5121a6404a1SSujith Manoharan */ 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 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 536f078f209SLuis R. Rodriguez /* 5375379c8a2SSujith * For multi-bss ap support beacons are either staggered evenly over N slots or 5385379c8a2SSujith * burst together. For the former arrange for the SWBA to be delivered for each 5395379c8a2SSujith * slot. Slots that are not occupied will generate nothing. 540f078f209SLuis R. Rodriguez */ 541ef4ad633SSujith Manoharan static void ath9k_beacon_config_ap(struct ath_softc *sc, 542d31e20afSVasanthakumar Thiagarajan struct ath_beacon_config *conf) 543f078f209SLuis R. Rodriguez { 5443069168cSPavel Roskin struct ath_hw *ah = sc->sc_ah; 545f078f209SLuis R. Rodriguez 546fa7b52faSOleksij Rempel ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF); 547cfda2d8eSBenjamin Berg ath9k_beacon_init(sc, conf->nexttbtt, conf->intval); 548f078f209SLuis R. Rodriguez } 549f078f209SLuis R. Rodriguez 550cbbdf2aeSOleksij Rempel static void ath9k_beacon_config_sta(struct ath_hw *ah, 551d31e20afSVasanthakumar Thiagarajan struct ath_beacon_config *conf) 5525379c8a2SSujith { 553f078f209SLuis R. Rodriguez struct ath9k_beacon_state bs; 5545379c8a2SSujith 555cbbdf2aeSOleksij Rempel if (ath9k_cmn_beacon_config_sta(ah, conf, &bs) == -EPERM) 5561a20034aSSenthil Balasubramanian return; 5579fc9ab0aSSujith 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 } 5655379c8a2SSujith 566ef4ad633SSujith Manoharan static void ath9k_beacon_config_adhoc(struct ath_softc *sc, 567ee832d3eSMohammed Shafi Shajakhan struct ath_beacon_config *conf) 5685379c8a2SSujith { 5693069168cSPavel Roskin struct ath_hw *ah = sc->sc_ah; 5703069168cSPavel Roskin struct ath_common *common = ath9k_hw_common(ah); 5715379c8a2SSujith 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 } 5855379c8a2SSujith 586ef4ad633SSujith Manoharan static void ath9k_cache_beacon_config(struct ath_softc *sc, 5879a9c4fbcSRajkumar Manoharan struct ath_chanctx *ctx, 588ef4ad633SSujith Manoharan struct ieee80211_bss_conf *bss_conf) 58999e4d43aSRajkumar Manoharan { 590ef4ad633SSujith Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 5919a9c4fbcSRajkumar Manoharan struct ath_beacon_config *cur_conf = &ctx->beacon; 59299e4d43aSRajkumar Manoharan 593ef4ad633SSujith Manoharan ath_dbg(common, BEACON, 594ef4ad633SSujith Manoharan "Caching beacon data for BSS: %pM\n", bss_conf->bssid); 59599e4d43aSRajkumar Manoharan 59699e4d43aSRajkumar Manoharan cur_conf->beacon_interval = bss_conf->beacon_int; 59799e4d43aSRajkumar Manoharan cur_conf->dtim_period = bss_conf->dtim_period; 5986b96f93eSVasanthakumar Thiagarajan cur_conf->dtim_count = 1; 5991a6404a1SSujith Manoharan cur_conf->ibss_creator = bss_conf->ibss_creator; 6006b96f93eSVasanthakumar Thiagarajan 601c4f9f16bSVasanthakumar Thiagarajan /* 602c4f9f16bSVasanthakumar Thiagarajan * It looks like mac80211 may end up using beacon interval of zero in 603c4f9f16bSVasanthakumar Thiagarajan * some cases (at least for mesh point). Avoid getting into an 604c4f9f16bSVasanthakumar Thiagarajan * infinite loop by using a bit safer value instead. To be safe, 605c4f9f16bSVasanthakumar Thiagarajan * do sanity check on beacon interval for all operating modes. 606c4f9f16bSVasanthakumar Thiagarajan */ 607c4f9f16bSVasanthakumar Thiagarajan if (cur_conf->beacon_interval == 0) 608c4f9f16bSVasanthakumar Thiagarajan cur_conf->beacon_interval = 100; 6096b96f93eSVasanthakumar Thiagarajan 61076c93983SBen Greear cur_conf->bmiss_timeout = 61176c93983SBen Greear ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; 61276c93983SBen Greear 613ee832d3eSMohammed Shafi Shajakhan /* 6143a2329f2SMohammed Shafi Shajakhan * We don't parse dtim period from mac80211 during the driver 6153a2329f2SMohammed Shafi Shajakhan * initialization as it breaks association with hidden-ssid 6163a2329f2SMohammed Shafi Shajakhan * AP and it causes latency in roaming 617ee832d3eSMohammed Shafi Shajakhan */ 618ee832d3eSMohammed Shafi Shajakhan if (cur_conf->dtim_period == 0) 619ee832d3eSMohammed Shafi Shajakhan cur_conf->dtim_period = 1; 620ee832d3eSMohammed Shafi Shajakhan 621cfda2d8eSBenjamin Berg ath9k_set_tsfadjust(sc, cur_conf); 62299e4d43aSRajkumar Manoharan } 62399e4d43aSRajkumar Manoharan 624cfda2d8eSBenjamin Berg void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif, 625cfda2d8eSBenjamin Berg bool beacons) 6268e22ad32SRajkumar Manoharan { 627eefa01ddSOleksij Rempel struct ath_hw *ah = sc->sc_ah; 628eefa01ddSOleksij Rempel struct ath_common *common = ath9k_hw_common(ah); 629cfda2d8eSBenjamin Berg struct ath_vif *avp; 630cfda2d8eSBenjamin Berg struct ath_chanctx *ctx; 6319a9c4fbcSRajkumar Manoharan struct ath_beacon_config *cur_conf; 6321a6404a1SSujith Manoharan unsigned long flags; 633cfda2d8eSBenjamin Berg bool enabled; 6341a6404a1SSujith Manoharan bool skip_beacon = false; 6358e22ad32SRajkumar Manoharan 636cfda2d8eSBenjamin Berg if (!beacons) { 637cfda2d8eSBenjamin Berg clear_bit(ATH_OP_BEACONS, &common->op_flags); 638cfda2d8eSBenjamin Berg ath9k_beacon_stop(sc); 639cfda2d8eSBenjamin Berg return; 640cfda2d8eSBenjamin Berg } 641cfda2d8eSBenjamin Berg 642cfda2d8eSBenjamin Berg if (WARN_ON(!main_vif)) 6439a9c4fbcSRajkumar Manoharan return; 6449a9c4fbcSRajkumar Manoharan 645cfda2d8eSBenjamin Berg avp = (void *)main_vif->drv_priv; 646cfda2d8eSBenjamin Berg ctx = avp->chanctx; 647cfda2d8eSBenjamin Berg cur_conf = &ctx->beacon; 648cfda2d8eSBenjamin Berg enabled = cur_conf->enable_beacon; 649cfda2d8eSBenjamin Berg cur_conf->enable_beacon = beacons; 650c32e4e51SFelix Fietkau 651cfda2d8eSBenjamin Berg if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { 652cfda2d8eSBenjamin Berg ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf); 6539a9c4fbcSRajkumar Manoharan 654ef4ad633SSujith Manoharan ath9k_set_beacon(sc); 655eefa01ddSOleksij Rempel set_bit(ATH_OP_BEACONS, &common->op_flags); 6561a6404a1SSujith Manoharan return; 6571a6404a1SSujith Manoharan } 6581a6404a1SSujith Manoharan 659cfda2d8eSBenjamin Berg /* Update the beacon configuration. */ 660cfda2d8eSBenjamin Berg ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf); 6619a9c4fbcSRajkumar Manoharan 6621a6404a1SSujith Manoharan /* 6631a6404a1SSujith Manoharan * Configure the HW beacon registers only when we have a valid 6641a6404a1SSujith Manoharan * beacon interval. 6651a6404a1SSujith Manoharan */ 666ef4ad633SSujith Manoharan if (cur_conf->beacon_interval) { 667cfda2d8eSBenjamin Berg /* Special case to sync the TSF when joining an existing IBSS. 668cfda2d8eSBenjamin Berg * This is only done if no AP interface is active. 669cfda2d8eSBenjamin Berg * Note that mac80211 always resets the TSF when creating a new 670cfda2d8eSBenjamin Berg * IBSS interface. 6711a6404a1SSujith Manoharan */ 672cfda2d8eSBenjamin Berg if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC && 673cfda2d8eSBenjamin Berg !enabled && beacons && !main_vif->bss_conf.ibss_creator) { 6741a6404a1SSujith Manoharan spin_lock_irqsave(&sc->sc_pm_lock, flags); 6751a6404a1SSujith Manoharan sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; 6761a6404a1SSujith Manoharan spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 6771a6404a1SSujith Manoharan skip_beacon = true; 6781a6404a1SSujith Manoharan } 6798e22ad32SRajkumar Manoharan 6801a6404a1SSujith Manoharan /* 681eefa01ddSOleksij Rempel * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode 6821a6404a1SSujith Manoharan * here, it is done in ath9k_beacon_config_adhoc(). 6831a6404a1SSujith Manoharan */ 684cfda2d8eSBenjamin Berg if (beacons && !skip_beacon) { 685eefa01ddSOleksij Rempel set_bit(ATH_OP_BEACONS, &common->op_flags); 686cfda2d8eSBenjamin Berg ath9k_set_beacon(sc); 687cfda2d8eSBenjamin Berg } else { 688eefa01ddSOleksij Rempel clear_bit(ATH_OP_BEACONS, &common->op_flags); 689cfda2d8eSBenjamin Berg ath9k_beacon_stop(sc); 690cfda2d8eSBenjamin Berg } 691cfda2d8eSBenjamin Berg } else { 692cfda2d8eSBenjamin Berg clear_bit(ATH_OP_BEACONS, &common->op_flags); 693cfda2d8eSBenjamin Berg ath9k_beacon_stop(sc); 694ef4ad633SSujith Manoharan } 695ef4ad633SSujith Manoharan } 696ef4ad633SSujith Manoharan 697ef4ad633SSujith Manoharan void ath9k_set_beacon(struct ath_softc *sc) 69899e4d43aSRajkumar Manoharan { 69999e4d43aSRajkumar Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 700ca900ac9SRajkumar Manoharan struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; 70199e4d43aSRajkumar Manoharan 70226cd322bSFelix Fietkau switch (sc->sc_ah->opmode) { 7035379c8a2SSujith case NL80211_IFTYPE_AP: 7042664d666SThomas Pedersen case NL80211_IFTYPE_MESH_POINT: 705ef4ad633SSujith Manoharan ath9k_beacon_config_ap(sc, cur_conf); 7065379c8a2SSujith break; 7075379c8a2SSujith case NL80211_IFTYPE_ADHOC: 708ef4ad633SSujith Manoharan ath9k_beacon_config_adhoc(sc, cur_conf); 7095379c8a2SSujith break; 7105379c8a2SSujith case NL80211_IFTYPE_STATION: 711cbbdf2aeSOleksij Rempel ath9k_beacon_config_sta(sc->sc_ah, cur_conf); 7125379c8a2SSujith break; 7135379c8a2SSujith default: 714d2182b69SJoe Perches ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); 7155379c8a2SSujith return; 716f078f209SLuis R. Rodriguez } 717014cf3bbSRajkumar Manoharan } 718