18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include "ath9k.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define FUDGE 2 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void ath9k_reset_beacon_status(struct ath_softc *sc) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci sc->beacon.tx_processed = false; 258c2ecf20Sopenharmony_ci sc->beacon.tx_last = false; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * This function will modify certain transmit queue properties depending on 308c2ecf20Sopenharmony_ci * the operating mode of the station (AP or AdHoc). Parameters are AIFS 318c2ecf20Sopenharmony_ci * settings and channel width min/max 328c2ecf20Sopenharmony_ci*/ 338c2ecf20Sopenharmony_cistatic void ath9k_beaconq_config(struct ath_softc *sc) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 368c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 378c2ecf20Sopenharmony_ci struct ath9k_tx_queue_info qi, qi_be; 388c2ecf20Sopenharmony_ci struct ath_txq *txq; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || 438c2ecf20Sopenharmony_ci sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) { 448c2ecf20Sopenharmony_ci /* Always burst out beacon and CAB traffic. */ 458c2ecf20Sopenharmony_ci qi.tqi_aifs = 1; 468c2ecf20Sopenharmony_ci qi.tqi_cwmin = 0; 478c2ecf20Sopenharmony_ci qi.tqi_cwmax = 0; 488c2ecf20Sopenharmony_ci } else { 498c2ecf20Sopenharmony_ci /* Adhoc mode; important thing is to use 2x cwmin. */ 508c2ecf20Sopenharmony_ci txq = sc->tx.txq_map[IEEE80211_AC_BE]; 518c2ecf20Sopenharmony_ci ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be); 528c2ecf20Sopenharmony_ci qi.tqi_aifs = qi_be.tqi_aifs; 538c2ecf20Sopenharmony_ci if (ah->slottime == 20) 548c2ecf20Sopenharmony_ci qi.tqi_cwmin = 2*qi_be.tqi_cwmin; 558c2ecf20Sopenharmony_ci else 568c2ecf20Sopenharmony_ci qi.tqi_cwmin = 4*qi_be.tqi_cwmin; 578c2ecf20Sopenharmony_ci qi.tqi_cwmax = qi_be.tqi_cwmax; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) { 618c2ecf20Sopenharmony_ci ath_err(common, "Unable to update h/w beacon queue parameters\n"); 628c2ecf20Sopenharmony_ci } else { 638c2ecf20Sopenharmony_ci ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * Associates the beacon frame buffer with a transmit descriptor. Will set 698c2ecf20Sopenharmony_ci * up rate codes, and channel flags. Beacons are always sent out at the 708c2ecf20Sopenharmony_ci * lowest rate, and are not retried. 718c2ecf20Sopenharmony_ci*/ 728c2ecf20Sopenharmony_cistatic void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, 738c2ecf20Sopenharmony_ci struct ath_buf *bf, int rateidx) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct sk_buff *skb = bf->bf_mpdu; 768c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 778c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 788c2ecf20Sopenharmony_ci struct ath_tx_info info; 798c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 808c2ecf20Sopenharmony_ci u8 chainmask = ah->txchainmask; 818c2ecf20Sopenharmony_ci u8 i, rate = 0; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci sband = &common->sbands[sc->cur_chandef.chan->band]; 848c2ecf20Sopenharmony_ci rate = sband->bitrates[rateidx].hw_value; 858c2ecf20Sopenharmony_ci if (vif->bss_conf.use_short_preamble) 868c2ecf20Sopenharmony_ci rate |= sband->bitrates[rateidx].hw_value_short; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 898c2ecf20Sopenharmony_ci info.pkt_len = skb->len + FCS_LEN; 908c2ecf20Sopenharmony_ci info.type = ATH9K_PKT_TYPE_BEACON; 918c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 928c2ecf20Sopenharmony_ci info.txpower[i] = MAX_RATE_POWER; 938c2ecf20Sopenharmony_ci info.keyix = ATH9K_TXKEYIX_INVALID; 948c2ecf20Sopenharmony_ci info.keytype = ATH9K_KEY_TYPE_CLEAR; 958c2ecf20Sopenharmony_ci info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci info.buf_addr[0] = bf->bf_buf_addr; 988c2ecf20Sopenharmony_ci info.buf_len[0] = roundup(skb->len, 4); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci info.is_first = true; 1018c2ecf20Sopenharmony_ci info.is_last = true; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci info.qcu = sc->beacon.beaconq; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci info.rates[0].Tries = 1; 1068c2ecf20Sopenharmony_ci info.rates[0].Rate = rate; 1078c2ecf20Sopenharmony_ci info.rates[0].ChSel = ath_txchainmask_reduction(sc, chainmask, rate); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, 1138c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 1168c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 1178c2ecf20Sopenharmony_ci struct ath_buf *bf; 1188c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 1198c2ecf20Sopenharmony_ci struct sk_buff *skb; 1208c2ecf20Sopenharmony_ci struct ath_txq *cabq = sc->beacon.cabq; 1218c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 1228c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt_hdr; 1238c2ecf20Sopenharmony_ci int cabq_depth; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (avp->av_bcbuf == NULL) 1268c2ecf20Sopenharmony_ci return NULL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci bf = avp->av_bcbuf; 1298c2ecf20Sopenharmony_ci skb = bf->bf_mpdu; 1308c2ecf20Sopenharmony_ci if (skb) { 1318c2ecf20Sopenharmony_ci dma_unmap_single(sc->dev, bf->bf_buf_addr, 1328c2ecf20Sopenharmony_ci skb->len, DMA_TO_DEVICE); 1338c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1348c2ecf20Sopenharmony_ci bf->bf_buf_addr = 0; 1358c2ecf20Sopenharmony_ci bf->bf_mpdu = NULL; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci skb = ieee80211_beacon_get(hw, vif); 1398c2ecf20Sopenharmony_ci if (skb == NULL) 1408c2ecf20Sopenharmony_ci return NULL; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci bf->bf_mpdu = skb; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci mgmt_hdr = (struct ieee80211_mgmt *)skb->data; 1458c2ecf20Sopenharmony_ci mgmt_hdr->u.beacon.timestamp = avp->tsf_adjust; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci ath_assign_seq(common, skb); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Always assign NOA attr when MCC enabled */ 1528c2ecf20Sopenharmony_ci if (ath9k_is_chanctx_enabled()) 1538c2ecf20Sopenharmony_ci ath9k_beacon_add_noa(sc, avp, skb); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, 1568c2ecf20Sopenharmony_ci skb->len, DMA_TO_DEVICE); 1578c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { 1588c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1598c2ecf20Sopenharmony_ci bf->bf_mpdu = NULL; 1608c2ecf20Sopenharmony_ci bf->bf_buf_addr = 0; 1618c2ecf20Sopenharmony_ci ath_err(common, "dma_mapping_error on beaconing\n"); 1628c2ecf20Sopenharmony_ci return NULL; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci skb = ieee80211_get_buffered_bc(hw, vif); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * if the CABQ traffic from previous DTIM is pending and the current 1698c2ecf20Sopenharmony_ci * beacon is also a DTIM. 1708c2ecf20Sopenharmony_ci * 1) if there is only one vif let the cab traffic continue. 1718c2ecf20Sopenharmony_ci * 2) if there are more than one vif and we are using staggered 1728c2ecf20Sopenharmony_ci * beacons, then drain the cabq by dropping all the frames in 1738c2ecf20Sopenharmony_ci * the cabq so that the current vifs cab traffic can be scheduled. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci spin_lock_bh(&cabq->axq_lock); 1768c2ecf20Sopenharmony_ci cabq_depth = cabq->axq_depth; 1778c2ecf20Sopenharmony_ci spin_unlock_bh(&cabq->axq_lock); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (skb && cabq_depth) { 1808c2ecf20Sopenharmony_ci if (sc->cur_chan->nvifs > 1) { 1818c2ecf20Sopenharmony_ci ath_dbg(common, BEACON, 1828c2ecf20Sopenharmony_ci "Flushing previous cabq traffic\n"); 1838c2ecf20Sopenharmony_ci ath_draintxq(sc, cabq); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (skb) 1908c2ecf20Sopenharmony_ci ath_tx_cabq(hw, vif, skb); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return bf; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_civoid ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 1988c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 1998c2ecf20Sopenharmony_ci int slot; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf, struct ath_buf, list); 2028c2ecf20Sopenharmony_ci list_del(&avp->av_bcbuf->list); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci for (slot = 0; slot < ATH_BCBUF; slot++) { 2058c2ecf20Sopenharmony_ci if (sc->beacon.bslot[slot] == NULL) { 2068c2ecf20Sopenharmony_ci avp->av_bslot = slot; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci sc->beacon.bslot[avp->av_bslot] = vif; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n", 2148c2ecf20Sopenharmony_ci avp->av_bslot); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_civoid ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 2208c2ecf20Sopenharmony_ci struct ath_vif *avp = (void *)vif->drv_priv; 2218c2ecf20Sopenharmony_ci struct ath_buf *bf = avp->av_bcbuf; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n", 2248c2ecf20Sopenharmony_ci avp->av_bslot); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci tasklet_disable(&sc->bcon_tasklet); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (bf && bf->bf_mpdu) { 2298c2ecf20Sopenharmony_ci struct sk_buff *skb = bf->bf_mpdu; 2308c2ecf20Sopenharmony_ci dma_unmap_single(sc->dev, bf->bf_buf_addr, 2318c2ecf20Sopenharmony_ci skb->len, DMA_TO_DEVICE); 2328c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2338c2ecf20Sopenharmony_ci bf->bf_mpdu = NULL; 2348c2ecf20Sopenharmony_ci bf->bf_buf_addr = 0; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci avp->av_bcbuf = NULL; 2388c2ecf20Sopenharmony_ci sc->beacon.bslot[avp->av_bslot] = NULL; 2398c2ecf20Sopenharmony_ci list_add_tail(&bf->list, &sc->beacon.bbuf); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci tasklet_enable(&sc->bcon_tasklet); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_civoid ath9k_beacon_ensure_primary_slot(struct ath_softc *sc) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 2478c2ecf20Sopenharmony_ci struct ieee80211_vif *vif; 2488c2ecf20Sopenharmony_ci struct ath_vif *avp; 2498c2ecf20Sopenharmony_ci s64 tsfadjust; 2508c2ecf20Sopenharmony_ci u32 offset; 2518c2ecf20Sopenharmony_ci int first_slot = ATH_BCBUF; 2528c2ecf20Sopenharmony_ci int slot; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci tasklet_disable(&sc->bcon_tasklet); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Find first taken slot. */ 2578c2ecf20Sopenharmony_ci for (slot = 0; slot < ATH_BCBUF; slot++) { 2588c2ecf20Sopenharmony_ci if (sc->beacon.bslot[slot]) { 2598c2ecf20Sopenharmony_ci first_slot = slot; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci if (first_slot == 0) 2648c2ecf20Sopenharmony_ci goto out; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Re-enumarate all slots, moving them forward. */ 2678c2ecf20Sopenharmony_ci for (slot = 0; slot < ATH_BCBUF; slot++) { 2688c2ecf20Sopenharmony_ci if (slot + first_slot < ATH_BCBUF) { 2698c2ecf20Sopenharmony_ci vif = sc->beacon.bslot[slot + first_slot]; 2708c2ecf20Sopenharmony_ci sc->beacon.bslot[slot] = vif; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (vif) { 2738c2ecf20Sopenharmony_ci avp = (void *)vif->drv_priv; 2748c2ecf20Sopenharmony_ci avp->av_bslot = slot; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } else { 2778c2ecf20Sopenharmony_ci sc->beacon.bslot[slot] = NULL; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci vif = sc->beacon.bslot[0]; 2828c2ecf20Sopenharmony_ci if (WARN_ON(!vif)) 2838c2ecf20Sopenharmony_ci goto out; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Get the tsf_adjust value for the new first slot. */ 2868c2ecf20Sopenharmony_ci avp = (void *)vif->drv_priv; 2878c2ecf20Sopenharmony_ci tsfadjust = le64_to_cpu(avp->tsf_adjust); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, 2908c2ecf20Sopenharmony_ci "Adjusting global TSF after beacon slot reassignment: %lld\n", 2918c2ecf20Sopenharmony_ci (signed long long)tsfadjust); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Modify TSF as required and update the HW. */ 2948c2ecf20Sopenharmony_ci avp->chanctx->tsf_val += tsfadjust; 2958c2ecf20Sopenharmony_ci if (sc->cur_chan == avp->chanctx) { 2968c2ecf20Sopenharmony_ci offset = ath9k_hw_get_tsf_offset(&avp->chanctx->tsf_ts, NULL); 2978c2ecf20Sopenharmony_ci ath9k_hw_settsf64(sc->sc_ah, avp->chanctx->tsf_val + offset); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* The slots tsf_adjust will be updated by ath9k_beacon_config later. */ 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciout: 3038c2ecf20Sopenharmony_ci tasklet_enable(&sc->bcon_tasklet); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int ath9k_beacon_choose_slot(struct ath_softc *sc) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 3098c2ecf20Sopenharmony_ci struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; 3108c2ecf20Sopenharmony_ci u16 intval; 3118c2ecf20Sopenharmony_ci u32 tsftu; 3128c2ecf20Sopenharmony_ci u64 tsf; 3138c2ecf20Sopenharmony_ci int slot; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (sc->sc_ah->opmode != NL80211_IFTYPE_AP && 3168c2ecf20Sopenharmony_ci sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) { 3178c2ecf20Sopenharmony_ci ath_dbg(common, BEACON, "slot 0, tsf: %llu\n", 3188c2ecf20Sopenharmony_ci ath9k_hw_gettsf64(sc->sc_ah)); 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci intval = cur_conf->beacon_interval ? : ATH_DEFAULT_BINTVAL; 3238c2ecf20Sopenharmony_ci tsf = ath9k_hw_gettsf64(sc->sc_ah); 3248c2ecf20Sopenharmony_ci tsf += TU_TO_USEC(sc->sc_ah->config.sw_beacon_response_time); 3258c2ecf20Sopenharmony_ci tsftu = TSF_TO_TU((tsf * ATH_BCBUF) >>32, tsf * ATH_BCBUF); 3268c2ecf20Sopenharmony_ci slot = (tsftu % (intval * ATH_BCBUF)) / intval; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ath_dbg(common, BEACON, "slot: %d tsf: %llu tsftu: %u\n", 3298c2ecf20Sopenharmony_ci slot, tsf, tsftu / ATH_BCBUF); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return slot; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic void ath9k_set_tsfadjust(struct ath_softc *sc, 3358c2ecf20Sopenharmony_ci struct ath_beacon_config *cur_conf) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 3388c2ecf20Sopenharmony_ci s64 tsfadjust; 3398c2ecf20Sopenharmony_ci int slot; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci for (slot = 0; slot < ATH_BCBUF; slot++) { 3428c2ecf20Sopenharmony_ci struct ath_vif *avp; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!sc->beacon.bslot[slot]) 3458c2ecf20Sopenharmony_ci continue; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci avp = (void *)sc->beacon.bslot[slot]->drv_priv; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* tsf_adjust is added to the TSF value. We send out the 3508c2ecf20Sopenharmony_ci * beacon late, so need to adjust the TSF starting point to be 3518c2ecf20Sopenharmony_ci * later in time (i.e. the theoretical first beacon has a TSF 3528c2ecf20Sopenharmony_ci * of 0 after correction). 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci tsfadjust = cur_conf->beacon_interval * avp->av_bslot; 3558c2ecf20Sopenharmony_ci tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF; 3568c2ecf20Sopenharmony_ci avp->tsf_adjust = cpu_to_le64(tsfadjust); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n", 3598c2ecf20Sopenharmony_ci (signed long long)tsfadjust, avp->av_bslot); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cibool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci if (!vif || !vif->csa_active) 3668c2ecf20Sopenharmony_ci return false; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!ieee80211_beacon_cntdwn_is_complete(vif)) 3698c2ecf20Sopenharmony_ci return false; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ieee80211_csa_finish(vif); 3728c2ecf20Sopenharmony_ci return true; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void ath9k_csa_update_vif(void *data, u8 *mac, struct ieee80211_vif *vif) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct ath_softc *sc = data; 3788c2ecf20Sopenharmony_ci ath9k_csa_is_finished(sc, vif); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_civoid ath9k_csa_update(struct ath_softc *sc) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic(sc->hw, 3848c2ecf20Sopenharmony_ci IEEE80211_IFACE_ITER_NORMAL, 3858c2ecf20Sopenharmony_ci ath9k_csa_update_vif, sc); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_civoid ath9k_beacon_tasklet(struct tasklet_struct *t) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct ath_softc *sc = from_tasklet(sc, t, bcon_tasklet); 3918c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 3928c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 3938c2ecf20Sopenharmony_ci struct ath_buf *bf = NULL; 3948c2ecf20Sopenharmony_ci struct ieee80211_vif *vif; 3958c2ecf20Sopenharmony_ci bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA); 3968c2ecf20Sopenharmony_ci int slot; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) { 3998c2ecf20Sopenharmony_ci ath_dbg(common, RESET, 4008c2ecf20Sopenharmony_ci "reset work is pending, skip beaconing now\n"); 4018c2ecf20Sopenharmony_ci return; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * Check if the previous beacon has gone out. If 4068c2ecf20Sopenharmony_ci * not don't try to post another, skip this period 4078c2ecf20Sopenharmony_ci * and wait for the next. Missed beacons indicate 4088c2ecf20Sopenharmony_ci * a problem and should not occur. If we miss too 4098c2ecf20Sopenharmony_ci * many consecutive beacons reset the device. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { 4128c2ecf20Sopenharmony_ci sc->beacon.bmisscnt++; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ath9k_hw_check_nav(ah); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * If the previous beacon has not been transmitted 4188c2ecf20Sopenharmony_ci * and a MAC/BB hang has been identified, return 4198c2ecf20Sopenharmony_ci * here because a chip reset would have been 4208c2ecf20Sopenharmony_ci * initiated. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci if (!ath_hw_check(sc)) 4238c2ecf20Sopenharmony_ci return; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) { 4268c2ecf20Sopenharmony_ci ath_dbg(common, BSTUCK, 4278c2ecf20Sopenharmony_ci "missed %u consecutive beacons\n", 4288c2ecf20Sopenharmony_ci sc->beacon.bmisscnt); 4298c2ecf20Sopenharmony_ci ath9k_hw_stop_dma_queue(ah, sc->beacon.beaconq); 4308c2ecf20Sopenharmony_ci if (sc->beacon.bmisscnt > 3) 4318c2ecf20Sopenharmony_ci ath9k_hw_bstuck_nfcal(ah); 4328c2ecf20Sopenharmony_ci } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { 4338c2ecf20Sopenharmony_ci ath_dbg(common, BSTUCK, "beacon is officially stuck\n"); 4348c2ecf20Sopenharmony_ci sc->beacon.bmisscnt = 0; 4358c2ecf20Sopenharmony_ci ath9k_queue_reset(sc, RESET_TYPE_BEACON_STUCK); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci slot = ath9k_beacon_choose_slot(sc); 4428c2ecf20Sopenharmony_ci vif = sc->beacon.bslot[slot]; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* EDMA devices check that in the tx completion function. */ 4458c2ecf20Sopenharmony_ci if (!edma) { 4468c2ecf20Sopenharmony_ci if (ath9k_is_chanctx_enabled()) { 4478c2ecf20Sopenharmony_ci ath_chanctx_beacon_sent_ev(sc, 4488c2ecf20Sopenharmony_ci ATH_CHANCTX_EVENT_BEACON_SENT); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (ath9k_csa_is_finished(sc, vif)) 4528c2ecf20Sopenharmony_ci return; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!vif || !vif->bss_conf.enable_beacon) 4568c2ecf20Sopenharmony_ci return; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (ath9k_is_chanctx_enabled()) { 4598c2ecf20Sopenharmony_ci ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE); 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci bf = ath9k_beacon_generate(sc->hw, vif); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (sc->beacon.bmisscnt != 0) { 4658c2ecf20Sopenharmony_ci ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n", 4668c2ecf20Sopenharmony_ci sc->beacon.bmisscnt); 4678c2ecf20Sopenharmony_ci sc->beacon.bmisscnt = 0; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * Handle slot time change when a non-ERP station joins/leaves 4728c2ecf20Sopenharmony_ci * an 11g network. The 802.11 layer notifies us via callback, 4738c2ecf20Sopenharmony_ci * we mark updateslot, then wait one beacon before effecting 4748c2ecf20Sopenharmony_ci * the change. This gives associated stations at least one 4758c2ecf20Sopenharmony_ci * beacon interval to note the state change. 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * NB: The slot time change state machine is clocked according 4788c2ecf20Sopenharmony_ci * to whether we are bursting or staggering beacons. We 4798c2ecf20Sopenharmony_ci * recognize the request to update and record the current 4808c2ecf20Sopenharmony_ci * slot then don't transition until that slot is reached 4818c2ecf20Sopenharmony_ci * again. If we miss a beacon for that slot then we'll be 4828c2ecf20Sopenharmony_ci * slow to transition but we'll be sure at least one beacon 4838c2ecf20Sopenharmony_ci * interval has passed. When bursting slot is always left 4848c2ecf20Sopenharmony_ci * set to ATH_BCBUF so this check is a noop. 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ci if (sc->beacon.updateslot == UPDATE) { 4878c2ecf20Sopenharmony_ci sc->beacon.updateslot = COMMIT; 4888c2ecf20Sopenharmony_ci sc->beacon.slotupdate = slot; 4898c2ecf20Sopenharmony_ci } else if (sc->beacon.updateslot == COMMIT && 4908c2ecf20Sopenharmony_ci sc->beacon.slotupdate == slot) { 4918c2ecf20Sopenharmony_ci ah->slottime = sc->beacon.slottime; 4928c2ecf20Sopenharmony_ci ath9k_hw_init_global_settings(ah); 4938c2ecf20Sopenharmony_ci sc->beacon.updateslot = OK; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (bf) { 4978c2ecf20Sopenharmony_ci ath9k_reset_beacon_status(sc); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ath_dbg(common, BEACON, 5008c2ecf20Sopenharmony_ci "Transmitting beacon for slot: %d\n", slot); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* NB: cabq traffic should already be queued and primed */ 5038c2ecf20Sopenharmony_ci ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (!edma) 5068c2ecf20Sopenharmony_ci ath9k_hw_txstart(ah, sc->beacon.beaconq); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* 5118c2ecf20Sopenharmony_ci * Both nexttbtt and intval have to be in usecs. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_cistatic void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, 5148c2ecf20Sopenharmony_ci u32 intval) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci ath9k_hw_disable_interrupts(ah); 5198c2ecf20Sopenharmony_ci ath9k_beaconq_config(sc); 5208c2ecf20Sopenharmony_ci ath9k_hw_beaconinit(ah, nexttbtt, intval); 5218c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_SWBA; 5228c2ecf20Sopenharmony_ci sc->beacon.bmisscnt = 0; 5238c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 5248c2ecf20Sopenharmony_ci ath9k_hw_enable_interrupts(ah); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void ath9k_beacon_stop(struct ath_softc *sc) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci ath9k_hw_disable_interrupts(sc->sc_ah); 5308c2ecf20Sopenharmony_ci sc->sc_ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); 5318c2ecf20Sopenharmony_ci sc->beacon.bmisscnt = 0; 5328c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(sc->sc_ah); 5338c2ecf20Sopenharmony_ci ath9k_hw_enable_interrupts(sc->sc_ah); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/* 5378c2ecf20Sopenharmony_ci * For multi-bss ap support beacons are either staggered evenly over N slots or 5388c2ecf20Sopenharmony_ci * burst together. For the former arrange for the SWBA to be delivered for each 5398c2ecf20Sopenharmony_ci * slot. Slots that are not occupied will generate nothing. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_cistatic void ath9k_beacon_config_ap(struct ath_softc *sc, 5428c2ecf20Sopenharmony_ci struct ath_beacon_config *conf) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF); 5478c2ecf20Sopenharmony_ci ath9k_beacon_init(sc, conf->nexttbtt, conf->intval); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void ath9k_beacon_config_sta(struct ath_hw *ah, 5518c2ecf20Sopenharmony_ci struct ath_beacon_config *conf) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct ath9k_beacon_state bs; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (ath9k_cmn_beacon_config_sta(ah, conf, &bs) == -EPERM) 5568c2ecf20Sopenharmony_ci return; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci ath9k_hw_disable_interrupts(ah); 5598c2ecf20Sopenharmony_ci ath9k_hw_set_sta_beacon_timers(ah, &bs); 5608c2ecf20Sopenharmony_ci ah->imask |= ATH9K_INT_BMISS; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 5638c2ecf20Sopenharmony_ci ath9k_hw_enable_interrupts(ah); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic void ath9k_beacon_config_adhoc(struct ath_softc *sc, 5678c2ecf20Sopenharmony_ci struct ath_beacon_config *conf) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 5708c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci ath9k_reset_beacon_status(sc); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci ath9k_cmn_beacon_config_adhoc(ah, conf); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci ath9k_beacon_init(sc, conf->nexttbtt, conf->intval); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* 5798c2ecf20Sopenharmony_ci * Set the global 'beacon has been configured' flag for the 5808c2ecf20Sopenharmony_ci * joiner case in IBSS mode. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci if (!conf->ibss_creator && conf->enable_beacon) 5838c2ecf20Sopenharmony_ci set_bit(ATH_OP_BEACONS, &common->op_flags); 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic void ath9k_cache_beacon_config(struct ath_softc *sc, 5878c2ecf20Sopenharmony_ci struct ath_chanctx *ctx, 5888c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *bss_conf) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 5918c2ecf20Sopenharmony_ci struct ath_beacon_config *cur_conf = &ctx->beacon; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ath_dbg(common, BEACON, 5948c2ecf20Sopenharmony_ci "Caching beacon data for BSS: %pM\n", bss_conf->bssid); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci cur_conf->beacon_interval = bss_conf->beacon_int; 5978c2ecf20Sopenharmony_ci cur_conf->dtim_period = bss_conf->dtim_period; 5988c2ecf20Sopenharmony_ci cur_conf->dtim_count = 1; 5998c2ecf20Sopenharmony_ci cur_conf->ibss_creator = bss_conf->ibss_creator; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* 6028c2ecf20Sopenharmony_ci * It looks like mac80211 may end up using beacon interval of zero in 6038c2ecf20Sopenharmony_ci * some cases (at least for mesh point). Avoid getting into an 6048c2ecf20Sopenharmony_ci * infinite loop by using a bit safer value instead. To be safe, 6058c2ecf20Sopenharmony_ci * do sanity check on beacon interval for all operating modes. 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_ci if (cur_conf->beacon_interval == 0) 6088c2ecf20Sopenharmony_ci cur_conf->beacon_interval = 100; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci cur_conf->bmiss_timeout = 6118c2ecf20Sopenharmony_ci ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* 6148c2ecf20Sopenharmony_ci * We don't parse dtim period from mac80211 during the driver 6158c2ecf20Sopenharmony_ci * initialization as it breaks association with hidden-ssid 6168c2ecf20Sopenharmony_ci * AP and it causes latency in roaming 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci if (cur_conf->dtim_period == 0) 6198c2ecf20Sopenharmony_ci cur_conf->dtim_period = 1; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci ath9k_set_tsfadjust(sc, cur_conf); 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_civoid ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif, 6258c2ecf20Sopenharmony_ci bool beacons) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 6288c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 6298c2ecf20Sopenharmony_ci struct ath_vif *avp; 6308c2ecf20Sopenharmony_ci struct ath_chanctx *ctx; 6318c2ecf20Sopenharmony_ci struct ath_beacon_config *cur_conf; 6328c2ecf20Sopenharmony_ci unsigned long flags; 6338c2ecf20Sopenharmony_ci bool enabled; 6348c2ecf20Sopenharmony_ci bool skip_beacon = false; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (!beacons) { 6378c2ecf20Sopenharmony_ci clear_bit(ATH_OP_BEACONS, &common->op_flags); 6388c2ecf20Sopenharmony_ci ath9k_beacon_stop(sc); 6398c2ecf20Sopenharmony_ci return; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (WARN_ON(!main_vif)) 6438c2ecf20Sopenharmony_ci return; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci avp = (void *)main_vif->drv_priv; 6468c2ecf20Sopenharmony_ci ctx = avp->chanctx; 6478c2ecf20Sopenharmony_ci cur_conf = &ctx->beacon; 6488c2ecf20Sopenharmony_ci enabled = cur_conf->enable_beacon; 6498c2ecf20Sopenharmony_ci cur_conf->enable_beacon = beacons; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { 6528c2ecf20Sopenharmony_ci ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ath9k_set_beacon(sc); 6558c2ecf20Sopenharmony_ci set_bit(ATH_OP_BEACONS, &common->op_flags); 6568c2ecf20Sopenharmony_ci return; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* Update the beacon configuration. */ 6608c2ecf20Sopenharmony_ci ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* 6638c2ecf20Sopenharmony_ci * Configure the HW beacon registers only when we have a valid 6648c2ecf20Sopenharmony_ci * beacon interval. 6658c2ecf20Sopenharmony_ci */ 6668c2ecf20Sopenharmony_ci if (cur_conf->beacon_interval) { 6678c2ecf20Sopenharmony_ci /* Special case to sync the TSF when joining an existing IBSS. 6688c2ecf20Sopenharmony_ci * This is only done if no AP interface is active. 6698c2ecf20Sopenharmony_ci * Note that mac80211 always resets the TSF when creating a new 6708c2ecf20Sopenharmony_ci * IBSS interface. 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC && 6738c2ecf20Sopenharmony_ci !enabled && beacons && !main_vif->bss_conf.ibss_creator) { 6748c2ecf20Sopenharmony_ci spin_lock_irqsave(&sc->sc_pm_lock, flags); 6758c2ecf20Sopenharmony_ci sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; 6768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sc->sc_pm_lock, flags); 6778c2ecf20Sopenharmony_ci skip_beacon = true; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* 6818c2ecf20Sopenharmony_ci * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode 6828c2ecf20Sopenharmony_ci * here, it is done in ath9k_beacon_config_adhoc(). 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_ci if (beacons && !skip_beacon) { 6858c2ecf20Sopenharmony_ci set_bit(ATH_OP_BEACONS, &common->op_flags); 6868c2ecf20Sopenharmony_ci ath9k_set_beacon(sc); 6878c2ecf20Sopenharmony_ci } else { 6888c2ecf20Sopenharmony_ci clear_bit(ATH_OP_BEACONS, &common->op_flags); 6898c2ecf20Sopenharmony_ci ath9k_beacon_stop(sc); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci } else { 6928c2ecf20Sopenharmony_ci clear_bit(ATH_OP_BEACONS, &common->op_flags); 6938c2ecf20Sopenharmony_ci ath9k_beacon_stop(sc); 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_civoid ath9k_set_beacon(struct ath_softc *sc) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 7008c2ecf20Sopenharmony_ci struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci switch (sc->sc_ah->opmode) { 7038c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 7048c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 7058c2ecf20Sopenharmony_ci ath9k_beacon_config_ap(sc, cur_conf); 7068c2ecf20Sopenharmony_ci break; 7078c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 7088c2ecf20Sopenharmony_ci ath9k_beacon_config_adhoc(sc, cur_conf); 7098c2ecf20Sopenharmony_ci break; 7108c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 7118c2ecf20Sopenharmony_ci ath9k_beacon_config_sta(sc->sc_ah, cur_conf); 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci default: 7148c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "Unsupported beaconing mode\n"); 7158c2ecf20Sopenharmony_ci return; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci} 718