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; 53d202caffSVivek Natarajan if (ah->slottime == ATH9K_SLOT_TIME_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; 81545750d3SFelix Fietkau u8 rate = 0; 82f078f209SLuis R. Rodriguez 83675a0b04SKarl Beldan sband = &sc->sbands[common->hw->conf.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; 91493cf04fSFelix Fietkau info.txpower = MAX_RATE_POWER; 92493cf04fSFelix Fietkau info.keyix = ATH9K_TXKEYIX_INVALID; 93493cf04fSFelix Fietkau info.keytype = ATH9K_KEY_TYPE_CLEAR; 94cd484aebSRajkumar Manoharan info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK; 95f078f209SLuis R. Rodriguez 96493cf04fSFelix Fietkau info.buf_addr[0] = bf->bf_buf_addr; 97493cf04fSFelix Fietkau info.buf_len[0] = roundup(skb->len, 4); 98f078f209SLuis R. Rodriguez 99493cf04fSFelix Fietkau info.is_first = true; 100493cf04fSFelix Fietkau info.is_last = true; 101493cf04fSFelix Fietkau 102493cf04fSFelix Fietkau info.qcu = sc->beacon.beaconq; 103493cf04fSFelix Fietkau 104493cf04fSFelix Fietkau info.rates[0].Tries = 1; 105493cf04fSFelix Fietkau info.rates[0].Rate = rate; 106493cf04fSFelix Fietkau info.rates[0].ChSel = ath_txchainmask_reduction(sc, chainmask, rate); 107493cf04fSFelix Fietkau 108493cf04fSFelix Fietkau ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); 109f078f209SLuis R. Rodriguez } 110f078f209SLuis R. Rodriguez 111fb6e252fSSujith Manoharan static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, 1122c3db3d5SJouni Malinen struct ieee80211_vif *vif) 113f078f209SLuis R. Rodriguez { 1149ac58615SFelix Fietkau struct ath_softc *sc = hw->priv; 115c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 116f078f209SLuis R. Rodriguez struct ath_buf *bf; 117aa45fe96SSujith Manoharan struct ath_vif *avp = (void *)vif->drv_priv; 118f078f209SLuis R. Rodriguez struct sk_buff *skb; 119aa45fe96SSujith Manoharan struct ath_txq *cabq = sc->beacon.cabq; 120147583c0SJouni Malinen struct ieee80211_tx_info *info; 121fb6e252fSSujith Manoharan struct ieee80211_mgmt *mgmt_hdr; 122980b24daSSujith int cabq_depth; 123980b24daSSujith 124aa45fe96SSujith Manoharan if (avp->av_bcbuf == NULL) 125f078f209SLuis R. Rodriguez return NULL; 126980b24daSSujith 127f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 128a22be22aSSujith skb = bf->bf_mpdu; 129a8fff50eSJouni Malinen if (skb) { 130c1739eb3SBen Greear dma_unmap_single(sc->dev, bf->bf_buf_addr, 1319fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 1323fbb9d95SJouni Malinen dev_kfree_skb_any(skb); 1336cf9e995SBen Greear bf->bf_buf_addr = 0; 1341adb2e2bSFelix Fietkau bf->bf_mpdu = NULL; 135a8fff50eSJouni Malinen } 136f078f209SLuis R. Rodriguez 137c52f33d0SJouni Malinen skb = ieee80211_beacon_get(hw, vif); 138a8fff50eSJouni Malinen if (skb == NULL) 139a8fff50eSJouni Malinen return NULL; 140fb6e252fSSujith Manoharan 141fb6e252fSSujith Manoharan bf->bf_mpdu = skb; 142fb6e252fSSujith Manoharan 143fb6e252fSSujith Manoharan mgmt_hdr = (struct ieee80211_mgmt *)skb->data; 144fb6e252fSSujith Manoharan mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust; 145980b24daSSujith 146147583c0SJouni Malinen info = IEEE80211_SKB_CB(skb); 147147583c0SJouni Malinen if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { 148147583c0SJouni Malinen /* 149147583c0SJouni Malinen * TODO: make sure the seq# gets assigned properly (vs. other 150147583c0SJouni Malinen * TX frames) 151147583c0SJouni Malinen */ 152147583c0SJouni Malinen struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 153b77f483fSSujith sc->tx.seq_no += 0x10; 154147583c0SJouni Malinen hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); 155b77f483fSSujith hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); 156147583c0SJouni Malinen } 157980b24daSSujith 158c1739eb3SBen Greear bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, 1599fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 1607da3c55cSGabor Juhos if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { 161f8316df1SLuis R. Rodriguez dev_kfree_skb_any(skb); 162f8316df1SLuis R. Rodriguez bf->bf_mpdu = NULL; 1636cf9e995SBen Greear bf->bf_buf_addr = 0; 1643800276aSJoe Perches ath_err(common, "dma_mapping_error on beaconing\n"); 165f8316df1SLuis R. Rodriguez return NULL; 166f8316df1SLuis R. Rodriguez } 167f078f209SLuis R. Rodriguez 168c52f33d0SJouni Malinen skb = ieee80211_get_buffered_bc(hw, vif); 169f078f209SLuis R. Rodriguez 170f078f209SLuis R. Rodriguez /* 171f078f209SLuis R. Rodriguez * if the CABQ traffic from previous DTIM is pending and the current 172f078f209SLuis R. Rodriguez * beacon is also a DTIM. 17317d7904dSSujith * 1) if there is only one vif let the cab traffic continue. 17417d7904dSSujith * 2) if there are more than one vif and we are using staggered 175f078f209SLuis R. Rodriguez * beacons, then drain the cabq by dropping all the frames in 17617d7904dSSujith * the cabq so that the current vifs cab traffic can be scheduled. 177f078f209SLuis R. Rodriguez */ 178f078f209SLuis R. Rodriguez spin_lock_bh(&cabq->axq_lock); 179f078f209SLuis R. Rodriguez cabq_depth = cabq->axq_depth; 180f078f209SLuis R. Rodriguez spin_unlock_bh(&cabq->axq_lock); 181f078f209SLuis R. Rodriguez 182e022edbdSJouni Malinen if (skb && cabq_depth) { 18317d7904dSSujith if (sc->nvifs > 1) { 184d2182b69SJoe Perches ath_dbg(common, BEACON, 1859fc9ab0aSSujith "Flushing previous cabq traffic\n"); 1861381559bSFelix Fietkau ath_draintxq(sc, cabq); 187f078f209SLuis R. Rodriguez } 188f078f209SLuis R. Rodriguez } 189f078f209SLuis R. Rodriguez 190fb6e252fSSujith Manoharan ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx); 191f078f209SLuis R. Rodriguez 192*59505c02SFelix Fietkau if (skb) 193*59505c02SFelix Fietkau ath_tx_cabq(hw, vif, skb); 194f078f209SLuis R. Rodriguez 195f078f209SLuis R. Rodriguez return bf; 196f078f209SLuis R. Rodriguez } 197f078f209SLuis R. Rodriguez 198130ef6e9SSujith Manoharan void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif) 199f078f209SLuis R. Rodriguez { 200c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 201130ef6e9SSujith Manoharan struct ath_vif *avp = (void *)vif->drv_priv; 202130ef6e9SSujith Manoharan int slot; 203f078f209SLuis R. Rodriguez 204130ef6e9SSujith Manoharan avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list); 205f078f209SLuis R. Rodriguez list_del(&avp->av_bcbuf->list); 206f078f209SLuis R. Rodriguez 207130ef6e9SSujith Manoharan for (slot = 0; slot < ATH_BCBUF; slot++) { 2082c3db3d5SJouni Malinen if (sc->beacon.bslot[slot] == NULL) { 209f078f209SLuis R. Rodriguez avp->av_bslot = slot; 210774610e4SFelix Fietkau break; 211f078f209SLuis R. Rodriguez } 212130ef6e9SSujith Manoharan } 213130ef6e9SSujith Manoharan 2142c3db3d5SJouni Malinen sc->beacon.bslot[avp->av_bslot] = vif; 21517d7904dSSujith sc->nbcnvifs++; 216130ef6e9SSujith Manoharan 217130ef6e9SSujith Manoharan ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n", 218130ef6e9SSujith Manoharan avp->av_bslot); 219f078f209SLuis R. Rodriguez } 220f078f209SLuis R. Rodriguez 221130ef6e9SSujith Manoharan void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) 222f078f209SLuis R. Rodriguez { 223130ef6e9SSujith Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 224130ef6e9SSujith Manoharan struct ath_vif *avp = (void *)vif->drv_priv; 225130ef6e9SSujith Manoharan struct ath_buf *bf = avp->av_bcbuf; 226f078f209SLuis R. Rodriguez 227130ef6e9SSujith Manoharan ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n", 228130ef6e9SSujith Manoharan avp->av_bslot); 229f078f209SLuis R. Rodriguez 230130ef6e9SSujith Manoharan tasklet_disable(&sc->bcon_tasklet); 231130ef6e9SSujith Manoharan 232130ef6e9SSujith Manoharan if (bf && bf->bf_mpdu) { 233a22be22aSSujith struct sk_buff *skb = bf->bf_mpdu; 234c1739eb3SBen Greear dma_unmap_single(sc->dev, bf->bf_buf_addr, 2359fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 236f078f209SLuis R. Rodriguez dev_kfree_skb_any(skb); 237f078f209SLuis R. Rodriguez bf->bf_mpdu = NULL; 2386cf9e995SBen Greear bf->bf_buf_addr = 0; 239f078f209SLuis R. Rodriguez } 240f078f209SLuis R. Rodriguez 241f078f209SLuis R. Rodriguez avp->av_bcbuf = NULL; 242130ef6e9SSujith Manoharan sc->beacon.bslot[avp->av_bslot] = NULL; 243130ef6e9SSujith Manoharan sc->nbcnvifs--; 244130ef6e9SSujith Manoharan list_add_tail(&bf->list, &sc->beacon.bbuf); 245130ef6e9SSujith Manoharan 246130ef6e9SSujith Manoharan tasklet_enable(&sc->bcon_tasklet); 247f078f209SLuis R. Rodriguez } 248f078f209SLuis R. Rodriguez 249fb6e252fSSujith Manoharan static int ath9k_beacon_choose_slot(struct ath_softc *sc) 250fb6e252fSSujith Manoharan { 251fb6e252fSSujith Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 252fb6e252fSSujith Manoharan struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; 253fb6e252fSSujith Manoharan u16 intval; 254fb6e252fSSujith Manoharan u32 tsftu; 255fb6e252fSSujith Manoharan u64 tsf; 256fb6e252fSSujith Manoharan int slot; 257fb6e252fSSujith Manoharan 2582664d666SThomas Pedersen if (sc->sc_ah->opmode != NL80211_IFTYPE_AP && 2592664d666SThomas Pedersen sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) { 260fb6e252fSSujith Manoharan ath_dbg(common, BEACON, "slot 0, tsf: %llu\n", 261fb6e252fSSujith Manoharan ath9k_hw_gettsf64(sc->sc_ah)); 262fb6e252fSSujith Manoharan return 0; 263fb6e252fSSujith Manoharan } 264fb6e252fSSujith Manoharan 265fb6e252fSSujith Manoharan intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL; 266fb6e252fSSujith Manoharan tsf = ath9k_hw_gettsf64(sc->sc_ah); 267fb6e252fSSujith Manoharan tsf += TU_TO_USEC(sc->sc_ah->config.sw_beacon_response_time); 268fb6e252fSSujith Manoharan tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF); 269fb6e252fSSujith Manoharan slot = (tsftu % (intval * ATH_BCBUF)) / intval; 270fb6e252fSSujith Manoharan 271fb6e252fSSujith Manoharan ath_dbg(common, BEACON, "slot: %d tsf: %llu tsftu: %u\n", 272fb6e252fSSujith Manoharan slot, tsf, tsftu / ATH_BCBUF); 273fb6e252fSSujith Manoharan 274fb6e252fSSujith Manoharan return slot; 275fb6e252fSSujith Manoharan } 276fb6e252fSSujith Manoharan 2772f8e82e8SSujith Manoharan void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) 2782f8e82e8SSujith Manoharan { 2792f8e82e8SSujith Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 2802f8e82e8SSujith Manoharan struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; 2812f8e82e8SSujith Manoharan struct ath_vif *avp = (void *)vif->drv_priv; 2822f8e82e8SSujith Manoharan u64 tsfadjust; 2832f8e82e8SSujith Manoharan 2842f8e82e8SSujith Manoharan if (avp->av_bslot == 0) 2852f8e82e8SSujith Manoharan return; 2862f8e82e8SSujith Manoharan 2872f8e82e8SSujith Manoharan tsfadjust = cur_conf->beacon_interval * avp->av_bslot / ATH_BCBUF; 2882f8e82e8SSujith Manoharan avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); 2892f8e82e8SSujith Manoharan 2902f8e82e8SSujith Manoharan ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n", 2912f8e82e8SSujith Manoharan (unsigned long long)tsfadjust, avp->av_bslot); 2922f8e82e8SSujith Manoharan } 2932f8e82e8SSujith Manoharan 294fb6e252fSSujith Manoharan void ath9k_beacon_tasklet(unsigned long data) 295f078f209SLuis R. Rodriguez { 296f078f209SLuis R. Rodriguez struct ath_softc *sc = (struct ath_softc *)data; 297cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 298c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(ah); 299f078f209SLuis R. Rodriguez struct ath_buf *bf = NULL; 3002c3db3d5SJouni Malinen struct ieee80211_vif *vif; 30198f0a5ebSMohammed Shafi Shajakhan bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); 3022c3db3d5SJouni Malinen int slot; 303f078f209SLuis R. Rodriguez 304124b979bSRajkumar Manoharan if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) { 3054e7fb718SRajkumar Manoharan ath_dbg(common, RESET, 3064e7fb718SRajkumar Manoharan "reset work is pending, skip beaconing now\n"); 3074e7fb718SRajkumar Manoharan return; 3084e7fb718SRajkumar Manoharan } 309124b979bSRajkumar Manoharan 310f078f209SLuis R. Rodriguez /* 311f078f209SLuis R. Rodriguez * Check if the previous beacon has gone out. If 312f078f209SLuis R. Rodriguez * not don't try to post another, skip this period 313f078f209SLuis R. Rodriguez * and wait for the next. Missed beacons indicate 314f078f209SLuis R. Rodriguez * a problem and should not occur. If we miss too 315f078f209SLuis R. Rodriguez * many consecutive beacons reset the device. 316f078f209SLuis R. Rodriguez */ 317b77f483fSSujith if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { 318b77f483fSSujith sc->beacon.bmisscnt++; 3199546aae0SSujith 320b381fa32SFelix Fietkau if (!ath9k_hw_check_alive(ah)) 321b381fa32SFelix Fietkau ieee80211_queue_work(sc->hw, &sc->hw_check_work); 322b381fa32SFelix Fietkau 323c944daf4SFelix Fietkau if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) { 324d2182b69SJoe Perches ath_dbg(common, BSTUCK, 32504bd4638SSujith "missed %u consecutive beacons\n", 326b77f483fSSujith sc->beacon.bmisscnt); 327efff395eSFelix Fietkau ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq); 32887c510feSFelix Fietkau if (sc->beacon.bmisscnt > 3) 32970cf1533SFelix Fietkau ath9k_hw_bstuck_nfcal(ah); 330b77f483fSSujith } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { 331d2182b69SJoe Perches ath_dbg(common, BSTUCK, "beacon is officially stuck\n"); 3324e7fb718SRajkumar Manoharan sc->beacon.bmisscnt = 0; 333124b979bSRajkumar Manoharan ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK); 334f078f209SLuis R. Rodriguez } 3359546aae0SSujith 336f078f209SLuis R. Rodriguez return; 337f078f209SLuis R. Rodriguez } 338980b24daSSujith 339fb6e252fSSujith Manoharan slot = ath9k_beacon_choose_slot(sc); 3404ed96f04SJouni Malinen vif = sc->beacon.bslot[slot]; 341980b24daSSujith 342fb6e252fSSujith Manoharan if (!vif || !vif->bss_conf.enable_beacon) 343fb6e252fSSujith Manoharan return; 344c6820f1eSFelix Fietkau 345fb6e252fSSujith Manoharan bf = ath9k_beacon_generate(sc->hw, vif); 346c944daf4SFelix Fietkau 347c944daf4SFelix Fietkau if (sc->beacon.bmisscnt != 0) { 348fb6e252fSSujith Manoharan ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n", 349c944daf4SFelix Fietkau sc->beacon.bmisscnt); 350c944daf4SFelix Fietkau sc->beacon.bmisscnt = 0; 351c944daf4SFelix Fietkau } 3529546aae0SSujith 353f078f209SLuis R. Rodriguez /* 354f078f209SLuis R. Rodriguez * Handle slot time change when a non-ERP station joins/leaves 355f078f209SLuis R. Rodriguez * an 11g network. The 802.11 layer notifies us via callback, 356f078f209SLuis R. Rodriguez * we mark updateslot, then wait one beacon before effecting 357f078f209SLuis R. Rodriguez * the change. This gives associated stations at least one 358f078f209SLuis R. Rodriguez * beacon interval to note the state change. 359f078f209SLuis R. Rodriguez * 360f078f209SLuis R. Rodriguez * NB: The slot time change state machine is clocked according 361f078f209SLuis R. Rodriguez * to whether we are bursting or staggering beacons. We 362f078f209SLuis R. Rodriguez * recognize the request to update and record the current 363f078f209SLuis R. Rodriguez * slot then don't transition until that slot is reached 364f078f209SLuis R. Rodriguez * again. If we miss a beacon for that slot then we'll be 365f078f209SLuis R. Rodriguez * slow to transition but we'll be sure at least one beacon 366f078f209SLuis R. Rodriguez * interval has passed. When bursting slot is always left 367f078f209SLuis R. Rodriguez * set to ATH_BCBUF so this check is a noop. 368f078f209SLuis R. Rodriguez */ 369b77f483fSSujith if (sc->beacon.updateslot == UPDATE) { 370fb6e252fSSujith Manoharan sc->beacon.updateslot = COMMIT; 371b77f483fSSujith sc->beacon.slotupdate = slot; 372fb6e252fSSujith Manoharan } else if (sc->beacon.updateslot == COMMIT && 373fb6e252fSSujith Manoharan sc->beacon.slotupdate == slot) { 3740005baf4SFelix Fietkau ah->slottime = sc->beacon.slottime; 3750005baf4SFelix Fietkau ath9k_hw_init_global_settings(ah); 376b77f483fSSujith sc->beacon.updateslot = OK; 377ff37e337SSujith } 378fb6e252fSSujith Manoharan 379fb6e252fSSujith Manoharan if (bf) { 380fb6e252fSSujith Manoharan ath9k_reset_beacon_status(sc); 381fb6e252fSSujith Manoharan 382fb6e252fSSujith Manoharan ath_dbg(common, BEACON, 383fb6e252fSSujith Manoharan "Transmitting beacon for slot: %d\n", slot); 384fb6e252fSSujith Manoharan 385f078f209SLuis R. Rodriguez /* NB: cabq traffic should already be queued and primed */ 386fb6e252fSSujith Manoharan ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); 38798f0a5ebSMohammed Shafi Shajakhan 38898f0a5ebSMohammed Shafi Shajakhan if (!edma) 389b77f483fSSujith ath9k_hw_txstart(ah, sc->beacon.beaconq); 390f078f209SLuis R. Rodriguez } 391f078f209SLuis R. Rodriguez } 392f078f209SLuis R. Rodriguez 3931a6404a1SSujith Manoharan /* 3941a6404a1SSujith Manoharan * Both nexttbtt and intval have to be in usecs. 3951a6404a1SSujith Manoharan */ 3961a6404a1SSujith Manoharan static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, 3971a6404a1SSujith Manoharan u32 intval, bool reset_tsf) 39821526d57SLuis R. Rodriguez { 399ef4ad633SSujith Manoharan struct ath_hw *ah = sc->sc_ah; 40021526d57SLuis R. Rodriguez 401ef4ad633SSujith Manoharan ath9k_hw_disable_interrupts(ah); 4021a6404a1SSujith Manoharan if (reset_tsf) 403ef4ad633SSujith Manoharan ath9k_hw_reset_tsf(ah); 4047e52c8aaSSujith Manoharan ath9k_beaconq_config(sc); 405ef4ad633SSujith Manoharan ath9k_hw_beaconinit(ah, nexttbtt, intval); 406ef4ad633SSujith Manoharan sc->beacon.bmisscnt = 0; 407ef4ad633SSujith Manoharan ath9k_hw_set_interrupts(ah); 408ef4ad633SSujith Manoharan ath9k_hw_enable_interrupts(ah); 40921526d57SLuis R. Rodriguez } 41021526d57SLuis R. Rodriguez 411f078f209SLuis R. Rodriguez /* 4125379c8a2SSujith * For multi-bss ap support beacons are either staggered evenly over N slots or 4135379c8a2SSujith * burst together. For the former arrange for the SWBA to be delivered for each 4145379c8a2SSujith * slot. Slots that are not occupied will generate nothing. 415f078f209SLuis R. Rodriguez */ 416ef4ad633SSujith Manoharan static void ath9k_beacon_config_ap(struct ath_softc *sc, 417d31e20afSVasanthakumar Thiagarajan struct ath_beacon_config *conf) 418f078f209SLuis R. Rodriguez { 4193069168cSPavel Roskin struct ath_hw *ah = sc->sc_ah; 420ef4ad633SSujith Manoharan struct ath_common *common = ath9k_hw_common(ah); 421980b24daSSujith u32 nexttbtt, intval; 422f078f209SLuis R. Rodriguez 423f078f209SLuis R. Rodriguez /* NB: the beacon interval is kept internally in TU's */ 424f29f5c08SRajkumar Manoharan intval = TU_TO_USEC(conf->beacon_interval); 425ef4ad633SSujith Manoharan intval /= ATH_BCBUF; 4265379c8a2SSujith nexttbtt = intval; 427d8728ee9SFelix Fietkau 428ef4ad633SSujith Manoharan if (conf->enable_beacon) 4293069168cSPavel Roskin ah->imask |= ATH9K_INT_SWBA; 430ef4ad633SSujith Manoharan else 431ef4ad633SSujith Manoharan ah->imask &= ~ATH9K_INT_SWBA; 432ef4ad633SSujith Manoharan 4331a6404a1SSujith Manoharan ath_dbg(common, BEACON, 4341a6404a1SSujith Manoharan "AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n", 4351a6404a1SSujith Manoharan (conf->enable_beacon) ? "Enable" : "Disable", 436ef4ad633SSujith Manoharan nexttbtt, intval, conf->beacon_interval); 437ef4ad633SSujith Manoharan 4381a6404a1SSujith Manoharan ath9k_beacon_init(sc, nexttbtt, intval, true); 439f078f209SLuis R. Rodriguez } 440f078f209SLuis R. Rodriguez 4415379c8a2SSujith /* 4425379c8a2SSujith * This sets up the beacon timers according to the timestamp of the last 4435379c8a2SSujith * received beacon and the current TSF, configures PCF and DTIM 4445379c8a2SSujith * handling, programs the sleep registers so the hardware will wakeup in 4455379c8a2SSujith * time to receive beacons, and configures the beacon miss handling so 4465379c8a2SSujith * we'll receive a BMISS interrupt when we stop seeing beacons from the AP 4475379c8a2SSujith * we've associated with. 4485379c8a2SSujith */ 449ef4ad633SSujith Manoharan static void ath9k_beacon_config_sta(struct ath_softc *sc, 450d31e20afSVasanthakumar Thiagarajan struct ath_beacon_config *conf) 4515379c8a2SSujith { 4523069168cSPavel Roskin struct ath_hw *ah = sc->sc_ah; 4533069168cSPavel Roskin struct ath_common *common = ath9k_hw_common(ah); 454f078f209SLuis R. Rodriguez struct ath9k_beacon_state bs; 455f078f209SLuis R. Rodriguez int dtimperiod, dtimcount, sleepduration; 456f078f209SLuis R. Rodriguez int cfpperiod, cfpcount; 4575379c8a2SSujith u32 nexttbtt = 0, intval, tsftu; 4585379c8a2SSujith u64 tsf; 459267a9012SJouni Malinen int num_beacons, offset, dtim_dec_count, cfp_dec_count; 4605379c8a2SSujith 4611a20034aSSenthil Balasubramanian /* No need to configure beacon if we are not associated */ 4626c43c090SSujith Manoharan if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { 463d2182b69SJoe Perches ath_dbg(common, BEACON, 4641a20034aSSenthil Balasubramanian "STA is not yet associated..skipping beacon config\n"); 4651a20034aSSenthil Balasubramanian return; 4661a20034aSSenthil Balasubramanian } 4671a20034aSSenthil Balasubramanian 4685379c8a2SSujith memset(&bs, 0, sizeof(bs)); 469f29f5c08SRajkumar Manoharan intval = conf->beacon_interval; 470f078f209SLuis R. Rodriguez 471f078f209SLuis R. Rodriguez /* 472f078f209SLuis R. Rodriguez * Setup dtim and cfp parameters according to 473f078f209SLuis R. Rodriguez * last beacon we received (which may be none). 474f078f209SLuis R. Rodriguez */ 4755379c8a2SSujith dtimperiod = conf->dtim_period; 4765379c8a2SSujith dtimcount = conf->dtim_count; 477f078f209SLuis R. Rodriguez if (dtimcount >= dtimperiod) /* NB: sanity check */ 478980b24daSSujith dtimcount = 0; 479f078f209SLuis R. Rodriguez cfpperiod = 1; /* NB: no PCF support yet */ 480f078f209SLuis R. Rodriguez cfpcount = 0; 481f078f209SLuis R. Rodriguez 4825379c8a2SSujith sleepduration = conf->listen_interval * intval; 483f078f209SLuis R. Rodriguez 484f078f209SLuis R. Rodriguez /* 485f078f209SLuis R. Rodriguez * Pull nexttbtt forward to reflect the current 486f078f209SLuis R. Rodriguez * TSF and calculate dtim+cfp state for the result. 487f078f209SLuis R. Rodriguez */ 4883069168cSPavel Roskin tsf = ath9k_hw_gettsf64(ah); 489f078f209SLuis R. Rodriguez tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 490267a9012SJouni Malinen 491267a9012SJouni Malinen num_beacons = tsftu / intval + 1; 492267a9012SJouni Malinen offset = tsftu % intval; 493267a9012SJouni Malinen nexttbtt = tsftu - offset; 494267a9012SJouni Malinen if (offset) 495f078f209SLuis R. Rodriguez nexttbtt += intval; 496267a9012SJouni Malinen 497267a9012SJouni Malinen /* DTIM Beacon every dtimperiod Beacon */ 498267a9012SJouni Malinen dtim_dec_count = num_beacons % dtimperiod; 499267a9012SJouni Malinen /* CFP every cfpperiod DTIM Beacon */ 500267a9012SJouni Malinen cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; 501267a9012SJouni Malinen if (dtim_dec_count) 502267a9012SJouni Malinen cfp_dec_count++; 503267a9012SJouni Malinen 504267a9012SJouni Malinen dtimcount -= dtim_dec_count; 505267a9012SJouni Malinen if (dtimcount < 0) 506267a9012SJouni Malinen dtimcount += dtimperiod; 507267a9012SJouni Malinen 508267a9012SJouni Malinen cfpcount -= cfp_dec_count; 509267a9012SJouni Malinen if (cfpcount < 0) 510267a9012SJouni Malinen cfpcount += cfpperiod; 5115379c8a2SSujith 512f078f209SLuis R. Rodriguez bs.bs_intval = intval; 513f078f209SLuis R. Rodriguez bs.bs_nexttbtt = nexttbtt; 514f078f209SLuis R. Rodriguez bs.bs_dtimperiod = dtimperiod*intval; 515f078f209SLuis R. Rodriguez bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 516f078f209SLuis R. Rodriguez bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 517f078f209SLuis R. Rodriguez bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 518f078f209SLuis R. Rodriguez bs.bs_cfpmaxduration = 0; 519980b24daSSujith 520f078f209SLuis R. Rodriguez /* 5215379c8a2SSujith * Calculate the number of consecutive beacons to miss* before taking 5225379c8a2SSujith * a BMISS interrupt. The configuration is specified in TU so we only 5235379c8a2SSujith * need calculate based on the beacon interval. Note that we clamp the 524f078f209SLuis R. Rodriguez * result to at most 15 beacons. 525f078f209SLuis R. Rodriguez */ 526f078f209SLuis R. Rodriguez if (sleepduration > intval) { 5275379c8a2SSujith bs.bs_bmissthreshold = conf->listen_interval * 528f078f209SLuis R. Rodriguez ATH_DEFAULT_BMISS_LIMIT / 2; 529f078f209SLuis R. Rodriguez } else { 5305379c8a2SSujith bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval); 531f078f209SLuis R. Rodriguez if (bs.bs_bmissthreshold > 15) 532f078f209SLuis R. Rodriguez bs.bs_bmissthreshold = 15; 533f078f209SLuis R. Rodriguez else if (bs.bs_bmissthreshold <= 0) 534f078f209SLuis R. Rodriguez bs.bs_bmissthreshold = 1; 535f078f209SLuis R. Rodriguez } 536f078f209SLuis R. Rodriguez 537f078f209SLuis R. Rodriguez /* 5385379c8a2SSujith * Calculate sleep duration. The configuration is given in ms. 5395379c8a2SSujith * We ensure a multiple of the beacon period is used. Also, if the sleep 5405379c8a2SSujith * duration is greater than the DTIM period then it makes senses 541f078f209SLuis R. Rodriguez * to make it a multiple of that. 542f078f209SLuis R. Rodriguez * 543f078f209SLuis R. Rodriguez * XXX fixed at 100ms 544f078f209SLuis R. Rodriguez */ 545f078f209SLuis R. Rodriguez 5465379c8a2SSujith bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); 547f078f209SLuis R. Rodriguez if (bs.bs_sleepduration > bs.bs_dtimperiod) 548f078f209SLuis R. Rodriguez bs.bs_sleepduration = bs.bs_dtimperiod; 549f078f209SLuis R. Rodriguez 5504af9cf4fSSujith /* TSF out of range threshold fixed at 1 second */ 5514af9cf4fSSujith bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; 5524af9cf4fSSujith 553d2182b69SJoe Perches ath_dbg(common, BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); 554d2182b69SJoe Perches ath_dbg(common, BEACON, 5559fc9ab0aSSujith "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", 5569fc9ab0aSSujith bs.bs_bmissthreshold, bs.bs_sleepduration, 5579fc9ab0aSSujith bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); 558f078f209SLuis R. Rodriguez 5595379c8a2SSujith /* Set the computed STA beacon timers */ 5609fc9ab0aSSujith 5614df3071eSFelix Fietkau ath9k_hw_disable_interrupts(ah); 5623069168cSPavel Roskin ath9k_hw_set_sta_beacon_timers(ah, &bs); 5633069168cSPavel Roskin ah->imask |= ATH9K_INT_BMISS; 564deb75188SRajkumar Manoharan 56572d874c6SFelix Fietkau ath9k_hw_set_interrupts(ah); 566b037b693SRajkumar Manoharan ath9k_hw_enable_interrupts(ah); 567b037b693SRajkumar Manoharan } 5685379c8a2SSujith 569ef4ad633SSujith Manoharan static void ath9k_beacon_config_adhoc(struct ath_softc *sc, 570ee832d3eSMohammed Shafi Shajakhan struct ath_beacon_config *conf) 5715379c8a2SSujith { 5723069168cSPavel Roskin struct ath_hw *ah = sc->sc_ah; 5733069168cSPavel Roskin struct ath_common *common = ath9k_hw_common(ah); 574ef4ad633SSujith Manoharan u32 intval, nexttbtt; 5755379c8a2SSujith 576ba4903f9SFelix Fietkau ath9k_reset_beacon_status(sc); 577ba4903f9SFelix Fietkau 578f29f5c08SRajkumar Manoharan intval = TU_TO_USEC(conf->beacon_interval); 5791a6404a1SSujith Manoharan 5801a6404a1SSujith Manoharan if (conf->ibss_creator) { 581ef4ad633SSujith Manoharan nexttbtt = intval; 5821a6404a1SSujith Manoharan } else { 5831a6404a1SSujith Manoharan u32 tbtt, offset, tsftu; 5841a6404a1SSujith Manoharan u64 tsf; 5851a6404a1SSujith Manoharan 5861a6404a1SSujith Manoharan /* 5871a6404a1SSujith Manoharan * Pull nexttbtt forward to reflect the current 5881a6404a1SSujith Manoharan * sync'd TSF. 5891a6404a1SSujith Manoharan */ 5901a6404a1SSujith Manoharan tsf = ath9k_hw_gettsf64(ah); 5911a6404a1SSujith Manoharan tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; 5921a6404a1SSujith Manoharan offset = tsftu % conf->beacon_interval; 5931a6404a1SSujith Manoharan tbtt = tsftu - offset; 5941a6404a1SSujith Manoharan if (offset) 5951a6404a1SSujith Manoharan tbtt += conf->beacon_interval; 5961a6404a1SSujith Manoharan 5971a6404a1SSujith Manoharan nexttbtt = TU_TO_USEC(tbtt); 5981a6404a1SSujith Manoharan } 5995379c8a2SSujith 600ef4ad633SSujith Manoharan if (conf->enable_beacon) 601ef4ad633SSujith Manoharan ah->imask |= ATH9K_INT_SWBA; 602ef4ad633SSujith Manoharan else 603ef4ad633SSujith Manoharan ah->imask &= ~ATH9K_INT_SWBA; 604ef4ad633SSujith Manoharan 6051a6404a1SSujith Manoharan ath_dbg(common, BEACON, 6061a6404a1SSujith Manoharan "IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n", 6071a6404a1SSujith Manoharan (conf->enable_beacon) ? "Enable" : "Disable", 6085379c8a2SSujith nexttbtt, intval, conf->beacon_interval); 609f078f209SLuis R. Rodriguez 6101a6404a1SSujith Manoharan ath9k_beacon_init(sc, nexttbtt, intval, conf->ibss_creator); 6111a6404a1SSujith Manoharan 6121a6404a1SSujith Manoharan /* 6131a6404a1SSujith Manoharan * Set the global 'beacon has been configured' flag for the 6141a6404a1SSujith Manoharan * joiner case in IBSS mode. 6151a6404a1SSujith Manoharan */ 6161a6404a1SSujith Manoharan if (!conf->ibss_creator && conf->enable_beacon) 6171a6404a1SSujith Manoharan set_bit(SC_OP_BEACONS, &sc->sc_flags); 618b037b693SRajkumar Manoharan } 6195379c8a2SSujith 620ef4ad633SSujith Manoharan bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) 6215379c8a2SSujith { 622c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 62399e4d43aSRajkumar Manoharan struct ath_vif *avp = (void *)vif->drv_priv; 62499e4d43aSRajkumar Manoharan 625ef4ad633SSujith Manoharan if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { 626ef4ad633SSujith Manoharan if ((vif->type != NL80211_IFTYPE_AP) || 627ef4ad633SSujith Manoharan (sc->nbcnvifs > 1)) { 628d2182b69SJoe Perches ath_dbg(common, CONFIG, 629ef4ad633SSujith Manoharan "An AP interface is already present !\n"); 63099e4d43aSRajkumar Manoharan return false; 63199e4d43aSRajkumar Manoharan } 63299e4d43aSRajkumar Manoharan } 633ef4ad633SSujith Manoharan 634ef4ad633SSujith Manoharan if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { 635ef4ad633SSujith Manoharan if ((vif->type == NL80211_IFTYPE_STATION) && 636781b14a3SSujith Manoharan test_bit(SC_OP_BEACONS, &sc->sc_flags) && 63799e4d43aSRajkumar Manoharan !avp->primary_sta_vif) { 638d2182b69SJoe Perches ath_dbg(common, CONFIG, 63999e4d43aSRajkumar Manoharan "Beacon already configured for a station interface\n"); 64099e4d43aSRajkumar Manoharan return false; 64199e4d43aSRajkumar Manoharan } 642ef4ad633SSujith Manoharan } 643ef4ad633SSujith Manoharan 64499e4d43aSRajkumar Manoharan return true; 645ee832d3eSMohammed Shafi Shajakhan } 646ee832d3eSMohammed Shafi Shajakhan 647ef4ad633SSujith Manoharan static void ath9k_cache_beacon_config(struct ath_softc *sc, 648ef4ad633SSujith Manoharan struct ieee80211_bss_conf *bss_conf) 64999e4d43aSRajkumar Manoharan { 650ef4ad633SSujith Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 65199e4d43aSRajkumar Manoharan struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; 65299e4d43aSRajkumar Manoharan 653ef4ad633SSujith Manoharan ath_dbg(common, BEACON, 654ef4ad633SSujith Manoharan "Caching beacon data for BSS: %pM\n", bss_conf->bssid); 65599e4d43aSRajkumar Manoharan 65699e4d43aSRajkumar Manoharan cur_conf->beacon_interval = bss_conf->beacon_int; 65799e4d43aSRajkumar Manoharan cur_conf->dtim_period = bss_conf->dtim_period; 6586b96f93eSVasanthakumar Thiagarajan cur_conf->listen_interval = 1; 6596b96f93eSVasanthakumar Thiagarajan cur_conf->dtim_count = 1; 6601a6404a1SSujith Manoharan cur_conf->ibss_creator = bss_conf->ibss_creator; 6616b96f93eSVasanthakumar Thiagarajan cur_conf->bmiss_timeout = 6626b96f93eSVasanthakumar Thiagarajan ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; 6636b96f93eSVasanthakumar Thiagarajan 664c4f9f16bSVasanthakumar Thiagarajan /* 665c4f9f16bSVasanthakumar Thiagarajan * It looks like mac80211 may end up using beacon interval of zero in 666c4f9f16bSVasanthakumar Thiagarajan * some cases (at least for mesh point). Avoid getting into an 667c4f9f16bSVasanthakumar Thiagarajan * infinite loop by using a bit safer value instead. To be safe, 668c4f9f16bSVasanthakumar Thiagarajan * do sanity check on beacon interval for all operating modes. 669c4f9f16bSVasanthakumar Thiagarajan */ 670c4f9f16bSVasanthakumar Thiagarajan if (cur_conf->beacon_interval == 0) 671c4f9f16bSVasanthakumar Thiagarajan cur_conf->beacon_interval = 100; 6726b96f93eSVasanthakumar Thiagarajan 673ee832d3eSMohammed Shafi Shajakhan /* 6743a2329f2SMohammed Shafi Shajakhan * We don't parse dtim period from mac80211 during the driver 6753a2329f2SMohammed Shafi Shajakhan * initialization as it breaks association with hidden-ssid 6763a2329f2SMohammed Shafi Shajakhan * AP and it causes latency in roaming 677ee832d3eSMohammed Shafi Shajakhan */ 678ee832d3eSMohammed Shafi Shajakhan if (cur_conf->dtim_period == 0) 679ee832d3eSMohammed Shafi Shajakhan cur_conf->dtim_period = 1; 680ee832d3eSMohammed Shafi Shajakhan 68199e4d43aSRajkumar Manoharan } 68299e4d43aSRajkumar Manoharan 683ef4ad633SSujith Manoharan void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, 684ef4ad633SSujith Manoharan u32 changed) 6858e22ad32SRajkumar Manoharan { 686ef4ad633SSujith Manoharan struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 687ef4ad633SSujith Manoharan struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; 6881a6404a1SSujith Manoharan unsigned long flags; 6891a6404a1SSujith Manoharan bool skip_beacon = false; 6908e22ad32SRajkumar Manoharan 691ef4ad633SSujith Manoharan if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { 692ef4ad633SSujith Manoharan ath9k_cache_beacon_config(sc, bss_conf); 693ef4ad633SSujith Manoharan ath9k_set_beacon(sc); 694ef4ad633SSujith Manoharan set_bit(SC_OP_BEACONS, &sc->sc_flags); 6951a6404a1SSujith Manoharan return; 6961a6404a1SSujith Manoharan 6971a6404a1SSujith Manoharan } 6981a6404a1SSujith Manoharan 699ef4ad633SSujith Manoharan /* 700ef4ad633SSujith Manoharan * Take care of multiple interfaces when 701ef4ad633SSujith Manoharan * enabling/disabling SWBA. 702ef4ad633SSujith Manoharan */ 703ef4ad633SSujith Manoharan if (changed & BSS_CHANGED_BEACON_ENABLED) { 704ef4ad633SSujith Manoharan if (!bss_conf->enable_beacon && 705ef4ad633SSujith Manoharan (sc->nbcnvifs <= 1)) { 706ef4ad633SSujith Manoharan cur_conf->enable_beacon = false; 707ef4ad633SSujith Manoharan } else if (bss_conf->enable_beacon) { 708ef4ad633SSujith Manoharan cur_conf->enable_beacon = true; 709ef4ad633SSujith Manoharan ath9k_cache_beacon_config(sc, bss_conf); 7108e22ad32SRajkumar Manoharan } 7118e22ad32SRajkumar Manoharan } 7128e22ad32SRajkumar Manoharan 7131a6404a1SSujith Manoharan /* 7141a6404a1SSujith Manoharan * Configure the HW beacon registers only when we have a valid 7151a6404a1SSujith Manoharan * beacon interval. 7161a6404a1SSujith Manoharan */ 717ef4ad633SSujith Manoharan if (cur_conf->beacon_interval) { 7181a6404a1SSujith Manoharan /* 7191a6404a1SSujith Manoharan * If we are joining an existing IBSS network, start beaconing 7201a6404a1SSujith Manoharan * only after a TSF-sync has taken place. Ensure that this 7211a6404a1SSujith Manoharan * happens by setting the appropriate flags. 7221a6404a1SSujith Manoharan */ 7231a6404a1SSujith Manoharan if ((changed & BSS_CHANGED_IBSS) && !bss_conf->ibss_creator && 7241a6404a1SSujith Manoharan bss_conf->enable_beacon) { 7251a6404a1SSujith Manoharan spin_lock_irqsave(&sc->sc_pm_lock, flags); 7261a6404a1SSujith Manoharan sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; 7271a6404a1SSujith Manoharan spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 7281a6404a1SSujith Manoharan skip_beacon = true; 7291a6404a1SSujith Manoharan } else { 730ef4ad633SSujith Manoharan ath9k_set_beacon(sc); 7311a6404a1SSujith Manoharan } 7328e22ad32SRajkumar Manoharan 7331a6404a1SSujith Manoharan /* 7341a6404a1SSujith Manoharan * Do not set the SC_OP_BEACONS flag for IBSS joiner mode 7351a6404a1SSujith Manoharan * here, it is done in ath9k_beacon_config_adhoc(). 7361a6404a1SSujith Manoharan */ 7371a6404a1SSujith Manoharan if (cur_conf->enable_beacon && !skip_beacon) 738ef4ad633SSujith Manoharan set_bit(SC_OP_BEACONS, &sc->sc_flags); 739ef4ad633SSujith Manoharan else 740ef4ad633SSujith Manoharan clear_bit(SC_OP_BEACONS, &sc->sc_flags); 741ef4ad633SSujith Manoharan } 742ef4ad633SSujith Manoharan } 743ef4ad633SSujith Manoharan 744ef4ad633SSujith Manoharan void ath9k_set_beacon(struct ath_softc *sc) 74599e4d43aSRajkumar Manoharan { 74699e4d43aSRajkumar Manoharan struct ath_common *common = ath9k_hw_common(sc->sc_ah); 74799e4d43aSRajkumar Manoharan struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; 74899e4d43aSRajkumar Manoharan 74926cd322bSFelix Fietkau switch (sc->sc_ah->opmode) { 7505379c8a2SSujith case NL80211_IFTYPE_AP: 7512664d666SThomas Pedersen case NL80211_IFTYPE_MESH_POINT: 752ef4ad633SSujith Manoharan ath9k_beacon_config_ap(sc, cur_conf); 7535379c8a2SSujith break; 7545379c8a2SSujith case NL80211_IFTYPE_ADHOC: 755ef4ad633SSujith Manoharan ath9k_beacon_config_adhoc(sc, cur_conf); 7565379c8a2SSujith break; 7575379c8a2SSujith case NL80211_IFTYPE_STATION: 758ef4ad633SSujith Manoharan ath9k_beacon_config_sta(sc, cur_conf); 7595379c8a2SSujith break; 7605379c8a2SSujith default: 761d2182b69SJoe Perches ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); 7625379c8a2SSujith return; 763f078f209SLuis R. Rodriguez } 764014cf3bbSRajkumar Manoharan } 765