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 */ 26f078f209SLuis R. Rodriguez static int ath_beaconq_config(struct ath_softc *sc) 27f078f209SLuis R. Rodriguez { 28cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 29*c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(ah); 30ea9880fbSSujith struct ath9k_tx_queue_info qi; 31f078f209SLuis R. Rodriguez 32b77f483fSSujith ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); 332660b81aSSujith if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { 34f078f209SLuis R. Rodriguez /* Always burst out beacon and CAB traffic. */ 35f078f209SLuis R. Rodriguez qi.tqi_aifs = 1; 36f078f209SLuis R. Rodriguez qi.tqi_cwmin = 0; 37f078f209SLuis R. Rodriguez qi.tqi_cwmax = 0; 38f078f209SLuis R. Rodriguez } else { 39f078f209SLuis R. Rodriguez /* Adhoc mode; important thing is to use 2x cwmin. */ 40b77f483fSSujith qi.tqi_aifs = sc->beacon.beacon_qi.tqi_aifs; 41b77f483fSSujith qi.tqi_cwmin = 2*sc->beacon.beacon_qi.tqi_cwmin; 42b77f483fSSujith qi.tqi_cwmax = sc->beacon.beacon_qi.tqi_cwmax; 43f078f209SLuis R. Rodriguez } 44f078f209SLuis R. Rodriguez 45b77f483fSSujith if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) { 46*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_FATAL, 47d8baa939SSujith "Unable to update h/w beacon queue parameters\n"); 48f078f209SLuis R. Rodriguez return 0; 49f078f209SLuis R. Rodriguez } else { 509fc9ab0aSSujith ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); 51f078f209SLuis R. Rodriguez return 1; 52f078f209SLuis R. Rodriguez } 53f078f209SLuis R. Rodriguez } 54f078f209SLuis R. Rodriguez 55f078f209SLuis R. Rodriguez /* 56f078f209SLuis R. Rodriguez * Associates the beacon frame buffer with a transmit descriptor. Will set 57f078f209SLuis R. Rodriguez * up all required antenna switch parameters, rate codes, and channel flags. 58f078f209SLuis R. Rodriguez * Beacons are always sent out at the lowest rate, and are not retried. 59f078f209SLuis R. Rodriguez */ 609fc9ab0aSSujith static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, 619fc9ab0aSSujith struct ath_buf *bf) 62f078f209SLuis R. Rodriguez { 63a22be22aSSujith struct sk_buff *skb = bf->bf_mpdu; 64cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 65f078f209SLuis R. Rodriguez struct ath_desc *ds; 66980b24daSSujith struct ath9k_11n_rate_series series[4]; 674f0fc7c3SLuis R. Rodriguez const struct ath_rate_table *rt; 689fc9ab0aSSujith int flags, antenna, ctsrate = 0, ctsduration = 0; 699fc9ab0aSSujith u8 rate; 70f078f209SLuis R. Rodriguez 71f078f209SLuis R. Rodriguez ds = bf->bf_desc; 72f078f209SLuis R. Rodriguez flags = ATH9K_TXDESC_NOACK; 73f078f209SLuis R. Rodriguez 749cb5412bSPat Erley if (((sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) || 759cb5412bSPat Erley (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) && 762660b81aSSujith (ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { 77f078f209SLuis R. Rodriguez ds->ds_link = bf->bf_daddr; /* self-linked */ 78f078f209SLuis R. Rodriguez flags |= ATH9K_TXDESC_VEOL; 79f078f209SLuis R. Rodriguez /* Let hardware handle antenna switching. */ 80f078f209SLuis R. Rodriguez antenna = 0; 81f078f209SLuis R. Rodriguez } else { 82f078f209SLuis R. Rodriguez ds->ds_link = 0; 83f078f209SLuis R. Rodriguez /* 84f078f209SLuis R. Rodriguez * Switch antenna every beacon. 859fc9ab0aSSujith * Should only switch every beacon period, not for every SWBA 869fc9ab0aSSujith * XXX assumes two antennae 87f078f209SLuis R. Rodriguez */ 8817d7904dSSujith antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1); 89f078f209SLuis R. Rodriguez } 90f078f209SLuis R. Rodriguez 91f078f209SLuis R. Rodriguez ds->ds_data = bf->bf_buf_addr; 92f078f209SLuis R. Rodriguez 933706de6fSSujith rt = sc->cur_rate_table; 949fc9ab0aSSujith rate = rt->info[0].ratecode; 95672840acSSujith if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) 969fc9ab0aSSujith rate |= rt->info[0].short_preamble; 97f078f209SLuis R. Rodriguez 989fc9ab0aSSujith ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN, 999fc9ab0aSSujith ATH9K_PKT_TYPE_BEACON, 1009fc9ab0aSSujith MAX_RATE_POWER, 1019fc9ab0aSSujith ATH9K_TXKEYIX_INVALID, 1029fc9ab0aSSujith ATH9K_KEY_TYPE_CLEAR, 1039fc9ab0aSSujith flags); 104f078f209SLuis R. Rodriguez 105f078f209SLuis R. Rodriguez /* NB: beacon's BufLen must be a multiple of 4 bytes */ 1069fc9ab0aSSujith ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4), 1079fc9ab0aSSujith true, true, ds); 108f078f209SLuis R. Rodriguez 1090345f37bSLuis R. Rodriguez memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4); 110f078f209SLuis R. Rodriguez series[0].Tries = 1; 111f078f209SLuis R. Rodriguez series[0].Rate = rate; 11217d7904dSSujith series[0].ChSel = sc->tx_chainmask; 113f078f209SLuis R. Rodriguez series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; 1149fc9ab0aSSujith ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration, 1159fc9ab0aSSujith series, 4, 0); 116f078f209SLuis R. Rodriguez } 117f078f209SLuis R. Rodriguez 118c52f33d0SJouni Malinen static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, 1192c3db3d5SJouni Malinen struct ieee80211_vif *vif) 120f078f209SLuis R. Rodriguez { 121c52f33d0SJouni Malinen struct ath_wiphy *aphy = hw->priv; 122c52f33d0SJouni Malinen struct ath_softc *sc = aphy->sc; 123*c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 124f078f209SLuis R. Rodriguez struct ath_buf *bf; 12517d7904dSSujith struct ath_vif *avp; 126f078f209SLuis R. Rodriguez struct sk_buff *skb; 127f078f209SLuis R. Rodriguez struct ath_txq *cabq; 128147583c0SJouni Malinen struct ieee80211_tx_info *info; 129980b24daSSujith int cabq_depth; 130980b24daSSujith 131f0ed85c6SJouni Malinen if (aphy->state != ATH_WIPHY_ACTIVE) 132f0ed85c6SJouni Malinen return NULL; 133f0ed85c6SJouni Malinen 1345640b08eSSujith avp = (void *)vif->drv_priv; 135b77f483fSSujith cabq = sc->beacon.cabq; 136f078f209SLuis R. Rodriguez 137d8baa939SSujith if (avp->av_bcbuf == NULL) 138f078f209SLuis R. Rodriguez return NULL; 139980b24daSSujith 1409fc9ab0aSSujith /* Release the old beacon first */ 1419fc9ab0aSSujith 142f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 143a22be22aSSujith skb = bf->bf_mpdu; 144a8fff50eSJouni Malinen if (skb) { 1457da3c55cSGabor Juhos dma_unmap_single(sc->dev, bf->bf_dmacontext, 1469fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 1473fbb9d95SJouni Malinen dev_kfree_skb_any(skb); 148a8fff50eSJouni Malinen } 149f078f209SLuis R. Rodriguez 1509fc9ab0aSSujith /* Get a new beacon from mac80211 */ 1519fc9ab0aSSujith 152c52f33d0SJouni Malinen skb = ieee80211_beacon_get(hw, vif); 153a8fff50eSJouni Malinen bf->bf_mpdu = skb; 154a8fff50eSJouni Malinen if (skb == NULL) 155a8fff50eSJouni Malinen return NULL; 1564ed96f04SJouni Malinen ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = 1574ed96f04SJouni Malinen avp->tsf_adjust; 158980b24daSSujith 159147583c0SJouni Malinen info = IEEE80211_SKB_CB(skb); 160147583c0SJouni Malinen if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { 161147583c0SJouni Malinen /* 162147583c0SJouni Malinen * TODO: make sure the seq# gets assigned properly (vs. other 163147583c0SJouni Malinen * TX frames) 164147583c0SJouni Malinen */ 165147583c0SJouni Malinen struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 166b77f483fSSujith sc->tx.seq_no += 0x10; 167147583c0SJouni Malinen hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); 168b77f483fSSujith hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); 169147583c0SJouni Malinen } 170980b24daSSujith 171a8fff50eSJouni Malinen bf->bf_buf_addr = bf->bf_dmacontext = 1727da3c55cSGabor Juhos dma_map_single(sc->dev, skb->data, 1739fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 1747da3c55cSGabor Juhos if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { 175f8316df1SLuis R. Rodriguez dev_kfree_skb_any(skb); 176f8316df1SLuis R. Rodriguez bf->bf_mpdu = NULL; 177*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_FATAL, 178*c46917bbSLuis R. Rodriguez "dma_mapping_error on beaconing\n"); 179f8316df1SLuis R. Rodriguez return NULL; 180f8316df1SLuis R. Rodriguez } 181f078f209SLuis R. Rodriguez 182c52f33d0SJouni Malinen skb = ieee80211_get_buffered_bc(hw, vif); 183f078f209SLuis R. Rodriguez 184f078f209SLuis R. Rodriguez /* 185f078f209SLuis R. Rodriguez * if the CABQ traffic from previous DTIM is pending and the current 186f078f209SLuis R. Rodriguez * beacon is also a DTIM. 18717d7904dSSujith * 1) if there is only one vif let the cab traffic continue. 18817d7904dSSujith * 2) if there are more than one vif and we are using staggered 189f078f209SLuis R. Rodriguez * beacons, then drain the cabq by dropping all the frames in 19017d7904dSSujith * the cabq so that the current vifs cab traffic can be scheduled. 191f078f209SLuis R. Rodriguez */ 192f078f209SLuis R. Rodriguez spin_lock_bh(&cabq->axq_lock); 193f078f209SLuis R. Rodriguez cabq_depth = cabq->axq_depth; 194f078f209SLuis R. Rodriguez spin_unlock_bh(&cabq->axq_lock); 195f078f209SLuis R. Rodriguez 196e022edbdSJouni Malinen if (skb && cabq_depth) { 19717d7904dSSujith if (sc->nvifs > 1) { 198*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 1999fc9ab0aSSujith "Flushing previous cabq traffic\n"); 2009fc9ab0aSSujith ath_draintxq(sc, cabq, false); 201f078f209SLuis R. Rodriguez } 202f078f209SLuis R. Rodriguez } 203f078f209SLuis R. Rodriguez 204f078f209SLuis R. Rodriguez ath_beacon_setup(sc, avp, bf); 205f078f209SLuis R. Rodriguez 206e022edbdSJouni Malinen while (skb) { 207c52f33d0SJouni Malinen ath_tx_cabq(hw, skb); 208c52f33d0SJouni Malinen skb = ieee80211_get_buffered_bc(hw, vif); 209e022edbdSJouni Malinen } 210f078f209SLuis R. Rodriguez 211f078f209SLuis R. Rodriguez return bf; 212f078f209SLuis R. Rodriguez } 213f078f209SLuis R. Rodriguez 214f078f209SLuis R. Rodriguez /* 215f078f209SLuis R. Rodriguez * Startup beacon transmission for adhoc mode when they are sent entirely 216f078f209SLuis R. Rodriguez * by the hardware using the self-linked descriptor + veol trick. 217f078f209SLuis R. Rodriguez */ 2182c3db3d5SJouni Malinen static void ath_beacon_start_adhoc(struct ath_softc *sc, 2192c3db3d5SJouni Malinen struct ieee80211_vif *vif) 220f078f209SLuis R. Rodriguez { 221cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 222*c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(ah); 223f078f209SLuis R. Rodriguez struct ath_buf *bf; 22417d7904dSSujith struct ath_vif *avp; 225f078f209SLuis R. Rodriguez struct sk_buff *skb; 226f078f209SLuis R. Rodriguez 2275640b08eSSujith avp = (void *)vif->drv_priv; 228f078f209SLuis R. Rodriguez 2299fc9ab0aSSujith if (avp->av_bcbuf == NULL) 230f078f209SLuis R. Rodriguez return; 2319fc9ab0aSSujith 232f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 233a22be22aSSujith skb = bf->bf_mpdu; 234f078f209SLuis R. Rodriguez 235f078f209SLuis R. Rodriguez ath_beacon_setup(sc, avp, bf); 236f078f209SLuis R. Rodriguez 237f078f209SLuis R. Rodriguez /* NB: caller is known to have already stopped tx dma */ 238b77f483fSSujith ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); 239b77f483fSSujith ath9k_hw_txstart(ah, sc->beacon.beaconq); 240*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n", 241b77f483fSSujith sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc); 242f078f209SLuis R. Rodriguez } 243f078f209SLuis R. Rodriguez 244cbe61d8aSSujith int ath_beaconq_setup(struct ath_hw *ah) 245f078f209SLuis R. Rodriguez { 246ea9880fbSSujith struct ath9k_tx_queue_info qi; 247f078f209SLuis R. Rodriguez 2480345f37bSLuis R. Rodriguez memset(&qi, 0, sizeof(qi)); 249f078f209SLuis R. Rodriguez qi.tqi_aifs = 1; 250f078f209SLuis R. Rodriguez qi.tqi_cwmin = 0; 251f078f209SLuis R. Rodriguez qi.tqi_cwmax = 0; 252f078f209SLuis R. Rodriguez /* NB: don't enable any interrupts */ 253f078f209SLuis R. Rodriguez return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); 254f078f209SLuis R. Rodriguez } 255f078f209SLuis R. Rodriguez 256c52f33d0SJouni Malinen int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) 257f078f209SLuis R. Rodriguez { 258c52f33d0SJouni Malinen struct ath_softc *sc = aphy->sc; 259*c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 26017d7904dSSujith struct ath_vif *avp; 261f078f209SLuis R. Rodriguez struct ath_buf *bf; 262f078f209SLuis R. Rodriguez struct sk_buff *skb; 263459f5f90SSujith __le64 tstamp; 264f078f209SLuis R. Rodriguez 2655640b08eSSujith avp = (void *)vif->drv_priv; 266f078f209SLuis R. Rodriguez 267f078f209SLuis R. Rodriguez /* Allocate a beacon descriptor if we haven't done so. */ 268f078f209SLuis R. Rodriguez if (!avp->av_bcbuf) { 269980b24daSSujith /* Allocate beacon state for hostap/ibss. We know 270980b24daSSujith * a buffer is available. */ 271b77f483fSSujith avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, 272f078f209SLuis R. Rodriguez struct ath_buf, list); 273f078f209SLuis R. Rodriguez list_del(&avp->av_bcbuf->list); 274f078f209SLuis R. Rodriguez 2752660b81aSSujith if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || 2762660b81aSSujith !(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { 277f078f209SLuis R. Rodriguez int slot; 278f078f209SLuis R. Rodriguez /* 27917d7904dSSujith * Assign the vif to a beacon xmit slot. As 280f078f209SLuis R. Rodriguez * above, this cannot fail to find one. 281f078f209SLuis R. Rodriguez */ 282f078f209SLuis R. Rodriguez avp->av_bslot = 0; 283f078f209SLuis R. Rodriguez for (slot = 0; slot < ATH_BCBUF; slot++) 2842c3db3d5SJouni Malinen if (sc->beacon.bslot[slot] == NULL) { 285f078f209SLuis R. Rodriguez /* 286f078f209SLuis R. Rodriguez * XXX hack, space out slots to better 287f078f209SLuis R. Rodriguez * deal with misses 288f078f209SLuis R. Rodriguez */ 289f078f209SLuis R. Rodriguez if (slot+1 < ATH_BCBUF && 2902c3db3d5SJouni Malinen sc->beacon.bslot[slot+1] == NULL) { 291f078f209SLuis R. Rodriguez avp->av_bslot = slot+1; 292f078f209SLuis R. Rodriguez break; 293f078f209SLuis R. Rodriguez } 294f078f209SLuis R. Rodriguez avp->av_bslot = slot; 295f078f209SLuis R. Rodriguez /* NB: keep looking for a double slot */ 296f078f209SLuis R. Rodriguez } 2972c3db3d5SJouni Malinen BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL); 2982c3db3d5SJouni Malinen sc->beacon.bslot[avp->av_bslot] = vif; 299c52f33d0SJouni Malinen sc->beacon.bslot_aphy[avp->av_bslot] = aphy; 30017d7904dSSujith sc->nbcnvifs++; 301f078f209SLuis R. Rodriguez } 302f078f209SLuis R. Rodriguez } 303f078f209SLuis R. Rodriguez 304f078f209SLuis R. Rodriguez /* release the previous beacon frame, if it already exists. */ 305f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 306f078f209SLuis R. Rodriguez if (bf->bf_mpdu != NULL) { 307a22be22aSSujith skb = bf->bf_mpdu; 3087da3c55cSGabor Juhos dma_unmap_single(sc->dev, bf->bf_dmacontext, 3099fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 310f078f209SLuis R. Rodriguez dev_kfree_skb_any(skb); 311f078f209SLuis R. Rodriguez bf->bf_mpdu = NULL; 312f078f209SLuis R. Rodriguez } 313f078f209SLuis R. Rodriguez 3149fc9ab0aSSujith /* NB: the beacon data buffer must be 32-bit aligned. */ 3155640b08eSSujith skb = ieee80211_beacon_get(sc->hw, vif); 316f078f209SLuis R. Rodriguez if (skb == NULL) { 317*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, "cannot get skb\n"); 318f078f209SLuis R. Rodriguez return -ENOMEM; 319f078f209SLuis R. Rodriguez } 320f078f209SLuis R. Rodriguez 321459f5f90SSujith tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; 322b77f483fSSujith sc->beacon.bc_tstamp = le64_to_cpu(tstamp); 3234ed96f04SJouni Malinen /* Calculate a TSF adjustment factor required for staggered beacons. */ 324f078f209SLuis R. Rodriguez if (avp->av_bslot > 0) { 325f078f209SLuis R. Rodriguez u64 tsfadjust; 326f078f209SLuis R. Rodriguez int intval; 327f078f209SLuis R. Rodriguez 32857c4d7b4SJohannes Berg intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; 329f078f209SLuis R. Rodriguez 330f078f209SLuis R. Rodriguez /* 3314ed96f04SJouni Malinen * Calculate the TSF offset for this beacon slot, i.e., the 3324ed96f04SJouni Malinen * number of usecs that need to be added to the timestamp field 3334ed96f04SJouni Malinen * in Beacon and Probe Response frames. Beacon slot 0 is 3344ed96f04SJouni Malinen * processed at the correct offset, so it does not require TSF 3354ed96f04SJouni Malinen * adjustment. Other slots are adjusted to get the timestamp 3364ed96f04SJouni Malinen * close to the TBTT for the BSS. 337f078f209SLuis R. Rodriguez */ 3384ed96f04SJouni Malinen tsfadjust = intval * avp->av_bslot / ATH_BCBUF; 3394ed96f04SJouni Malinen avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); 340f078f209SLuis R. Rodriguez 341*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 342*c46917bbSLuis R. Rodriguez "stagger beacons, bslot %d intval " 343*c46917bbSLuis R. Rodriguez "%u tsfadjust %llu\n", 344f078f209SLuis R. Rodriguez avp->av_bslot, intval, (unsigned long long)tsfadjust); 345f078f209SLuis R. Rodriguez 3464ed96f04SJouni Malinen ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = 3474ed96f04SJouni Malinen avp->tsf_adjust; 3484ed96f04SJouni Malinen } else 3494ed96f04SJouni Malinen avp->tsf_adjust = cpu_to_le64(0); 350f078f209SLuis R. Rodriguez 351f8316df1SLuis R. Rodriguez bf->bf_mpdu = skb; 352a8fff50eSJouni Malinen bf->bf_buf_addr = bf->bf_dmacontext = 3537da3c55cSGabor Juhos dma_map_single(sc->dev, skb->data, 3549fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 3557da3c55cSGabor Juhos if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { 356f8316df1SLuis R. Rodriguez dev_kfree_skb_any(skb); 357f8316df1SLuis R. Rodriguez bf->bf_mpdu = NULL; 358*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_FATAL, 3599fc9ab0aSSujith "dma_mapping_error on beacon alloc\n"); 360f8316df1SLuis R. Rodriguez return -ENOMEM; 361f8316df1SLuis R. Rodriguez } 362f078f209SLuis R. Rodriguez 363f078f209SLuis R. Rodriguez return 0; 364f078f209SLuis R. Rodriguez } 365f078f209SLuis R. Rodriguez 36617d7904dSSujith void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp) 367f078f209SLuis R. Rodriguez { 368f078f209SLuis R. Rodriguez if (avp->av_bcbuf != NULL) { 369f078f209SLuis R. Rodriguez struct ath_buf *bf; 370f078f209SLuis R. Rodriguez 371f078f209SLuis R. Rodriguez if (avp->av_bslot != -1) { 3722c3db3d5SJouni Malinen sc->beacon.bslot[avp->av_bslot] = NULL; 373c52f33d0SJouni Malinen sc->beacon.bslot_aphy[avp->av_bslot] = NULL; 37417d7904dSSujith sc->nbcnvifs--; 375f078f209SLuis R. Rodriguez } 376f078f209SLuis R. Rodriguez 377f078f209SLuis R. Rodriguez bf = avp->av_bcbuf; 378f078f209SLuis R. Rodriguez if (bf->bf_mpdu != NULL) { 379a22be22aSSujith struct sk_buff *skb = bf->bf_mpdu; 3807da3c55cSGabor Juhos dma_unmap_single(sc->dev, bf->bf_dmacontext, 3819fc9ab0aSSujith skb->len, DMA_TO_DEVICE); 382f078f209SLuis R. Rodriguez dev_kfree_skb_any(skb); 383f078f209SLuis R. Rodriguez bf->bf_mpdu = NULL; 384f078f209SLuis R. Rodriguez } 385b77f483fSSujith list_add_tail(&bf->list, &sc->beacon.bbuf); 386f078f209SLuis R. Rodriguez 387f078f209SLuis R. Rodriguez avp->av_bcbuf = NULL; 388f078f209SLuis R. Rodriguez } 389f078f209SLuis R. Rodriguez } 390f078f209SLuis R. Rodriguez 3919fc9ab0aSSujith void ath_beacon_tasklet(unsigned long data) 392f078f209SLuis R. Rodriguez { 393f078f209SLuis R. Rodriguez struct ath_softc *sc = (struct ath_softc *)data; 394cbe61d8aSSujith struct ath_hw *ah = sc->sc_ah; 395*c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(ah); 396f078f209SLuis R. Rodriguez struct ath_buf *bf = NULL; 3972c3db3d5SJouni Malinen struct ieee80211_vif *vif; 398c52f33d0SJouni Malinen struct ath_wiphy *aphy; 3992c3db3d5SJouni Malinen int slot; 4009546aae0SSujith u32 bfaddr, bc = 0, tsftu; 401f078f209SLuis R. Rodriguez u64 tsf; 402f078f209SLuis R. Rodriguez u16 intval; 403f078f209SLuis R. Rodriguez 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 414b77f483fSSujith if (sc->beacon.bmisscnt < BSTUCK_THRESH) { 415*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 41604bd4638SSujith "missed %u consecutive beacons\n", 417b77f483fSSujith sc->beacon.bmisscnt); 418b77f483fSSujith } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { 419*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 42004bd4638SSujith "beacon is officially stuck\n"); 421b74444f8SJeff Hansen sc->sc_flags |= SC_OP_TSF_RESET; 4229546aae0SSujith ath_reset(sc, false); 423f078f209SLuis R. Rodriguez } 4249546aae0SSujith 425f078f209SLuis R. Rodriguez return; 426f078f209SLuis R. Rodriguez } 427980b24daSSujith 428b77f483fSSujith if (sc->beacon.bmisscnt != 0) { 429*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 43004bd4638SSujith "resume beacon xmit after %u misses\n", 431b77f483fSSujith sc->beacon.bmisscnt); 432b77f483fSSujith sc->beacon.bmisscnt = 0; 433f078f209SLuis R. Rodriguez } 434f078f209SLuis R. Rodriguez 435f078f209SLuis R. Rodriguez /* 436f078f209SLuis R. Rodriguez * Generate beacon frames. we are sending frames 437f078f209SLuis R. Rodriguez * staggered so calculate the slot for this frame based 438f078f209SLuis R. Rodriguez * on the tsf to safeguard against missing an swba. 439f078f209SLuis R. Rodriguez */ 440f078f209SLuis R. Rodriguez 44157c4d7b4SJohannes Berg intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; 442f078f209SLuis R. Rodriguez 443f078f209SLuis R. Rodriguez tsf = ath9k_hw_gettsf64(ah); 444f078f209SLuis R. Rodriguez tsftu = TSF_TO_TU(tsf>>32, tsf); 445f078f209SLuis R. Rodriguez slot = ((tsftu % intval) * ATH_BCBUF) / intval; 4464ed96f04SJouni Malinen /* 4474ed96f04SJouni Malinen * Reverse the slot order to get slot 0 on the TBTT offset that does 4484ed96f04SJouni Malinen * not require TSF adjustment and other slots adding 4494ed96f04SJouni Malinen * slot/ATH_BCBUF * beacon_int to timestamp. For example, with 4504ed96f04SJouni Malinen * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 .. 4514ed96f04SJouni Malinen * and slot 0 is at correct offset to TBTT. 4524ed96f04SJouni Malinen */ 4534ed96f04SJouni Malinen slot = ATH_BCBUF - slot - 1; 4544ed96f04SJouni Malinen vif = sc->beacon.bslot[slot]; 4554ed96f04SJouni Malinen aphy = sc->beacon.bslot_aphy[slot]; 456980b24daSSujith 457*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 4582c3db3d5SJouni Malinen "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", 4592c3db3d5SJouni Malinen slot, tsf, tsftu, intval, vif); 460980b24daSSujith 461f078f209SLuis R. Rodriguez bfaddr = 0; 4622c3db3d5SJouni Malinen if (vif) { 463c52f33d0SJouni Malinen bf = ath_beacon_generate(aphy->hw, vif); 464f078f209SLuis R. Rodriguez if (bf != NULL) { 465f078f209SLuis R. Rodriguez bfaddr = bf->bf_daddr; 466f078f209SLuis R. Rodriguez bc = 1; 467f078f209SLuis R. Rodriguez } 468f078f209SLuis R. Rodriguez } 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) { 487b77f483fSSujith sc->beacon.updateslot = COMMIT; /* commit next beacon */ 488b77f483fSSujith sc->beacon.slotupdate = slot; 489b77f483fSSujith } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) { 490b77f483fSSujith ath9k_hw_setslottime(sc->sc_ah, sc->beacon.slottime); 491b77f483fSSujith sc->beacon.updateslot = OK; 492ff37e337SSujith } 493f078f209SLuis R. Rodriguez if (bfaddr != 0) { 494f078f209SLuis R. Rodriguez /* 495f078f209SLuis R. Rodriguez * Stop any current dma and put the new frame(s) on the queue. 496f078f209SLuis R. Rodriguez * This should never fail since we check above that no frames 497f078f209SLuis R. Rodriguez * are still pending on the queue. 498f078f209SLuis R. Rodriguez */ 499b77f483fSSujith if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) { 500*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_FATAL, 501b77f483fSSujith "beacon queue %u did not stop?\n", sc->beacon.beaconq); 502f078f209SLuis R. Rodriguez } 503f078f209SLuis R. Rodriguez 504f078f209SLuis R. Rodriguez /* NB: cabq traffic should already be queued and primed */ 505b77f483fSSujith ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr); 506b77f483fSSujith ath9k_hw_txstart(ah, sc->beacon.beaconq); 507f078f209SLuis R. Rodriguez 50817d7904dSSujith sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */ 509f078f209SLuis R. Rodriguez } 510f078f209SLuis R. Rodriguez } 511f078f209SLuis R. Rodriguez 51221526d57SLuis R. Rodriguez static void ath9k_beacon_init(struct ath_softc *sc, 51321526d57SLuis R. Rodriguez u32 next_beacon, 51421526d57SLuis R. Rodriguez u32 beacon_period) 51521526d57SLuis R. Rodriguez { 51621526d57SLuis R. Rodriguez if (beacon_period & ATH9K_BEACON_RESET_TSF) 51721526d57SLuis R. Rodriguez ath9k_ps_wakeup(sc); 51821526d57SLuis R. Rodriguez 51921526d57SLuis R. Rodriguez ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period); 52021526d57SLuis R. Rodriguez 52121526d57SLuis R. Rodriguez if (beacon_period & ATH9K_BEACON_RESET_TSF) 52221526d57SLuis R. Rodriguez ath9k_ps_restore(sc); 52321526d57SLuis R. Rodriguez } 52421526d57SLuis R. Rodriguez 525f078f209SLuis R. Rodriguez /* 5265379c8a2SSujith * For multi-bss ap support beacons are either staggered evenly over N slots or 5275379c8a2SSujith * burst together. For the former arrange for the SWBA to be delivered for each 5285379c8a2SSujith * slot. Slots that are not occupied will generate nothing. 529f078f209SLuis R. Rodriguez */ 5305379c8a2SSujith static void ath_beacon_config_ap(struct ath_softc *sc, 531d31e20afSVasanthakumar Thiagarajan struct ath_beacon_config *conf) 532f078f209SLuis R. Rodriguez { 533980b24daSSujith u32 nexttbtt, intval; 534f078f209SLuis R. Rodriguez 535b238e90eSSujith /* Configure the timers only when the TSF has to be reset */ 536b238e90eSSujith 537b238e90eSSujith if (!(sc->sc_flags & SC_OP_TSF_RESET)) 538b238e90eSSujith return; 539b238e90eSSujith 540f078f209SLuis R. Rodriguez /* NB: the beacon interval is kept internally in TU's */ 5415379c8a2SSujith intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; 542f078f209SLuis R. Rodriguez intval /= ATH_BCBUF; /* for staggered beacons */ 5435379c8a2SSujith nexttbtt = intval; 5445379c8a2SSujith intval |= ATH9K_BEACON_RESET_TSF; 5455379c8a2SSujith 5465379c8a2SSujith /* 5475379c8a2SSujith * In AP mode we enable the beacon timers and SWBA interrupts to 5485379c8a2SSujith * prepare beacon frames. 5495379c8a2SSujith */ 5505379c8a2SSujith intval |= ATH9K_BEACON_ENA; 5515379c8a2SSujith sc->imask |= ATH9K_INT_SWBA; 5525379c8a2SSujith ath_beaconq_config(sc); 5535379c8a2SSujith 5545379c8a2SSujith /* Set the computed AP beacon timers */ 5555379c8a2SSujith 5565379c8a2SSujith ath9k_hw_set_interrupts(sc->sc_ah, 0); 55721526d57SLuis R. Rodriguez ath9k_beacon_init(sc, nexttbtt, intval); 5585379c8a2SSujith sc->beacon.bmisscnt = 0; 5595379c8a2SSujith ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); 560b238e90eSSujith 561b238e90eSSujith /* Clear the reset TSF flag, so that subsequent beacon updation 562b238e90eSSujith will not reset the HW TSF. */ 563b238e90eSSujith 564b238e90eSSujith sc->sc_flags &= ~SC_OP_TSF_RESET; 565f078f209SLuis R. Rodriguez } 566f078f209SLuis R. Rodriguez 5675379c8a2SSujith /* 5685379c8a2SSujith * This sets up the beacon timers according to the timestamp of the last 5695379c8a2SSujith * received beacon and the current TSF, configures PCF and DTIM 5705379c8a2SSujith * handling, programs the sleep registers so the hardware will wakeup in 5715379c8a2SSujith * time to receive beacons, and configures the beacon miss handling so 5725379c8a2SSujith * we'll receive a BMISS interrupt when we stop seeing beacons from the AP 5735379c8a2SSujith * we've associated with. 5745379c8a2SSujith */ 5755379c8a2SSujith static void ath_beacon_config_sta(struct ath_softc *sc, 576d31e20afSVasanthakumar Thiagarajan struct ath_beacon_config *conf) 5775379c8a2SSujith { 578*c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 579f078f209SLuis R. Rodriguez struct ath9k_beacon_state bs; 580f078f209SLuis R. Rodriguez int dtimperiod, dtimcount, sleepduration; 581f078f209SLuis R. Rodriguez int cfpperiod, cfpcount; 5825379c8a2SSujith u32 nexttbtt = 0, intval, tsftu; 5835379c8a2SSujith u64 tsf; 584267a9012SJouni Malinen int num_beacons, offset, dtim_dec_count, cfp_dec_count; 5855379c8a2SSujith 5865379c8a2SSujith memset(&bs, 0, sizeof(bs)); 5875379c8a2SSujith intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; 588f078f209SLuis R. Rodriguez 589f078f209SLuis R. Rodriguez /* 590f078f209SLuis R. Rodriguez * Setup dtim and cfp parameters according to 591f078f209SLuis R. Rodriguez * last beacon we received (which may be none). 592f078f209SLuis R. Rodriguez */ 5935379c8a2SSujith dtimperiod = conf->dtim_period; 594f078f209SLuis R. Rodriguez if (dtimperiod <= 0) /* NB: 0 if not known */ 595f078f209SLuis R. Rodriguez dtimperiod = 1; 5965379c8a2SSujith dtimcount = conf->dtim_count; 597f078f209SLuis R. Rodriguez if (dtimcount >= dtimperiod) /* NB: sanity check */ 598980b24daSSujith dtimcount = 0; 599f078f209SLuis R. Rodriguez cfpperiod = 1; /* NB: no PCF support yet */ 600f078f209SLuis R. Rodriguez cfpcount = 0; 601f078f209SLuis R. Rodriguez 6025379c8a2SSujith sleepduration = conf->listen_interval * intval; 603f078f209SLuis R. Rodriguez if (sleepduration <= 0) 604f078f209SLuis R. Rodriguez sleepduration = intval; 605f078f209SLuis R. Rodriguez 606f078f209SLuis R. Rodriguez /* 607f078f209SLuis R. Rodriguez * Pull nexttbtt forward to reflect the current 608f078f209SLuis R. Rodriguez * TSF and calculate dtim+cfp state for the result. 609f078f209SLuis R. Rodriguez */ 6105379c8a2SSujith tsf = ath9k_hw_gettsf64(sc->sc_ah); 611f078f209SLuis R. Rodriguez tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 612267a9012SJouni Malinen 613267a9012SJouni Malinen num_beacons = tsftu / intval + 1; 614267a9012SJouni Malinen offset = tsftu % intval; 615267a9012SJouni Malinen nexttbtt = tsftu - offset; 616267a9012SJouni Malinen if (offset) 617f078f209SLuis R. Rodriguez nexttbtt += intval; 618267a9012SJouni Malinen 619267a9012SJouni Malinen /* DTIM Beacon every dtimperiod Beacon */ 620267a9012SJouni Malinen dtim_dec_count = num_beacons % dtimperiod; 621267a9012SJouni Malinen /* CFP every cfpperiod DTIM Beacon */ 622267a9012SJouni Malinen cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; 623267a9012SJouni Malinen if (dtim_dec_count) 624267a9012SJouni Malinen cfp_dec_count++; 625267a9012SJouni Malinen 626267a9012SJouni Malinen dtimcount -= dtim_dec_count; 627267a9012SJouni Malinen if (dtimcount < 0) 628267a9012SJouni Malinen dtimcount += dtimperiod; 629267a9012SJouni Malinen 630267a9012SJouni Malinen cfpcount -= cfp_dec_count; 631267a9012SJouni Malinen if (cfpcount < 0) 632267a9012SJouni Malinen cfpcount += cfpperiod; 6335379c8a2SSujith 634f078f209SLuis R. Rodriguez bs.bs_intval = intval; 635f078f209SLuis R. Rodriguez bs.bs_nexttbtt = nexttbtt; 636f078f209SLuis R. Rodriguez bs.bs_dtimperiod = dtimperiod*intval; 637f078f209SLuis R. Rodriguez bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 638f078f209SLuis R. Rodriguez bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 639f078f209SLuis R. Rodriguez bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 640f078f209SLuis R. Rodriguez bs.bs_cfpmaxduration = 0; 641980b24daSSujith 642f078f209SLuis R. Rodriguez /* 6435379c8a2SSujith * Calculate the number of consecutive beacons to miss* before taking 6445379c8a2SSujith * a BMISS interrupt. The configuration is specified in TU so we only 6455379c8a2SSujith * need calculate based on the beacon interval. Note that we clamp the 646f078f209SLuis R. Rodriguez * result to at most 15 beacons. 647f078f209SLuis R. Rodriguez */ 648f078f209SLuis R. Rodriguez if (sleepduration > intval) { 6495379c8a2SSujith bs.bs_bmissthreshold = conf->listen_interval * 650f078f209SLuis R. Rodriguez ATH_DEFAULT_BMISS_LIMIT / 2; 651f078f209SLuis R. Rodriguez } else { 6525379c8a2SSujith bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval); 653f078f209SLuis R. Rodriguez if (bs.bs_bmissthreshold > 15) 654f078f209SLuis R. Rodriguez bs.bs_bmissthreshold = 15; 655f078f209SLuis R. Rodriguez else if (bs.bs_bmissthreshold <= 0) 656f078f209SLuis R. Rodriguez bs.bs_bmissthreshold = 1; 657f078f209SLuis R. Rodriguez } 658f078f209SLuis R. Rodriguez 659f078f209SLuis R. Rodriguez /* 6605379c8a2SSujith * Calculate sleep duration. The configuration is given in ms. 6615379c8a2SSujith * We ensure a multiple of the beacon period is used. Also, if the sleep 6625379c8a2SSujith * duration is greater than the DTIM period then it makes senses 663f078f209SLuis R. Rodriguez * to make it a multiple of that. 664f078f209SLuis R. Rodriguez * 665f078f209SLuis R. Rodriguez * XXX fixed at 100ms 666f078f209SLuis R. Rodriguez */ 667f078f209SLuis R. Rodriguez 6685379c8a2SSujith bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); 669f078f209SLuis R. Rodriguez if (bs.bs_sleepduration > bs.bs_dtimperiod) 670f078f209SLuis R. Rodriguez bs.bs_sleepduration = bs.bs_dtimperiod; 671f078f209SLuis R. Rodriguez 6724af9cf4fSSujith /* TSF out of range threshold fixed at 1 second */ 6734af9cf4fSSujith bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; 6744af9cf4fSSujith 675*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); 676*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 6779fc9ab0aSSujith "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", 6789fc9ab0aSSujith bs.bs_bmissthreshold, bs.bs_sleepduration, 6799fc9ab0aSSujith bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); 680f078f209SLuis R. Rodriguez 6815379c8a2SSujith /* Set the computed STA beacon timers */ 6829fc9ab0aSSujith 6835379c8a2SSujith ath9k_hw_set_interrupts(sc->sc_ah, 0); 6845379c8a2SSujith ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs); 6855379c8a2SSujith sc->imask |= ATH9K_INT_BMISS; 6865379c8a2SSujith ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); 6875379c8a2SSujith } 6885379c8a2SSujith 6895379c8a2SSujith static void ath_beacon_config_adhoc(struct ath_softc *sc, 6905379c8a2SSujith struct ath_beacon_config *conf, 6912c3db3d5SJouni Malinen struct ieee80211_vif *vif) 6925379c8a2SSujith { 693*c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 6945379c8a2SSujith u64 tsf; 6955379c8a2SSujith u32 tsftu, intval, nexttbtt; 6965379c8a2SSujith 6975379c8a2SSujith intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; 6985379c8a2SSujith 699546256fbSJouni Malinen 7009fc9ab0aSSujith /* Pull nexttbtt forward to reflect the current TSF */ 7015379c8a2SSujith 7025379c8a2SSujith nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp); 7035379c8a2SSujith if (nexttbtt == 0) 7045379c8a2SSujith nexttbtt = intval; 7055379c8a2SSujith else if (intval) 7065379c8a2SSujith nexttbtt = roundup(nexttbtt, intval); 7075379c8a2SSujith 7085379c8a2SSujith tsf = ath9k_hw_gettsf64(sc->sc_ah); 7095379c8a2SSujith tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE; 710f078f209SLuis R. Rodriguez do { 711f078f209SLuis R. Rodriguez nexttbtt += intval; 712f078f209SLuis R. Rodriguez } while (nexttbtt < tsftu); 7135379c8a2SSujith 714*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_BEACON, 71504bd4638SSujith "IBSS nexttbtt %u intval %u (%u)\n", 7165379c8a2SSujith nexttbtt, intval, conf->beacon_interval); 717f078f209SLuis R. Rodriguez 718f078f209SLuis R. Rodriguez /* 7195379c8a2SSujith * In IBSS mode enable the beacon timers but only enable SWBA interrupts 7205379c8a2SSujith * if we need to manually prepare beacon frames. Otherwise we use a 7215379c8a2SSujith * self-linked tx descriptor and let the hardware deal with things. 722f078f209SLuis R. Rodriguez */ 723f078f209SLuis R. Rodriguez intval |= ATH9K_BEACON_ENA; 7245379c8a2SSujith if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) 72517d7904dSSujith sc->imask |= ATH9K_INT_SWBA; 7269fc9ab0aSSujith 7275379c8a2SSujith ath_beaconq_config(sc); 7285379c8a2SSujith 7295379c8a2SSujith /* Set the computed ADHOC beacon timers */ 7305379c8a2SSujith 7315379c8a2SSujith ath9k_hw_set_interrupts(sc->sc_ah, 0); 73221526d57SLuis R. Rodriguez ath9k_beacon_init(sc, nexttbtt, intval); 733b77f483fSSujith sc->beacon.bmisscnt = 0; 7345379c8a2SSujith ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); 7359fc9ab0aSSujith 7366b96f93eSVasanthakumar Thiagarajan /* FIXME: Handle properly when vif is NULL */ 7376b96f93eSVasanthakumar Thiagarajan if (vif && sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL) 7382c3db3d5SJouni Malinen ath_beacon_start_adhoc(sc, vif); 739f078f209SLuis R. Rodriguez } 7405379c8a2SSujith 7412c3db3d5SJouni Malinen void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) 7425379c8a2SSujith { 7436b96f93eSVasanthakumar Thiagarajan struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; 744*c46917bbSLuis R. Rodriguez struct ath_common *common = ath9k_hw_common(sc->sc_ah); 7456b96f93eSVasanthakumar Thiagarajan enum nl80211_iftype iftype; 7465379c8a2SSujith 7475379c8a2SSujith /* Setup the beacon configuration parameters */ 7485379c8a2SSujith 7492c3db3d5SJouni Malinen if (vif) { 7506b96f93eSVasanthakumar Thiagarajan struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 7515379c8a2SSujith 7526b96f93eSVasanthakumar Thiagarajan iftype = vif->type; 7536b96f93eSVasanthakumar Thiagarajan 7546b96f93eSVasanthakumar Thiagarajan cur_conf->beacon_interval = bss_conf->beacon_int; 7556b96f93eSVasanthakumar Thiagarajan cur_conf->dtim_period = bss_conf->dtim_period; 7566b96f93eSVasanthakumar Thiagarajan cur_conf->listen_interval = 1; 7576b96f93eSVasanthakumar Thiagarajan cur_conf->dtim_count = 1; 7586b96f93eSVasanthakumar Thiagarajan cur_conf->bmiss_timeout = 7596b96f93eSVasanthakumar Thiagarajan ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; 7606b96f93eSVasanthakumar Thiagarajan } else { 7616b96f93eSVasanthakumar Thiagarajan iftype = sc->sc_ah->opmode; 7626b96f93eSVasanthakumar Thiagarajan } 7636b96f93eSVasanthakumar Thiagarajan 764c4f9f16bSVasanthakumar Thiagarajan /* 765c4f9f16bSVasanthakumar Thiagarajan * It looks like mac80211 may end up using beacon interval of zero in 766c4f9f16bSVasanthakumar Thiagarajan * some cases (at least for mesh point). Avoid getting into an 767c4f9f16bSVasanthakumar Thiagarajan * infinite loop by using a bit safer value instead. To be safe, 768c4f9f16bSVasanthakumar Thiagarajan * do sanity check on beacon interval for all operating modes. 769c4f9f16bSVasanthakumar Thiagarajan */ 770c4f9f16bSVasanthakumar Thiagarajan if (cur_conf->beacon_interval == 0) 771c4f9f16bSVasanthakumar Thiagarajan cur_conf->beacon_interval = 100; 7726b96f93eSVasanthakumar Thiagarajan 7736b96f93eSVasanthakumar Thiagarajan switch (iftype) { 7745379c8a2SSujith case NL80211_IFTYPE_AP: 7756b96f93eSVasanthakumar Thiagarajan ath_beacon_config_ap(sc, cur_conf); 7765379c8a2SSujith break; 7775379c8a2SSujith case NL80211_IFTYPE_ADHOC: 7789cb5412bSPat Erley case NL80211_IFTYPE_MESH_POINT: 7796b96f93eSVasanthakumar Thiagarajan ath_beacon_config_adhoc(sc, cur_conf, vif); 7805379c8a2SSujith break; 7815379c8a2SSujith case NL80211_IFTYPE_STATION: 7826b96f93eSVasanthakumar Thiagarajan ath_beacon_config_sta(sc, cur_conf); 7835379c8a2SSujith break; 7845379c8a2SSujith default: 785*c46917bbSLuis R. Rodriguez ath_print(common, ATH_DBG_CONFIG, 7865379c8a2SSujith "Unsupported beaconing mode\n"); 7875379c8a2SSujith return; 788f078f209SLuis R. Rodriguez } 789f078f209SLuis R. Rodriguez 790672840acSSujith sc->sc_flags |= SC_OP_BEACONS; 791f078f209SLuis R. Rodriguez } 792