1f078f209SLuis R. Rodriguez /* 2cee075a2SSujith * Copyright (c) 2008-2009 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 17394cf0a1SSujith #include "ath9k.h" 18f078f209SLuis R. Rodriguez 195379c8a2SSujith #define FUDGE 2 205379c8a2SSujith 21f078f209SLuis R. Rodriguez /* 22f078f209SLuis R. Rodriguez * This function will modify certain transmit queue properties depending on 23f078f209SLuis R. Rodriguez * the operating mode of the station (AP or AdHoc). Parameters are AIFS 24f078f209SLuis R. Rodriguez * settings and channel width min/max 25f078f209SLuis R. Rodriguez */ 2694db2936SVivek Natarajan int ath_beaconq_config(struct ath_softc *sc) 27f078f209SLuis R. Rodriguez { 28cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 29c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(ah); 3094db2936SVivek Natarajan struct ath9k_tx_queue_info qi, qi_be; 31066dae93SFelix Fietkau struct ath_txq *txq; 32f078f209SLuis R. Rodriguez 33b77f483fSSujith ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); 342660b81aSSujith if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { 35f078f209SLuis R. Rodriguez /* Always burst out beacon and CAB traffic. */ 36f078f209SLuis R. Rodriguez qi.tqi_aifs = 1; 37f078f209SLuis R. Rodriguez qi.tqi_cwmin = 0; 38f078f209SLuis R. Rodriguez qi.tqi_cwmax = 0; 39f078f209SLuis R. Rodriguez } else { 40f078f209SLuis R. Rodriguez /* Adhoc mode; important thing is to use 2x cwmin. */ 41066dae93SFelix Fietkau txq = sc->tx.txq_map[WME_AC_BE]; 42066dae93SFelix Fietkau ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be); 4394db2936SVivek Natarajan qi.tqi_aifs = qi_be.tqi_aifs; 4494db2936SVivek Natarajan qi.tqi_cwmin = 4*qi_be.tqi_cwmin; 4594db2936SVivek Natarajan qi.tqi_cwmax = qi_be.tqi_cwmax; 46f078f209SLuis R. Rodriguez } 47f078f209SLuis R. Rodriguez 48b77f483fSSujith if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) { 49*3800276aSJoe Perches ath_err(common, 50d8baa939SSujith "Unable to update h/w beacon queue parameters\n"); 51f078f209SLuis R. Rodriguez return 0; 52f078f209SLuis R. Rodriguez } else { 539fc9ab0aSSujith ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); 54f078f209SLuis R. Rodriguez return 1; 55f078f209SLuis R. Rodriguez } 56f078f209SLuis R. Rodriguez } 57f078f209SLuis R. Rodriguez 58f078f209SLuis R. Rodriguez /* 59f078f209SLuis R. Rodriguez * Associates the beacon frame buffer with a transmit descriptor. Will set 60f078f209SLuis R. Rodriguez * up all required antenna switch parameters, rate codes, and channel flags. 61f078f209SLuis R. Rodriguez * Beacons are always sent out at the lowest rate, and are not retried. 62f078f209SLuis R. Rodriguez */ 639fc9ab0aSSujith static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, 6464b84010SJouni Malinen struct ath_buf *bf, int rateidx) 65f078f209SLuis R. Rodriguez { 66a22be22aSSujith struct sk_buff *skb = bf->bf_mpdu; 67cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 6843c27613SLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(ah); 69f078f209SLuis R. Rodriguez struct ath_desc *ds; 70980b24daSSujith struct ath9k_11n_rate_series series[4]; 719fc9ab0aSSujith int flags, antenna, ctsrate = 0, ctsduration = 0; 72545750d3SFelix Fietkau struct ieee80211_supported_band *sband; 73545750d3SFelix Fietkau u8 rate = 0; 74f078f209SLuis R. Rodriguez 75f078f209SLuis R. Rodriguez ds = bf->bf_desc; 76f078f209SLuis R. Rodriguez flags = ATH9K_TXDESC_NOACK; 77f078f209SLuis R. Rodriguez 78f078f209SLuis R. Rodriguez ds->ds_link = 0; 79f078f209SLuis R. Rodriguez /* 80f078f209SLuis R. Rodriguez * Switch antenna every beacon. 819fc9ab0aSSujith * Should only switch every beacon period, not for every SWBA 829fc9ab0aSSujith * XXX assumes two antennae 83f078f209SLuis R. Rodriguez */ 8417d7904dSSujith antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1); 85f078f209SLuis R. Rodriguez 86545750d3SFelix Fietkau sband = &sc->sbands[common->hw->conf.channel->band]; 8764b84010SJouni Malinen rate = sband->bitrates[rateidx].hw_value; 88672840acSSujith if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) 8964b84010SJouni Malinen rate |= sband->bitrates[rateidx].hw_value_short; 90f078f209SLuis R. Rodriguez 919fc9ab0aSSujith ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN, 929fc9ab0aSSujith ATH9K_PKT_TYPE_BEACON, 939fc9ab0aSSujith MAX_RATE_POWER, 949fc9ab0aSSujith ATH9K_TXKEYIX_INVALID, 959fc9ab0aSSujith ATH9K_KEY_TYPE_CLEAR, 969fc9ab0aSSujith flags); 97f078f209SLuis R. Rodriguez 98f078f209SLuis R. Rodriguez /* NB: beacon's BufLen must be a multiple of 4 bytes */ 999fc9ab0aSSujith ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4), 100cc610ac0SVasanthakumar Thiagarajan true, true, ds, bf->bf_buf_addr, 101cc610ac0SVasanthakumar Thiagarajan sc->beacon.beaconq); 102f078f209SLuis R. Rodriguez 1030345f37bSLuis R. Rodriguez memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4); 104f078f209SLuis R. Rodriguez series[0].Tries = 1; 105f078f209SLuis R. Rodriguez series[0].Rate = rate; 106ea066d5aSMohammed Shafi Shajakhan series[0].ChSel = ath_txchainmask_reduction(sc, 107ea066d5aSMohammed Shafi Shajakhan common->tx_chainmask, series[0].Rate); 108f078f209SLuis R. Rodriguez series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; 1099fc9ab0aSSujith ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration, 1109fc9ab0aSSujith series, 4, 0); 111f078f209SLuis R. Rodriguez } 112f078f209SLuis R. Rodriguez 11328d16708SFelix Fietkau static void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) 11428d16708SFelix Fietkau { 11528d16708SFelix Fietkau struct ath_wiphy *aphy = hw->priv; 11628d16708SFelix Fietkau struct ath_softc *sc = aphy->sc; 11728d16708SFelix Fietkau struct ath_common *common = ath9k_hw_common(sc->sc_ah); 11828d16708SFelix Fietkau struct ath_tx_control txctl; 11928d16708SFelix Fietkau 12028d16708SFelix Fietkau memset(&txctl, 0, sizeof(struct ath_tx_control)); 12128d16708SFelix Fietkau txctl.txq = sc->beacon.cabq; 12228d16708SFelix Fietkau 12328d16708SFelix Fietkau ath_print(common, ATH_DBG_XMIT, 12428d16708SFelix Fietkau "transmitting CABQ packet, skb: %p\n", skb); 12528d16708SFelix Fietkau 12628d16708SFelix Fietkau if (ath_tx_start(hw, skb, &txctl) != 0) { 12728d16708SFelix Fietkau ath_print(common, ATH_DBG_XMIT, "CABQ TX failed\n"); 12828d16708SFelix Fietkau dev_kfree_skb_any(skb); 12928d16708SFelix Fietkau } 13028d16708SFelix Fietkau } 13128d16708SFelix Fietkau 132c52f33d0SJouni Malinen static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, 1332c3db3d5SJouni Malinen struct ieee80211_vif *vif) 134f078f209SLuis R. Rodriguez { 135c52f33d0SJouni Malinen struct ath_wiphy *aphy = hw->priv; 136c52f33d0SJouni Malinen struct ath_softc *sc = aphy->sc; 137c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 138f078f209SLuis R. Rodriguez struct ath_buf *bf; 13917d7904dSSujith struct ath_vif *avp; 140f078f209SLuis R. Rodriguez struct sk_buff *skb; 141f078f209SLuis R. Rodriguez struct ath_txq *cabq; 142147583c0SJouni Malinen struct ieee80211_tx_info *info; 143980b24daSSujith int cabq_depth; 144980b24daSSujith 145f0ed85c6SJouni Malinen if (aphy->state != ATH_WIPHY_ACTIVE) 146f0ed85c6SJouni Malinen return NULL; 147f0ed85c6SJouni Malinen 1485640b08eSSujith avp = (void *)vif->drv_priv; 149b77f483fSSujith cabq = sc->beacon.cabq; 150f078f209SLuis R. Rodriguez 151d8baa939SSujith if (avp->av_bcbuf == NULL) 152f078f209SLuis R. Rodriguez return NULL; 153980b24daSSujith 1549fc9ab0aSSujith /* Release the old beacon first */ 1559fc9ab0aSSujith 156f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 157a22be22aSSujith skb = bf->bf_mpdu; 158a8fff50eSJouni Malinen if (skb) { 159c1739eb3SBen Greear dma_unmap_single(sc->dev, bf->bf_buf_addr, 1609fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 1613fbb9d95SJouni Malinen dev_kfree_skb_any(skb); 1626cf9e995SBen Greear bf->bf_buf_addr = 0; 163a8fff50eSJouni Malinen } 164f078f209SLuis R. Rodriguez 1659fc9ab0aSSujith /* Get a new beacon from mac80211 */ 1669fc9ab0aSSujith 167c52f33d0SJouni Malinen skb = ieee80211_beacon_get(hw, vif); 168a8fff50eSJouni Malinen bf->bf_mpdu = skb; 169a8fff50eSJouni Malinen if (skb == NULL) 170a8fff50eSJouni Malinen return NULL; 1714ed96f04SJouni Malinen ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = 1724ed96f04SJouni Malinen avp->tsf_adjust; 173980b24daSSujith 174147583c0SJouni Malinen info = IEEE80211_SKB_CB(skb); 175147583c0SJouni Malinen if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { 176147583c0SJouni Malinen /* 177147583c0SJouni Malinen * TODO: make sure the seq# gets assigned properly (vs. other 178147583c0SJouni Malinen * TX frames) 179147583c0SJouni Malinen */ 180147583c0SJouni Malinen struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 181b77f483fSSujith sc->tx.seq_no += 0x10; 182147583c0SJouni Malinen hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); 183b77f483fSSujith hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); 184147583c0SJouni Malinen } 185980b24daSSujith 186c1739eb3SBen Greear bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, 1879fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 1887da3c55cSGabor Juhos if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { 189f8316df1SLuis R. Rodriguez dev_kfree_skb_any(skb); 190f8316df1SLuis R. Rodriguez bf->bf_mpdu = NULL; 1916cf9e995SBen Greear bf->bf_buf_addr = 0; 192*3800276aSJoe Perches ath_err(common, "dma_mapping_error on beaconing\n"); 193f8316df1SLuis R. Rodriguez return NULL; 194f8316df1SLuis R. Rodriguez } 195f078f209SLuis R. Rodriguez 196c52f33d0SJouni Malinen skb = ieee80211_get_buffered_bc(hw, vif); 197f078f209SLuis R. Rodriguez 198f078f209SLuis R. Rodriguez /* 199f078f209SLuis R. Rodriguez * if the CABQ traffic from previous DTIM is pending and the current 200f078f209SLuis R. Rodriguez * beacon is also a DTIM. 20117d7904dSSujith * 1) if there is only one vif let the cab traffic continue. 20217d7904dSSujith * 2) if there are more than one vif and we are using staggered 203f078f209SLuis R. Rodriguez * beacons, then drain the cabq by dropping all the frames in 20417d7904dSSujith * the cabq so that the current vifs cab traffic can be scheduled. 205f078f209SLuis R. Rodriguez */ 206f078f209SLuis R. Rodriguez spin_lock_bh(&cabq->axq_lock); 207f078f209SLuis R. Rodriguez cabq_depth = cabq->axq_depth; 208f078f209SLuis R. Rodriguez spin_unlock_bh(&cabq->axq_lock); 209f078f209SLuis R. Rodriguez 210e022edbdSJouni Malinen if (skb && cabq_depth) { 21117d7904dSSujith if (sc->nvifs > 1) { 212c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 2139fc9ab0aSSujith "Flushing previous cabq traffic\n"); 2149fc9ab0aSSujith ath_draintxq(sc, cabq, false); 215f078f209SLuis R. Rodriguez } 216f078f209SLuis R. Rodriguez } 217f078f209SLuis R. Rodriguez 21864b84010SJouni Malinen ath_beacon_setup(sc, avp, bf, info->control.rates[0].idx); 219f078f209SLuis R. Rodriguez 220e022edbdSJouni Malinen while (skb) { 221c52f33d0SJouni Malinen ath_tx_cabq(hw, skb); 222c52f33d0SJouni Malinen skb = ieee80211_get_buffered_bc(hw, vif); 223e022edbdSJouni Malinen } 224f078f209SLuis R. Rodriguez 225f078f209SLuis R. Rodriguez return bf; 226f078f209SLuis R. Rodriguez } 227f078f209SLuis R. Rodriguez 228c52f33d0SJouni Malinen int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) 229f078f209SLuis R. Rodriguez { 230c52f33d0SJouni Malinen struct ath_softc *sc = aphy->sc; 231c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 23217d7904dSSujith struct ath_vif *avp; 233f078f209SLuis R. Rodriguez struct ath_buf *bf; 234f078f209SLuis R. Rodriguez struct sk_buff *skb; 235459f5f90SSujith __le64 tstamp; 236f078f209SLuis R. Rodriguez 2375640b08eSSujith avp = (void *)vif->drv_priv; 238f078f209SLuis R. Rodriguez 239f078f209SLuis R. Rodriguez /* Allocate a beacon descriptor if we haven't done so. */ 240f078f209SLuis R. Rodriguez if (!avp->av_bcbuf) { 241980b24daSSujith /* Allocate beacon state for hostap/ibss. We know 242980b24daSSujith * a buffer is available. */ 243b77f483fSSujith avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, 244f078f209SLuis R. Rodriguez struct ath_buf, list); 245f078f209SLuis R. Rodriguez list_del(&avp->av_bcbuf->list); 246f078f209SLuis R. Rodriguez 2472660b81aSSujith if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || 248a65e4cb4SFelix Fietkau sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC || 249a65e4cb4SFelix Fietkau sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) { 250f078f209SLuis R. Rodriguez int slot; 251f078f209SLuis R. Rodriguez /* 25217d7904dSSujith * Assign the vif to a beacon xmit slot. As 253f078f209SLuis R. Rodriguez * above, this cannot fail to find one. 254f078f209SLuis R. Rodriguez */ 255f078f209SLuis R. Rodriguez avp->av_bslot = 0; 256f078f209SLuis R. Rodriguez for (slot = 0; slot < ATH_BCBUF; slot++) 2572c3db3d5SJouni Malinen if (sc->beacon.bslot[slot] == NULL) { 258f078f209SLuis R. Rodriguez avp->av_bslot = slot; 259774610e4SFelix Fietkau 260f078f209SLuis R. Rodriguez /* NB: keep looking for a double slot */ 261774610e4SFelix Fietkau if (slot == 0 || !sc->beacon.bslot[slot-1]) 262774610e4SFelix Fietkau break; 263f078f209SLuis R. Rodriguez } 2642c3db3d5SJouni Malinen BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL); 2652c3db3d5SJouni Malinen sc->beacon.bslot[avp->av_bslot] = vif; 266c52f33d0SJouni Malinen sc->beacon.bslot_aphy[avp->av_bslot] = aphy; 26717d7904dSSujith sc->nbcnvifs++; 268f078f209SLuis R. Rodriguez } 269f078f209SLuis R. Rodriguez } 270f078f209SLuis R. Rodriguez 271f078f209SLuis R. Rodriguez /* release the previous beacon frame, if it already exists. */ 272f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 273f078f209SLuis R. Rodriguez if (bf->bf_mpdu != NULL) { 274a22be22aSSujith skb = bf->bf_mpdu; 275c1739eb3SBen Greear dma_unmap_single(sc->dev, bf->bf_buf_addr, 2769fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 277f078f209SLuis R. Rodriguez dev_kfree_skb_any(skb); 278f078f209SLuis R. Rodriguez bf->bf_mpdu = NULL; 2796cf9e995SBen Greear bf->bf_buf_addr = 0; 280f078f209SLuis R. Rodriguez } 281f078f209SLuis R. Rodriguez 2829fc9ab0aSSujith /* NB: the beacon data buffer must be 32-bit aligned. */ 2835640b08eSSujith skb = ieee80211_beacon_get(sc->hw, vif); 284f078f209SLuis R. Rodriguez if (skb == NULL) { 285c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, "cannot get skb\n"); 286f078f209SLuis R. Rodriguez return -ENOMEM; 287f078f209SLuis R. Rodriguez } 288f078f209SLuis R. Rodriguez 289459f5f90SSujith tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; 290b77f483fSSujith sc->beacon.bc_tstamp = le64_to_cpu(tstamp); 2914ed96f04SJouni Malinen /* Calculate a TSF adjustment factor required for staggered beacons. */ 292f078f209SLuis R. Rodriguez if (avp->av_bslot > 0) { 293f078f209SLuis R. Rodriguez u64 tsfadjust; 294f078f209SLuis R. Rodriguez int intval; 295f078f209SLuis R. Rodriguez 29657c4d7b4SJohannes Berg intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; 297f078f209SLuis R. Rodriguez 298f078f209SLuis R. Rodriguez /* 2994ed96f04SJouni Malinen * Calculate the TSF offset for this beacon slot, i.e., the 3004ed96f04SJouni Malinen * number of usecs that need to be added to the timestamp field 3014ed96f04SJouni Malinen * in Beacon and Probe Response frames. Beacon slot 0 is 3024ed96f04SJouni Malinen * processed at the correct offset, so it does not require TSF 3034ed96f04SJouni Malinen * adjustment. Other slots are adjusted to get the timestamp 3044ed96f04SJouni Malinen * close to the TBTT for the BSS. 305f078f209SLuis R. Rodriguez */ 3064ed96f04SJouni Malinen tsfadjust = intval * avp->av_bslot / ATH_BCBUF; 3074ed96f04SJouni Malinen avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); 308f078f209SLuis R. Rodriguez 309c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 310c46917bbSLuis R. Rodriguez "stagger beacons, bslot %d intval " 311c46917bbSLuis R. Rodriguez "%u tsfadjust %llu\n", 312f078f209SLuis R. Rodriguez avp->av_bslot, intval, (unsigned long long)tsfadjust); 313f078f209SLuis R. Rodriguez 3144ed96f04SJouni Malinen ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = 3154ed96f04SJouni Malinen avp->tsf_adjust; 3164ed96f04SJouni Malinen } else 3174ed96f04SJouni Malinen avp->tsf_adjust = cpu_to_le64(0); 318f078f209SLuis R. Rodriguez 319f8316df1SLuis R. Rodriguez bf->bf_mpdu = skb; 320c1739eb3SBen Greear bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, 3219fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 3227da3c55cSGabor Juhos if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { 323f8316df1SLuis R. Rodriguez dev_kfree_skb_any(skb); 324f8316df1SLuis R. Rodriguez bf->bf_mpdu = NULL; 3256cf9e995SBen Greear bf->bf_buf_addr = 0; 326*3800276aSJoe Perches ath_err(common, "dma_mapping_error on beacon alloc\n"); 327f8316df1SLuis R. Rodriguez return -ENOMEM; 328f8316df1SLuis R. Rodriguez } 329f078f209SLuis R. Rodriguez 330f078f209SLuis R. Rodriguez return 0; 331f078f209SLuis R. Rodriguez } 332f078f209SLuis R. Rodriguez 33317d7904dSSujith void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp) 334f078f209SLuis R. Rodriguez { 335f078f209SLuis R. Rodriguez if (avp->av_bcbuf != NULL) { 336f078f209SLuis R. Rodriguez struct ath_buf *bf; 337f078f209SLuis R. Rodriguez 338f078f209SLuis R. Rodriguez if (avp->av_bslot != -1) { 3392c3db3d5SJouni Malinen sc->beacon.bslot[avp->av_bslot] = NULL; 340c52f33d0SJouni Malinen sc->beacon.bslot_aphy[avp->av_bslot] = NULL; 34117d7904dSSujith sc->nbcnvifs--; 342f078f209SLuis R. Rodriguez } 343f078f209SLuis R. Rodriguez 344f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 345f078f209SLuis R. Rodriguez if (bf->bf_mpdu != NULL) { 346a22be22aSSujith struct sk_buff *skb = bf->bf_mpdu; 347c1739eb3SBen Greear dma_unmap_single(sc->dev, bf->bf_buf_addr, 3489fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 349f078f209SLuis R. Rodriguez dev_kfree_skb_any(skb); 350f078f209SLuis R. Rodriguez bf->bf_mpdu = NULL; 3516cf9e995SBen Greear bf->bf_buf_addr = 0; 352f078f209SLuis R. Rodriguez } 353b77f483fSSujith list_add_tail(&bf->list, &sc->beacon.bbuf); 354f078f209SLuis R. Rodriguez 355f078f209SLuis R. Rodriguez avp->av_bcbuf = NULL; 356f078f209SLuis R. Rodriguez } 357f078f209SLuis R. Rodriguez } 358f078f209SLuis R. Rodriguez 3599fc9ab0aSSujith void ath_beacon_tasklet(unsigned long data) 360f078f209SLuis R. Rodriguez { 361f078f209SLuis R. Rodriguez struct ath_softc *sc = (struct ath_softc *)data; 362cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 363c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(ah); 364f078f209SLuis R. Rodriguez struct ath_buf *bf = NULL; 3652c3db3d5SJouni Malinen struct ieee80211_vif *vif; 366c52f33d0SJouni Malinen struct ath_wiphy *aphy; 3672c3db3d5SJouni Malinen int slot; 3689546aae0SSujith u32 bfaddr, bc = 0, tsftu; 369f078f209SLuis R. Rodriguez u64 tsf; 370f078f209SLuis R. Rodriguez u16 intval; 371f078f209SLuis R. Rodriguez 372f078f209SLuis R. Rodriguez /* 373f078f209SLuis R. Rodriguez * Check if the previous beacon has gone out. If 374f078f209SLuis R. Rodriguez * not don't try to post another, skip this period 375f078f209SLuis R. Rodriguez * and wait for the next. Missed beacons indicate 376f078f209SLuis R. Rodriguez * a problem and should not occur. If we miss too 377f078f209SLuis R. Rodriguez * many consecutive beacons reset the device. 378f078f209SLuis R. Rodriguez */ 379b77f483fSSujith if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { 380b77f483fSSujith sc->beacon.bmisscnt++; 3819546aae0SSujith 382b77f483fSSujith if (sc->beacon.bmisscnt < BSTUCK_THRESH) { 3836252fcb9SFelix Fietkau ath_print(common, ATH_DBG_BSTUCK, 38404bd4638SSujith "missed %u consecutive beacons\n", 385b77f483fSSujith sc->beacon.bmisscnt); 38670cf1533SFelix Fietkau ath9k_hw_bstuck_nfcal(ah); 387b77f483fSSujith } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { 3886252fcb9SFelix Fietkau ath_print(common, ATH_DBG_BSTUCK, 38904bd4638SSujith "beacon is officially stuck\n"); 390b74444f8SJeff Hansen sc->sc_flags |= SC_OP_TSF_RESET; 391fac6b6a0SFelix Fietkau ath_reset(sc, true); 392f078f209SLuis R. Rodriguez } 3939546aae0SSujith 394f078f209SLuis R. Rodriguez return; 395f078f209SLuis R. Rodriguez } 396980b24daSSujith 397b77f483fSSujith if (sc->beacon.bmisscnt != 0) { 3986252fcb9SFelix Fietkau ath_print(common, ATH_DBG_BSTUCK, 39904bd4638SSujith "resume beacon xmit after %u misses\n", 400b77f483fSSujith sc->beacon.bmisscnt); 401b77f483fSSujith sc->beacon.bmisscnt = 0; 402f078f209SLuis R. Rodriguez } 403f078f209SLuis R. Rodriguez 404f078f209SLuis R. Rodriguez /* 405f078f209SLuis R. Rodriguez * Generate beacon frames. we are sending frames 406f078f209SLuis R. Rodriguez * staggered so calculate the slot for this frame based 407f078f209SLuis R. Rodriguez * on the tsf to safeguard against missing an swba. 408f078f209SLuis R. Rodriguez */ 409f078f209SLuis R. Rodriguez 41057c4d7b4SJohannes Berg intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; 411f078f209SLuis R. Rodriguez 412f078f209SLuis R. Rodriguez tsf = ath9k_hw_gettsf64(ah); 413f078f209SLuis R. Rodriguez tsftu = TSF_TO_TU(tsf>>32, tsf); 414f078f209SLuis R. Rodriguez slot = ((tsftu % intval) * ATH_BCBUF) / intval; 4154ed96f04SJouni Malinen /* 4164ed96f04SJouni Malinen * Reverse the slot order to get slot 0 on the TBTT offset that does 4174ed96f04SJouni Malinen * not require TSF adjustment and other slots adding 4184ed96f04SJouni Malinen * slot/ATH_BCBUF * beacon_int to timestamp. For example, with 4194ed96f04SJouni Malinen * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 .. 4204ed96f04SJouni Malinen * and slot 0 is at correct offset to TBTT. 4214ed96f04SJouni Malinen */ 4224ed96f04SJouni Malinen slot = ATH_BCBUF - slot - 1; 4234ed96f04SJouni Malinen vif = sc->beacon.bslot[slot]; 4244ed96f04SJouni Malinen aphy = sc->beacon.bslot_aphy[slot]; 425980b24daSSujith 426c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 4272c3db3d5SJouni Malinen "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", 4282c3db3d5SJouni Malinen slot, tsf, tsftu, intval, vif); 429980b24daSSujith 430f078f209SLuis R. Rodriguez bfaddr = 0; 4312c3db3d5SJouni Malinen if (vif) { 432c52f33d0SJouni Malinen bf = ath_beacon_generate(aphy->hw, vif); 433f078f209SLuis R. Rodriguez if (bf != NULL) { 434f078f209SLuis R. Rodriguez bfaddr = bf->bf_daddr; 435f078f209SLuis R. Rodriguez bc = 1; 436f078f209SLuis R. Rodriguez } 437f078f209SLuis R. Rodriguez } 4389546aae0SSujith 439f078f209SLuis R. Rodriguez /* 440f078f209SLuis R. Rodriguez * Handle slot time change when a non-ERP station joins/leaves 441f078f209SLuis R. Rodriguez * an 11g network. The 802.11 layer notifies us via callback, 442f078f209SLuis R. Rodriguez * we mark updateslot, then wait one beacon before effecting 443f078f209SLuis R. Rodriguez * the change. This gives associated stations at least one 444f078f209SLuis R. Rodriguez * beacon interval to note the state change. 445f078f209SLuis R. Rodriguez * 446f078f209SLuis R. Rodriguez * NB: The slot time change state machine is clocked according 447f078f209SLuis R. Rodriguez * to whether we are bursting or staggering beacons. We 448f078f209SLuis R. Rodriguez * recognize the request to update and record the current 449f078f209SLuis R. Rodriguez * slot then don't transition until that slot is reached 450f078f209SLuis R. Rodriguez * again. If we miss a beacon for that slot then we'll be 451f078f209SLuis R. Rodriguez * slow to transition but we'll be sure at least one beacon 452f078f209SLuis R. Rodriguez * interval has passed. When bursting slot is always left 453f078f209SLuis R. Rodriguez * set to ATH_BCBUF so this check is a noop. 454f078f209SLuis R. Rodriguez */ 455b77f483fSSujith if (sc->beacon.updateslot == UPDATE) { 456b77f483fSSujith sc->beacon.updateslot = COMMIT; /* commit next beacon */ 457b77f483fSSujith sc->beacon.slotupdate = slot; 458b77f483fSSujith } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) { 4590005baf4SFelix Fietkau ah->slottime = sc->beacon.slottime; 4600005baf4SFelix Fietkau ath9k_hw_init_global_settings(ah); 461b77f483fSSujith sc->beacon.updateslot = OK; 462ff37e337SSujith } 463f078f209SLuis R. Rodriguez if (bfaddr != 0) { 464f078f209SLuis R. Rodriguez /* 465f078f209SLuis R. Rodriguez * Stop any current dma and put the new frame(s) on the queue. 466f078f209SLuis R. Rodriguez * This should never fail since we check above that no frames 467f078f209SLuis R. Rodriguez * are still pending on the queue. 468f078f209SLuis R. Rodriguez */ 469b77f483fSSujith if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) { 470*3800276aSJoe Perches ath_err(common, "beacon queue %u did not stop?\n", 471*3800276aSJoe Perches sc->beacon.beaconq); 472f078f209SLuis R. Rodriguez } 473f078f209SLuis R. Rodriguez 474f078f209SLuis R. Rodriguez /* NB: cabq traffic should already be queued and primed */ 475b77f483fSSujith ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr); 476b77f483fSSujith ath9k_hw_txstart(ah, sc->beacon.beaconq); 477f078f209SLuis R. Rodriguez 47817d7904dSSujith sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */ 479f078f209SLuis R. Rodriguez } 480f078f209SLuis R. Rodriguez } 481f078f209SLuis R. Rodriguez 48221526d57SLuis R. Rodriguez static void ath9k_beacon_init(struct ath_softc *sc, 48321526d57SLuis R. Rodriguez u32 next_beacon, 48421526d57SLuis R. Rodriguez u32 beacon_period) 48521526d57SLuis R. Rodriguez { 48621526d57SLuis R. Rodriguez if (beacon_period & ATH9K_BEACON_RESET_TSF) 48721526d57SLuis R. Rodriguez ath9k_ps_wakeup(sc); 48821526d57SLuis R. Rodriguez 48921526d57SLuis R. Rodriguez ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period); 49021526d57SLuis R. Rodriguez 49121526d57SLuis R. Rodriguez if (beacon_period & ATH9K_BEACON_RESET_TSF) 49221526d57SLuis R. Rodriguez ath9k_ps_restore(sc); 49321526d57SLuis R. Rodriguez } 49421526d57SLuis R. Rodriguez 495f078f209SLuis R. Rodriguez /* 4965379c8a2SSujith * For multi-bss ap support beacons are either staggered evenly over N slots or 4975379c8a2SSujith * burst together. For the former arrange for the SWBA to be delivered for each 4985379c8a2SSujith * slot. Slots that are not occupied will generate nothing. 499f078f209SLuis R. Rodriguez */ 5005379c8a2SSujith static void ath_beacon_config_ap(struct ath_softc *sc, 501d31e20afSVasanthakumar Thiagarajan struct ath_beacon_config *conf) 502f078f209SLuis R. Rodriguez { 5033069168cSPavel Roskin struct ath_hw *ah = sc->sc_ah; 504980b24daSSujith u32 nexttbtt, intval; 505f078f209SLuis R. Rodriguez 506f078f209SLuis R. Rodriguez /* NB: the beacon interval is kept internally in TU's */ 5075379c8a2SSujith intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; 508f078f209SLuis R. Rodriguez intval /= ATH_BCBUF; /* for staggered beacons */ 5095379c8a2SSujith nexttbtt = intval; 510d8728ee9SFelix Fietkau 511d8728ee9SFelix Fietkau if (sc->sc_flags & SC_OP_TSF_RESET) 5125379c8a2SSujith intval |= ATH9K_BEACON_RESET_TSF; 5135379c8a2SSujith 5145379c8a2SSujith /* 5155379c8a2SSujith * In AP mode we enable the beacon timers and SWBA interrupts to 5165379c8a2SSujith * prepare beacon frames. 5175379c8a2SSujith */ 5185379c8a2SSujith intval |= ATH9K_BEACON_ENA; 5193069168cSPavel Roskin ah->imask |= ATH9K_INT_SWBA; 5205379c8a2SSujith ath_beaconq_config(sc); 5215379c8a2SSujith 5225379c8a2SSujith /* Set the computed AP beacon timers */ 5235379c8a2SSujith 5244df3071eSFelix Fietkau ath9k_hw_disable_interrupts(ah); 52521526d57SLuis R. Rodriguez ath9k_beacon_init(sc, nexttbtt, intval); 5265379c8a2SSujith sc->beacon.bmisscnt = 0; 5273069168cSPavel Roskin ath9k_hw_set_interrupts(ah, ah->imask); 528b238e90eSSujith 529b238e90eSSujith /* Clear the reset TSF flag, so that subsequent beacon updation 530b238e90eSSujith will not reset the HW TSF. */ 531b238e90eSSujith 532b238e90eSSujith sc->sc_flags &= ~SC_OP_TSF_RESET; 533f078f209SLuis R. Rodriguez } 534f078f209SLuis R. Rodriguez 5355379c8a2SSujith /* 5365379c8a2SSujith * This sets up the beacon timers according to the timestamp of the last 5375379c8a2SSujith * received beacon and the current TSF, configures PCF and DTIM 5385379c8a2SSujith * handling, programs the sleep registers so the hardware will wakeup in 5395379c8a2SSujith * time to receive beacons, and configures the beacon miss handling so 5405379c8a2SSujith * we'll receive a BMISS interrupt when we stop seeing beacons from the AP 5415379c8a2SSujith * we've associated with. 5425379c8a2SSujith */ 5435379c8a2SSujith static void ath_beacon_config_sta(struct ath_softc *sc, 544d31e20afSVasanthakumar Thiagarajan struct ath_beacon_config *conf) 5455379c8a2SSujith { 5463069168cSPavel Roskin struct ath_hw *ah = sc->sc_ah; 5473069168cSPavel Roskin struct ath_common *common = ath9k_hw_common(ah); 548f078f209SLuis R. Rodriguez struct ath9k_beacon_state bs; 549f078f209SLuis R. Rodriguez int dtimperiod, dtimcount, sleepduration; 550f078f209SLuis R. Rodriguez int cfpperiod, cfpcount; 5515379c8a2SSujith u32 nexttbtt = 0, intval, tsftu; 5525379c8a2SSujith u64 tsf; 553267a9012SJouni Malinen int num_beacons, offset, dtim_dec_count, cfp_dec_count; 5545379c8a2SSujith 5551a20034aSSenthil Balasubramanian /* No need to configure beacon if we are not associated */ 5561a20034aSSenthil Balasubramanian if (!common->curaid) { 5571a20034aSSenthil Balasubramanian ath_print(common, ATH_DBG_BEACON, 5581a20034aSSenthil Balasubramanian "STA is not yet associated..skipping beacon config\n"); 5591a20034aSSenthil Balasubramanian return; 5601a20034aSSenthil Balasubramanian } 5611a20034aSSenthil Balasubramanian 5625379c8a2SSujith memset(&bs, 0, sizeof(bs)); 5635379c8a2SSujith intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; 564f078f209SLuis R. Rodriguez 565f078f209SLuis R. Rodriguez /* 566f078f209SLuis R. Rodriguez * Setup dtim and cfp parameters according to 567f078f209SLuis R. Rodriguez * last beacon we received (which may be none). 568f078f209SLuis R. Rodriguez */ 5695379c8a2SSujith dtimperiod = conf->dtim_period; 570f078f209SLuis R. Rodriguez if (dtimperiod <= 0) /* NB: 0 if not known */ 571f078f209SLuis R. Rodriguez dtimperiod = 1; 5725379c8a2SSujith dtimcount = conf->dtim_count; 573f078f209SLuis R. Rodriguez if (dtimcount >= dtimperiod) /* NB: sanity check */ 574980b24daSSujith dtimcount = 0; 575f078f209SLuis R. Rodriguez cfpperiod = 1; /* NB: no PCF support yet */ 576f078f209SLuis R. Rodriguez cfpcount = 0; 577f078f209SLuis R. Rodriguez 5785379c8a2SSujith sleepduration = conf->listen_interval * intval; 579f078f209SLuis R. Rodriguez if (sleepduration <= 0) 580f078f209SLuis R. Rodriguez sleepduration = intval; 581f078f209SLuis R. Rodriguez 582f078f209SLuis R. Rodriguez /* 583f078f209SLuis R. Rodriguez * Pull nexttbtt forward to reflect the current 584f078f209SLuis R. Rodriguez * TSF and calculate dtim+cfp state for the result. 585f078f209SLuis R. Rodriguez */ 5863069168cSPavel Roskin tsf = ath9k_hw_gettsf64(ah); 587f078f209SLuis R. Rodriguez tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 588267a9012SJouni Malinen 589267a9012SJouni Malinen num_beacons = tsftu / intval + 1; 590267a9012SJouni Malinen offset = tsftu % intval; 591267a9012SJouni Malinen nexttbtt = tsftu - offset; 592267a9012SJouni Malinen if (offset) 593f078f209SLuis R. Rodriguez nexttbtt += intval; 594267a9012SJouni Malinen 595267a9012SJouni Malinen /* DTIM Beacon every dtimperiod Beacon */ 596267a9012SJouni Malinen dtim_dec_count = num_beacons % dtimperiod; 597267a9012SJouni Malinen /* CFP every cfpperiod DTIM Beacon */ 598267a9012SJouni Malinen cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; 599267a9012SJouni Malinen if (dtim_dec_count) 600267a9012SJouni Malinen cfp_dec_count++; 601267a9012SJouni Malinen 602267a9012SJouni Malinen dtimcount -= dtim_dec_count; 603267a9012SJouni Malinen if (dtimcount < 0) 604267a9012SJouni Malinen dtimcount += dtimperiod; 605267a9012SJouni Malinen 606267a9012SJouni Malinen cfpcount -= cfp_dec_count; 607267a9012SJouni Malinen if (cfpcount < 0) 608267a9012SJouni Malinen cfpcount += cfpperiod; 6095379c8a2SSujith 610f078f209SLuis R. Rodriguez bs.bs_intval = intval; 611f078f209SLuis R. Rodriguez bs.bs_nexttbtt = nexttbtt; 612f078f209SLuis R. Rodriguez bs.bs_dtimperiod = dtimperiod*intval; 613f078f209SLuis R. Rodriguez bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 614f078f209SLuis R. Rodriguez bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 615f078f209SLuis R. Rodriguez bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 616f078f209SLuis R. Rodriguez bs.bs_cfpmaxduration = 0; 617980b24daSSujith 618f078f209SLuis R. Rodriguez /* 6195379c8a2SSujith * Calculate the number of consecutive beacons to miss* before taking 6205379c8a2SSujith * a BMISS interrupt. The configuration is specified in TU so we only 6215379c8a2SSujith * need calculate based on the beacon interval. Note that we clamp the 622f078f209SLuis R. Rodriguez * result to at most 15 beacons. 623f078f209SLuis R. Rodriguez */ 624f078f209SLuis R. Rodriguez if (sleepduration > intval) { 6255379c8a2SSujith bs.bs_bmissthreshold = conf->listen_interval * 626f078f209SLuis R. Rodriguez ATH_DEFAULT_BMISS_LIMIT / 2; 627f078f209SLuis R. Rodriguez } else { 6285379c8a2SSujith bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval); 629f078f209SLuis R. Rodriguez if (bs.bs_bmissthreshold > 15) 630f078f209SLuis R. Rodriguez bs.bs_bmissthreshold = 15; 631f078f209SLuis R. Rodriguez else if (bs.bs_bmissthreshold <= 0) 632f078f209SLuis R. Rodriguez bs.bs_bmissthreshold = 1; 633f078f209SLuis R. Rodriguez } 634f078f209SLuis R. Rodriguez 635f078f209SLuis R. Rodriguez /* 6365379c8a2SSujith * Calculate sleep duration. The configuration is given in ms. 6375379c8a2SSujith * We ensure a multiple of the beacon period is used. Also, if the sleep 6385379c8a2SSujith * duration is greater than the DTIM period then it makes senses 639f078f209SLuis R. Rodriguez * to make it a multiple of that. 640f078f209SLuis R. Rodriguez * 641f078f209SLuis R. Rodriguez * XXX fixed at 100ms 642f078f209SLuis R. Rodriguez */ 643f078f209SLuis R. Rodriguez 6445379c8a2SSujith bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); 645f078f209SLuis R. Rodriguez if (bs.bs_sleepduration > bs.bs_dtimperiod) 646f078f209SLuis R. Rodriguez bs.bs_sleepduration = bs.bs_dtimperiod; 647f078f209SLuis R. Rodriguez 6484af9cf4fSSujith /* TSF out of range threshold fixed at 1 second */ 6494af9cf4fSSujith bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; 6504af9cf4fSSujith 651c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); 652c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 6539fc9ab0aSSujith "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", 6549fc9ab0aSSujith bs.bs_bmissthreshold, bs.bs_sleepduration, 6559fc9ab0aSSujith bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); 656f078f209SLuis R. Rodriguez 6575379c8a2SSujith /* Set the computed STA beacon timers */ 6589fc9ab0aSSujith 6594df3071eSFelix Fietkau ath9k_hw_disable_interrupts(ah); 6603069168cSPavel Roskin ath9k_hw_set_sta_beacon_timers(ah, &bs); 6613069168cSPavel Roskin ah->imask |= ATH9K_INT_BMISS; 6623069168cSPavel Roskin ath9k_hw_set_interrupts(ah, ah->imask); 6635379c8a2SSujith } 6645379c8a2SSujith 6655379c8a2SSujith static void ath_beacon_config_adhoc(struct ath_softc *sc, 6665379c8a2SSujith struct ath_beacon_config *conf, 6672c3db3d5SJouni Malinen struct ieee80211_vif *vif) 6685379c8a2SSujith { 6693069168cSPavel Roskin struct ath_hw *ah = sc->sc_ah; 6703069168cSPavel Roskin struct ath_common *common = ath9k_hw_common(ah); 6715379c8a2SSujith u64 tsf; 6725379c8a2SSujith u32 tsftu, intval, nexttbtt; 6735379c8a2SSujith 6745379c8a2SSujith intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; 6755379c8a2SSujith 676546256fbSJouni Malinen 6779fc9ab0aSSujith /* Pull nexttbtt forward to reflect the current TSF */ 6785379c8a2SSujith 6795379c8a2SSujith nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp); 6805379c8a2SSujith if (nexttbtt == 0) 6815379c8a2SSujith nexttbtt = intval; 6825379c8a2SSujith else if (intval) 6835379c8a2SSujith nexttbtt = roundup(nexttbtt, intval); 6845379c8a2SSujith 6853069168cSPavel Roskin tsf = ath9k_hw_gettsf64(ah); 6865379c8a2SSujith tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE; 687f078f209SLuis R. Rodriguez do { 688f078f209SLuis R. Rodriguez nexttbtt += intval; 689f078f209SLuis R. Rodriguez } while (nexttbtt < tsftu); 6905379c8a2SSujith 691c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 69204bd4638SSujith "IBSS nexttbtt %u intval %u (%u)\n", 6935379c8a2SSujith nexttbtt, intval, conf->beacon_interval); 694f078f209SLuis R. Rodriguez 695f078f209SLuis R. Rodriguez /* 6965379c8a2SSujith * In IBSS mode enable the beacon timers but only enable SWBA interrupts 6975379c8a2SSujith * if we need to manually prepare beacon frames. Otherwise we use a 6985379c8a2SSujith * self-linked tx descriptor and let the hardware deal with things. 699f078f209SLuis R. Rodriguez */ 700f078f209SLuis R. Rodriguez intval |= ATH9K_BEACON_ENA; 7013069168cSPavel Roskin ah->imask |= ATH9K_INT_SWBA; 7029fc9ab0aSSujith 7035379c8a2SSujith ath_beaconq_config(sc); 7045379c8a2SSujith 7055379c8a2SSujith /* Set the computed ADHOC beacon timers */ 7065379c8a2SSujith 7074df3071eSFelix Fietkau ath9k_hw_disable_interrupts(ah); 70821526d57SLuis R. Rodriguez ath9k_beacon_init(sc, nexttbtt, intval); 709b77f483fSSujith sc->beacon.bmisscnt = 0; 7103069168cSPavel Roskin ath9k_hw_set_interrupts(ah, ah->imask); 711f078f209SLuis R. Rodriguez } 7125379c8a2SSujith 7132c3db3d5SJouni Malinen void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) 7145379c8a2SSujith { 7156b96f93eSVasanthakumar Thiagarajan struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; 716c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 7176b96f93eSVasanthakumar Thiagarajan enum nl80211_iftype iftype; 7185379c8a2SSujith 7195379c8a2SSujith /* Setup the beacon configuration parameters */ 7202c3db3d5SJouni Malinen if (vif) { 7216b96f93eSVasanthakumar Thiagarajan struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 7225379c8a2SSujith 7236b96f93eSVasanthakumar Thiagarajan iftype = vif->type; 7246b96f93eSVasanthakumar Thiagarajan 7256b96f93eSVasanthakumar Thiagarajan cur_conf->beacon_interval = bss_conf->beacon_int; 7266b96f93eSVasanthakumar Thiagarajan cur_conf->dtim_period = bss_conf->dtim_period; 7276b96f93eSVasanthakumar Thiagarajan cur_conf->listen_interval = 1; 7286b96f93eSVasanthakumar Thiagarajan cur_conf->dtim_count = 1; 7296b96f93eSVasanthakumar Thiagarajan cur_conf->bmiss_timeout = 7306b96f93eSVasanthakumar Thiagarajan ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; 7316b96f93eSVasanthakumar Thiagarajan } else { 7326b96f93eSVasanthakumar Thiagarajan iftype = sc->sc_ah->opmode; 7336b96f93eSVasanthakumar Thiagarajan } 7346b96f93eSVasanthakumar Thiagarajan 735c4f9f16bSVasanthakumar Thiagarajan /* 736c4f9f16bSVasanthakumar Thiagarajan * It looks like mac80211 may end up using beacon interval of zero in 737c4f9f16bSVasanthakumar Thiagarajan * some cases (at least for mesh point). Avoid getting into an 738c4f9f16bSVasanthakumar Thiagarajan * infinite loop by using a bit safer value instead. To be safe, 739c4f9f16bSVasanthakumar Thiagarajan * do sanity check on beacon interval for all operating modes. 740c4f9f16bSVasanthakumar Thiagarajan */ 741c4f9f16bSVasanthakumar Thiagarajan if (cur_conf->beacon_interval == 0) 742c4f9f16bSVasanthakumar Thiagarajan cur_conf->beacon_interval = 100; 7436b96f93eSVasanthakumar Thiagarajan 7446b96f93eSVasanthakumar Thiagarajan switch (iftype) { 7455379c8a2SSujith case NL80211_IFTYPE_AP: 7466b96f93eSVasanthakumar Thiagarajan ath_beacon_config_ap(sc, cur_conf); 7475379c8a2SSujith break; 7485379c8a2SSujith case NL80211_IFTYPE_ADHOC: 7499cb5412bSPat Erley case NL80211_IFTYPE_MESH_POINT: 7506b96f93eSVasanthakumar Thiagarajan ath_beacon_config_adhoc(sc, cur_conf, vif); 7515379c8a2SSujith break; 7525379c8a2SSujith case NL80211_IFTYPE_STATION: 7536b96f93eSVasanthakumar Thiagarajan ath_beacon_config_sta(sc, cur_conf); 7545379c8a2SSujith break; 7555379c8a2SSujith default: 756c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_CONFIG, 7575379c8a2SSujith "Unsupported beaconing mode\n"); 7585379c8a2SSujith return; 759f078f209SLuis R. Rodriguez } 760f078f209SLuis R. Rodriguez 761672840acSSujith sc->sc_flags |= SC_OP_BEACONS; 762f078f209SLuis R. Rodriguez } 763