diff options
Diffstat (limited to 'package/ath9k/src/drivers/net/wireless/ath9k/beacon.c')
-rw-r--r-- | package/ath9k/src/drivers/net/wireless/ath9k/beacon.c | 976 |
1 files changed, 0 insertions, 976 deletions
diff --git a/package/ath9k/src/drivers/net/wireless/ath9k/beacon.c b/package/ath9k/src/drivers/net/wireless/ath9k/beacon.c deleted file mode 100644 index ff803279ff..0000000000 --- a/package/ath9k/src/drivers/net/wireless/ath9k/beacon.c +++ /dev/null @@ -1,976 +0,0 @@ -/* - * Copyright (c) 2008 Atheros Communications Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - - /* Implementation of beacon processing. */ - -#include "core.h" - -/* - * Configure parameters for the beacon queue - * - * This function will modify certain transmit queue properties depending on - * the operating mode of the station (AP or AdHoc). Parameters are AIFS - * settings and channel width min/max -*/ - -static int ath_beaconq_config(struct ath_softc *sc) -{ - struct ath_hal *ah = sc->sc_ah; - struct ath9k_txq_info qi; - - ath9k_hw_gettxqueueprops(ah, sc->sc_bhalq, &qi); - if (sc->sc_opmode == ATH9K_M_HOSTAP) { - /* Always burst out beacon and CAB traffic. */ - qi.tqi_aifs = 1; - qi.tqi_cwmin = 0; - qi.tqi_cwmax = 0; - } else { - /* Adhoc mode; important thing is to use 2x cwmin. */ - qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs; - qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin; - qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax; - } - - if (!ath9k_hw_settxqueueprops(ah, sc->sc_bhalq, &qi)) { - DPRINTF(sc, ATH_DBG_FATAL, - "%s: unable to update h/w beacon queue parameters\n", - __func__); - return 0; - } else { - ath9k_hw_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ - return 1; - } -} - -/* - * Setup the beacon frame for transmit. - * - * Associates the beacon frame buffer with a transmit descriptor. Will set - * up all required antenna switch parameters, rate codes, and channel flags. - * Beacons are always sent out at the lowest rate, and are not retried. -*/ - -static void ath_beacon_setup(struct ath_softc *sc, - struct ath_vap *avp, struct ath_buf *bf) -{ - struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; - struct ath_hal *ah = sc->sc_ah; - struct ath_desc *ds; - int flags, antenna; - const struct ath9k_rate_table *rt; - u8 rix, rate; - int ctsrate = 0; - int ctsduration = 0; - struct ath9k_11n_rate_series series[4]; - - DPRINTF(sc, ATH_DBG_BEACON, "%s: m %p len %u\n", - __func__, skb, skb->len); - - /* setup descriptors */ - ds = bf->bf_desc; - - flags = ATH9K_TXDESC_NOACK; - - if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) { - ds->ds_link = bf->bf_daddr; /* self-linked */ - flags |= ATH9K_TXDESC_VEOL; - /* Let hardware handle antenna switching. */ - antenna = 0; - } else { - ds->ds_link = 0; - /* - * Switch antenna every beacon. - * Should only switch every beacon period, not for every - * SWBA's - * XXX assumes two antenna - */ - antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1); - } - - ds->ds_data = bf->bf_buf_addr; - - /* - * Calculate rate code. - * XXX everything at min xmit rate - */ - rix = sc->sc_minrateix; - rt = sc->sc_currates; - rate = rt->info[rix].rateCode; - if (sc->sc_flags & ATH_PREAMBLE_SHORT) - rate |= rt->info[rix].shortPreamble; - - ath9k_hw_set11n_txdesc(ah, ds - , skb->len + FCS_LEN /* frame length */ - , ATH9K_PKT_TYPE_BEACON /* Atheros packet type */ - , avp->av_btxctl.txpower /* txpower XXX */ - , ATH9K_TXKEYIX_INVALID /* no encryption */ - , ATH9K_KEY_TYPE_CLEAR /* no encryption */ - , flags /* no ack, veol for beacons */ - ); - - /* NB: beacon's BufLen must be a multiple of 4 bytes */ - ath9k_hw_filltxdesc(ah, ds - , roundup(skb->len, 4) /* buffer length */ - , true /* first segment */ - , true /* last segment */ - , ds /* first descriptor */ - ); - - memzero(series, sizeof(struct ath9k_11n_rate_series) * 4); - series[0].Tries = 1; - series[0].Rate = rate; - series[0].ChSel = sc->sc_tx_chainmask; - series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; - ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, - ctsrate, ctsduration, series, 4, 0); -} - -/* Move everything from the vap's mcast queue to the hardware cab queue. - * Caller must hold mcasq lock and cabq lock - * XXX MORE_DATA bit? - */ -static void empty_mcastq_into_cabq(struct ath_hal *ah, - struct ath_txq *mcastq, struct ath_txq *cabq) -{ - struct ath_buf *bfmcast; - - BUG_ON(list_empty(&mcastq->axq_q)); - - bfmcast = list_first_entry(&mcastq->axq_q, struct ath_buf, list); - - /* link the descriptors */ - if (!cabq->axq_link) - ath9k_hw_puttxbuf(ah, cabq->axq_qnum, bfmcast->bf_daddr); - else - *cabq->axq_link = bfmcast->bf_daddr; - - /* append the private vap mcast list to the cabq */ - - cabq->axq_depth += mcastq->axq_depth; - cabq->axq_totalqueued += mcastq->axq_totalqueued; - cabq->axq_linkbuf = mcastq->axq_linkbuf; - cabq->axq_link = mcastq->axq_link; - list_splice_tail_init(&mcastq->axq_q, &cabq->axq_q); - mcastq->axq_depth = 0; - mcastq->axq_totalqueued = 0; - mcastq->axq_linkbuf = NULL; - mcastq->axq_link = NULL; -} - -/* This is only run at DTIM. We move everything from the vap's mcast queue - * to the hardware cab queue. Caller must hold the mcastq lock. */ -static void trigger_mcastq(struct ath_hal *ah, - struct ath_txq *mcastq, struct ath_txq *cabq) -{ - spin_lock_bh(&cabq->axq_lock); - - if (!list_empty(&mcastq->axq_q)) - empty_mcastq_into_cabq(ah, mcastq, cabq); - - /* cabq is gated by beacon so it is safe to start here */ - if (!list_empty(&cabq->axq_q)) - ath9k_hw_txstart(ah, cabq->axq_qnum); - - spin_unlock_bh(&cabq->axq_lock); -} - -/* - * Generate beacon frame and queue cab data for a vap. - * - * Updates the contents of the beacon frame. It is assumed that the buffer for - * the beacon frame has been allocated in the ATH object, and simply needs to - * be filled for this cycle. Also, any CAB (crap after beacon?) traffic will - * be added to the beacon frame at this point. -*/ -static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) -{ - struct ath_hal *ah = sc->sc_ah; - struct ath_buf *bf; - struct ath_vap *avp; - struct sk_buff *skb; - int cabq_depth; - int mcastq_depth; - int is_beacon_dtim = 0; - unsigned int curlen; - struct ath_txq *cabq; - struct ath_txq *mcastq; - avp = sc->sc_vaps[if_id]; - - mcastq = &avp->av_mcastq; - cabq = sc->sc_cabq; - - ASSERT(avp); - - if (avp->av_bcbuf == NULL) { - DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n", - __func__, avp, avp->av_bcbuf); - return NULL; - } - bf = avp->av_bcbuf; - skb = (struct sk_buff *) bf->bf_mpdu; - - /* - * Update dynamic beacon contents. If this returns - * non-zero then we need to remap the memory because - * the beacon frame changed size (probably because - * of the TIM bitmap). - */ - curlen = skb->len; - - /* XXX: spin_lock_bh should not be used here, but sparse bitches - * otherwise. We should fix sparse :) */ - spin_lock_bh(&mcastq->axq_lock); - mcastq_depth = avp->av_mcastq.axq_depth; - - if (ath_update_beacon(sc, if_id, &avp->av_boff, skb, mcastq_depth) == - 1) { - ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, - get_dma_mem_context(bf, bf_dmacontext)); - bf->bf_buf_addr = ath_skb_map_single(sc, skb, PCI_DMA_TODEVICE, - get_dma_mem_context(bf, bf_dmacontext)); - } else { - pci_dma_sync_single_for_cpu(sc->pdev, - bf->bf_buf_addr, - skb_tailroom(skb), - PCI_DMA_TODEVICE); - } - - /* - * if the CABQ traffic from previous DTIM is pending and the current - * beacon is also a DTIM. - * 1) if there is only one vap let the cab traffic continue. - * 2) if there are more than one vap and we are using staggered - * beacons, then drain the cabq by dropping all the frames in - * the cabq so that the current vaps cab traffic can be scheduled. - */ - spin_lock_bh(&cabq->axq_lock); - cabq_depth = cabq->axq_depth; - spin_unlock_bh(&cabq->axq_lock); - - is_beacon_dtim = avp->av_boff.bo_tim[4] & 1; - - if (mcastq_depth && is_beacon_dtim && cabq_depth) { - /* - * Unlock the cabq lock as ath_tx_draintxq acquires - * the lock again which is a common function and that - * acquires txq lock inside. - */ - if (sc->sc_nvaps > 1) { - ath_tx_draintxq(sc, cabq, false); - DPRINTF(sc, ATH_DBG_BEACON, - "%s: flush previous cabq traffic\n", __func__); - } - } - - /* Construct tx descriptor. */ - ath_beacon_setup(sc, avp, bf); - - /* - * Enable the CAB queue before the beacon queue to - * insure cab frames are triggered by this beacon. - */ - if (is_beacon_dtim) - trigger_mcastq(ah, mcastq, cabq); - - spin_unlock_bh(&mcastq->axq_lock); - return bf; -} - -/* - * Startup beacon transmission for adhoc mode when they are sent entirely - * by the hardware using the self-linked descriptor + veol trick. -*/ - -static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id) -{ - struct ath_hal *ah = sc->sc_ah; - struct ath_buf *bf; - struct ath_vap *avp; - struct sk_buff *skb; - - avp = sc->sc_vaps[if_id]; - ASSERT(avp); - - if (avp->av_bcbuf == NULL) { - DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n", - __func__, avp, avp != NULL ? avp->av_bcbuf : NULL); - return; - } - bf = avp->av_bcbuf; - skb = (struct sk_buff *) bf->bf_mpdu; - - /* Construct tx descriptor. */ - ath_beacon_setup(sc, avp, bf); - - /* NB: caller is known to have already stopped tx dma */ - ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); - ath9k_hw_txstart(ah, sc->sc_bhalq); - DPRINTF(sc, ATH_DBG_BEACON, "%s: TXDP%u = %llx (%p)\n", __func__, - sc->sc_bhalq, ito64(bf->bf_daddr), bf->bf_desc); -} - -/* - * Setup a h/w transmit queue for beacons. - * - * This function allocates an information structure (struct ath9k_txq_info) - * on the stack, sets some specific parameters (zero out channel width - * min/max, and enable aifs). The info structure does not need to be - * persistant. -*/ - -int ath_beaconq_setup(struct ath_hal *ah) -{ - struct ath9k_txq_info qi; - - memzero(&qi, sizeof(qi)); - qi.tqi_aifs = 1; - qi.tqi_cwmin = 0; - qi.tqi_cwmax = 0; - /* NB: don't enable any interrupts */ - return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); -} - - -/* - * Allocate and setup an initial beacon frame. - * - * Allocate a beacon state variable for a specific VAP instance created on - * the ATH interface. This routine also calculates the beacon "slot" for - * staggared beacons in the mBSSID case. -*/ - -int ath_beacon_alloc(struct ath_softc *sc, int if_id) -{ - struct ath_vap *avp; - struct ieee80211_hdr *wh; - struct ath_buf *bf; - struct sk_buff *skb; - - avp = sc->sc_vaps[if_id]; - ASSERT(avp); - - /* Allocate a beacon descriptor if we haven't done so. */ - if (!avp->av_bcbuf) { - /* - * Allocate beacon state for hostap/ibss. We know - * a buffer is available. - */ - - avp->av_bcbuf = list_first_entry(&sc->sc_bbuf, - struct ath_buf, list); - list_del(&avp->av_bcbuf->list); - - if (sc->sc_opmode == ATH9K_M_HOSTAP || - !sc->sc_ah->ah_caps.halVEOLSupport) { - int slot; - /* - * Assign the vap to a beacon xmit slot. As - * above, this cannot fail to find one. - */ - avp->av_bslot = 0; - for (slot = 0; slot < ATH_BCBUF; slot++) - if (sc->sc_bslot[slot] == ATH_IF_ID_ANY) { - /* - * XXX hack, space out slots to better - * deal with misses - */ - if (slot+1 < ATH_BCBUF && - sc->sc_bslot[slot+1] == - ATH_IF_ID_ANY) { - avp->av_bslot = slot+1; - break; - } - avp->av_bslot = slot; - /* NB: keep looking for a double slot */ - } - BUG_ON(sc->sc_bslot[avp->av_bslot] != ATH_IF_ID_ANY); - sc->sc_bslot[avp->av_bslot] = if_id; - sc->sc_nbcnvaps++; - } - } - - /* release the previous beacon frame , if it already exists. */ - bf = avp->av_bcbuf; - if (bf->bf_mpdu != NULL) { - skb = (struct sk_buff *)bf->bf_mpdu; - ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, - get_dma_mem_context(bf, bf_dmacontext)); - dev_kfree_skb_any(skb); - bf->bf_mpdu = NULL; - } - - /* - * NB: the beacon data buffer must be 32-bit aligned; - * we assume the wbuf routines will return us something - * with this alignment (perhaps should assert). - * FIXME: Fill avp->av_boff.bo_tim,avp->av_btxctl.txpower and - * avp->av_btxctl.shortPreamble - */ - skb = ieee80211_beacon_get(sc->hw, avp->av_if_data); - if (skb == NULL) { - DPRINTF(sc, ATH_DBG_BEACON, "%s: cannot get skb\n", - __func__); - return -ENOMEM; - } - - /* - * Calculate a TSF adjustment factor required for - * staggered beacons. Note that we assume the format - * of the beacon frame leaves the tstamp field immediately - * following the header. - */ - if (avp->av_bslot > 0) { - u64 tsfadjust; - __le64 val; - int intval; - - /* FIXME: Use default value for now: Sujith */ - - intval = ATH_DEFAULT_BINTVAL; - - /* - * The beacon interval is in TU's; the TSF in usecs. - * We figure out how many TU's to add to align the - * timestamp then convert to TSF units and handle - * byte swapping before writing it in the frame. - * The hardware will then add this each time a beacon - * frame is sent. Note that we align vap's 1..N - * and leave vap 0 untouched. This means vap 0 - * has a timestamp in one beacon interval while the - * others get a timestamp aligned to the next interval. - */ - tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF; - val = cpu_to_le64(tsfadjust << 10); /* TU->TSF */ - - DPRINTF(sc, ATH_DBG_BEACON, - "%s: %s beacons, bslot %d intval %u tsfadjust %llu\n", - __func__, "stagger", - avp->av_bslot, intval, (unsigned long long)tsfadjust); - - wh = (struct ieee80211_hdr *)skb->data; - memcpy(&wh[1], &val, sizeof(val)); - } - - bf->bf_buf_addr = ath_skb_map_single(sc, skb, PCI_DMA_TODEVICE, - get_dma_mem_context(bf, bf_dmacontext)); - bf->bf_mpdu = skb; - - return 0; -} - -/* - * Reclaim beacon resources and return buffer to the pool. - * - * Checks the VAP to put the beacon frame buffer back to the ATH object - * queue, and de-allocates any wbuf frames that were sent as CAB traffic. -*/ - -void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp) -{ - if (avp->av_bcbuf != NULL) { - struct ath_buf *bf; - - if (avp->av_bslot != -1) { - sc->sc_bslot[avp->av_bslot] = ATH_IF_ID_ANY; - sc->sc_nbcnvaps--; - } - - bf = avp->av_bcbuf; - if (bf->bf_mpdu != NULL) { - struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; - ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, - get_dma_mem_context(bf, bf_dmacontext)); - dev_kfree_skb_any(skb); - bf->bf_mpdu = NULL; - } - list_add_tail(&bf->list, &sc->sc_bbuf); - - avp->av_bcbuf = NULL; - } -} - -/* - * Reclaim beacon resources and return buffer to the pool. - * - * This function will free any wbuf frames that are still attached to the - * beacon buffers in the ATH object. Note that this does not de-allocate - * any wbuf objects that are in the transmit queue and have not yet returned - * to the ATH object. -*/ - -void ath_beacon_free(struct ath_softc *sc) -{ - struct ath_buf *bf; - - list_for_each_entry(bf, &sc->sc_bbuf, list) { - if (bf->bf_mpdu != NULL) { - struct sk_buff *skb = (struct sk_buff *) bf->bf_mpdu; - ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, - get_dma_mem_context(bf, bf_dmacontext)); - dev_kfree_skb_any(skb); - bf->bf_mpdu = NULL; - } - } -} - -/* - * Tasklet for Sending Beacons - * - * Transmit one or more beacon frames at SWBA. Dynamic updates to the frame - * contents are done as needed and the slot time is also adjusted based on - * current state. - * - * This tasklet is not scheduled, it's called in ISR context. -*/ - -void ath9k_beacon_tasklet(unsigned long data) -{ -#define TSF_TO_TU(_h,_l) \ - ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) - - struct ath_softc *sc = (struct ath_softc *)data; - struct ath_hal *ah = sc->sc_ah; - struct ath_buf *bf = NULL; - int slot, if_id; - u32 bfaddr; - u32 rx_clear = 0, rx_frame = 0, tx_frame = 0; - u32 show_cycles = 0; - u32 bc = 0; /* beacon count */ - u64 tsf; - u32 tsftu; - u16 intval; - - if (sc->sc_noreset) { - show_cycles = ath9k_hw_GetMibCycleCountsPct(ah, - &rx_clear, - &rx_frame, - &tx_frame); - } - - /* - * Check if the previous beacon has gone out. If - * not don't try to post another, skip this period - * and wait for the next. Missed beacons indicate - * a problem and should not occur. If we miss too - * many consecutive beacons reset the device. - */ - if (ath9k_hw_numtxpending(ah, sc->sc_bhalq) != 0) { - sc->sc_bmisscount++; - /* XXX: doth needs the chanchange IE countdown decremented. - * We should consider adding a mac80211 call to indicate - * a beacon miss so appropriate action could be taken - * (in that layer). - */ - if (sc->sc_bmisscount < BSTUCK_THRESH) { - if (sc->sc_noreset) { - DPRINTF(sc, ATH_DBG_BEACON, - "%s: missed %u consecutive beacons\n", - __func__, sc->sc_bmisscount); - if (show_cycles) { - /* - * Display cycle counter stats - * from HW to aide in debug of - * stickiness. - */ - DPRINTF(sc, - ATH_DBG_BEACON, - "%s: busy times: rx_clear=%d, " - "rx_frame=%d, tx_frame=%d\n", - __func__, rx_clear, rx_frame, - tx_frame); - } else { - DPRINTF(sc, - ATH_DBG_BEACON, - "%s: unable to obtain " - "busy times\n", __func__); - } - } else { - DPRINTF(sc, ATH_DBG_BEACON, - "%s: missed %u consecutive beacons\n", - __func__, sc->sc_bmisscount); - } - } else if (sc->sc_bmisscount >= BSTUCK_THRESH) { - if (sc->sc_noreset) { - if (sc->sc_bmisscount == BSTUCK_THRESH) { - DPRINTF(sc, - ATH_DBG_BEACON, - "%s: beacon is officially " - "stuck\n", __func__); - ath9k_hw_dmaRegDump(ah); - } - } else { - DPRINTF(sc, ATH_DBG_BEACON, - "%s: beacon is officially stuck\n", - __func__); - ath_bstuck_process(sc); - } - } - - return; - } - if (sc->sc_bmisscount != 0) { - if (sc->sc_noreset) { - DPRINTF(sc, - ATH_DBG_BEACON, - "%s: resume beacon xmit after %u misses\n", - __func__, sc->sc_bmisscount); - } else { - DPRINTF(sc, ATH_DBG_BEACON, - "%s: resume beacon xmit after %u misses\n", - __func__, sc->sc_bmisscount); - } - sc->sc_bmisscount = 0; - } - - /* - * Generate beacon frames. we are sending frames - * staggered so calculate the slot for this frame based - * on the tsf to safeguard against missing an swba. - */ - - /* FIXME: Use default value for now - Sujith */ - intval = ATH_DEFAULT_BINTVAL; - - tsf = ath9k_hw_gettsf64(ah); - tsftu = TSF_TO_TU(tsf>>32, tsf); - slot = ((tsftu % intval) * ATH_BCBUF) / intval; - if_id = sc->sc_bslot[(slot + 1) % ATH_BCBUF]; - DPRINTF(sc, ATH_DBG_BEACON, - "%s: slot %d [tsf %llu tsftu %u intval %u] if_id %d\n", - __func__, slot, (unsigned long long) tsf, tsftu, - intval, if_id); - bfaddr = 0; - if (if_id != ATH_IF_ID_ANY) { - bf = ath_beacon_generate(sc, if_id); - if (bf != NULL) { - bfaddr = bf->bf_daddr; - bc = 1; - } - } - /* - * Handle slot time change when a non-ERP station joins/leaves - * an 11g network. The 802.11 layer notifies us via callback, - * we mark updateslot, then wait one beacon before effecting - * the change. This gives associated stations at least one - * beacon interval to note the state change. - * - * NB: The slot time change state machine is clocked according - * to whether we are bursting or staggering beacons. We - * recognize the request to update and record the current - * slot then don't transition until that slot is reached - * again. If we miss a beacon for that slot then we'll be - * slow to transition but we'll be sure at least one beacon - * interval has passed. When bursting slot is always left - * set to ATH_BCBUF so this check is a noop. - */ - /* XXX locking */ - if (sc->sc_updateslot == UPDATE) { - sc->sc_updateslot = COMMIT; /* commit next beacon */ - sc->sc_slotupdate = slot; - } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) - ath_setslottime(sc); /* commit change to hardware */ - - if (bfaddr != 0) { - /* - * Stop any current dma and put the new frame(s) on the queue. - * This should never fail since we check above that no frames - * are still pending on the queue. - */ - if (!ath9k_hw_stoptxdma(ah, sc->sc_bhalq)) { - DPRINTF(sc, ATH_DBG_FATAL, - "%s: beacon queue %u did not stop?\n", - __func__, sc->sc_bhalq); - /* NB: the HAL still stops DMA, so proceed */ - } - - /* NB: cabq traffic should already be queued and primed */ - ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bfaddr); - ath9k_hw_txstart(ah, sc->sc_bhalq); - - sc->ast_be_xmit += bc; /* XXX per-vap? */ - } -#undef TSF_TO_TU -} - -/* - * Tasklet for Beacon Stuck processing - * - * Processing for Beacon Stuck. - * Basically calls the ath_internal_reset function to reset the chip. -*/ - -void ath_bstuck_process(struct ath_softc *sc) -{ - DPRINTF(sc, ATH_DBG_BEACON, - "%s: stuck beacon; resetting (bmiss count %u)\n", - __func__, sc->sc_bmisscount); - ath_internal_reset(sc); -} - -/* - * Configure the beacon and sleep timers. - * - * When operating as an AP this resets the TSF and sets - * up the hardware to notify us when we need to issue beacons. - * - * When operating in station mode this sets up the beacon - * timers according to the timestamp of the last received - * beacon and the current TSF, configures PCF and DTIM - * handling, programs the sleep registers so the hardware - * will wakeup in time to receive beacons, and configures - * the beacon miss handling so we'll receive a BMISS - * interrupt when we stop seeing beacons from the AP - * we've associated with. - */ - -void ath_beacon_config(struct ath_softc *sc, int if_id) -{ -#define TSF_TO_TU(_h,_l) \ - ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) - struct ath_hal *ah = sc->sc_ah; - u32 nexttbtt, intval; - struct ath_beacon_config conf; - enum ath9k_opmode av_opmode; - - if (if_id != ATH_IF_ID_ANY) - av_opmode = sc->sc_vaps[if_id]->av_opmode; - else - av_opmode = sc->sc_opmode; - - memzero(&conf, sizeof(struct ath_beacon_config)); - - /* FIXME: Use default values for now - Sujith */ - /* Query beacon configuration first */ - /* - * Protocol stack doesn't support dynamic beacon configuration, - * use default configurations. - */ - conf.beacon_interval = ATH_DEFAULT_BINTVAL; - conf.listen_interval = 1; - conf.dtim_period = conf.beacon_interval; - conf.dtim_count = 1; - conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval; - - /* extract tstamp from last beacon and convert to TU */ - nexttbtt = TSF_TO_TU(LE_READ_4(conf.u.last_tstamp + 4), - LE_READ_4(conf.u.last_tstamp)); - /* XXX conditionalize multi-bss support? */ - if (sc->sc_opmode == ATH9K_M_HOSTAP) { - /* - * For multi-bss ap support beacons are either staggered - * evenly over N slots or burst together. For the former - * arrange for the SWBA to be delivered for each slot. - * Slots that are not occupied will generate nothing. - */ - /* NB: the beacon interval is kept internally in TU's */ - intval = conf.beacon_interval & ATH9K_BEACON_PERIOD; - intval /= ATH_BCBUF; /* for staggered beacons */ - } else { - intval = conf.beacon_interval & ATH9K_BEACON_PERIOD; - } - - if (nexttbtt == 0) /* e.g. for ap mode */ - nexttbtt = intval; - else if (intval) /* NB: can be 0 for monitor mode */ - nexttbtt = roundup(nexttbtt, intval); - DPRINTF(sc, ATH_DBG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", - __func__, nexttbtt, intval, conf.beacon_interval); - /* Check for ATH9K_M_HOSTAP and sc_nostabeacons for WDS client */ - if (sc->sc_opmode == ATH9K_M_STA) { - struct ath9k_beacon_state bs; - u64 tsf; - u32 tsftu; - int dtimperiod, dtimcount, sleepduration; - int cfpperiod, cfpcount; - - /* - * Setup dtim and cfp parameters according to - * last beacon we received (which may be none). - */ - dtimperiod = conf.dtim_period; - if (dtimperiod <= 0) /* NB: 0 if not known */ - dtimperiod = 1; - dtimcount = conf.dtim_count; - if (dtimcount >= dtimperiod) /* NB: sanity check */ - dtimcount = 0; /* XXX? */ - cfpperiod = 1; /* NB: no PCF support yet */ - cfpcount = 0; - - sleepduration = conf.listen_interval * intval; - if (sleepduration <= 0) - sleepduration = intval; - -#define FUDGE 2 - /* - * Pull nexttbtt forward to reflect the current - * TSF and calculate dtim+cfp state for the result. - */ - tsf = ath9k_hw_gettsf64(ah); - tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; - do { - nexttbtt += intval; - if (--dtimcount < 0) { - dtimcount = dtimperiod - 1; - if (--cfpcount < 0) - cfpcount = cfpperiod - 1; - } - } while (nexttbtt < tsftu); -#undef FUDGE - memzero(&bs, sizeof(bs)); - bs.bs_intval = intval; - bs.bs_nexttbtt = nexttbtt; - bs.bs_dtimperiod = dtimperiod*intval; - bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; - bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; - bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; - bs.bs_cfpmaxduration = 0; - /* - * Calculate the number of consecutive beacons to miss - * before taking a BMISS interrupt. The configuration - * is specified in TU so we only need calculate based - * on the beacon interval. Note that we clamp the - * result to at most 15 beacons. - */ - if (sleepduration > intval) { - bs.bs_bmissthreshold = - conf.listen_interval * - ATH_DEFAULT_BMISS_LIMIT / 2; - } else { - bs.bs_bmissthreshold = - howmany(conf.bmiss_timeout, intval); - if (bs.bs_bmissthreshold > 15) - bs.bs_bmissthreshold = 15; - else if (bs.bs_bmissthreshold <= 0) - bs.bs_bmissthreshold = 1; - } - - /* - * Calculate sleep duration. The configuration is - * given in ms. We insure a multiple of the beacon - * period is used. Also, if the sleep duration is - * greater than the DTIM period then it makes senses - * to make it a multiple of that. - * - * XXX fixed at 100ms - */ - - bs.bs_sleepduration = - roundup(IEEE80211_MS_TO_TU(100), sleepduration); - if (bs.bs_sleepduration > bs.bs_dtimperiod) - bs.bs_sleepduration = bs.bs_dtimperiod; - - DPRINTF(sc, ATH_DBG_BEACON, - "%s: tsf %llu " - "tsf:tu %u " - "intval %u " - "nexttbtt %u " - "dtim %u " - "nextdtim %u " - "bmiss %u " - "sleep %u " - "cfp:period %u " - "maxdur %u " - "next %u " - "timoffset %u\n" - , __func__ - , (unsigned long long)tsf, tsftu - , bs.bs_intval - , bs.bs_nexttbtt - , bs.bs_dtimperiod - , bs.bs_nextdtim - , bs.bs_bmissthreshold - , bs.bs_sleepduration - , bs.bs_cfpperiod - , bs.bs_cfpmaxduration - , bs.bs_cfpnext - , bs.bs_timoffset - ); - - ath9k_hw_set_interrupts(ah, 0); - ath9k_hw_set_sta_beacon_timers(ah, &bs); - sc->sc_imask |= ATH9K_INT_BMISS; - ath9k_hw_set_interrupts(ah, sc->sc_imask); - } else { - u64 tsf; - u32 tsftu; - ath9k_hw_set_interrupts(ah, 0); - if (nexttbtt == intval) - intval |= ATH9K_BEACON_RESET_TSF; - if (sc->sc_opmode == ATH9K_M_IBSS) { - /* - * Pull nexttbtt forward to reflect the current - * TSF . - */ -#define FUDGE 2 - if (!(intval & ATH9K_BEACON_RESET_TSF)) { - tsf = ath9k_hw_gettsf64(ah); - tsftu = TSF_TO_TU((u32)(tsf>>32), - (u32)tsf) + FUDGE; - do { - nexttbtt += intval; - } while (nexttbtt < tsftu); - } -#undef FUDGE - DPRINTF(sc, ATH_DBG_BEACON, - "%s: IBSS nexttbtt %u intval %u (%u)\n", - __func__, nexttbtt, - intval & ~ATH9K_BEACON_RESET_TSF, - conf.beacon_interval); - - /* - * In IBSS mode enable the beacon timers but only - * enable SWBA interrupts if we need to manually - * prepare beacon frames. Otherwise we use a - * self-linked tx descriptor and let the hardware - * deal with things. - */ - intval |= ATH9K_BEACON_ENA; - if (!ah->ah_caps.halVEOLSupport) - sc->sc_imask |= ATH9K_INT_SWBA; - ath_beaconq_config(sc); - } else if (sc->sc_opmode == ATH9K_M_HOSTAP) { - /* - * In AP mode we enable the beacon timers and - * SWBA interrupts to prepare beacon frames. - */ - intval |= ATH9K_BEACON_ENA; - sc->sc_imask |= ATH9K_INT_SWBA; /* beacon prepare */ - ath_beaconq_config(sc); - } - ath9k_hw_beaconinit(ah, nexttbtt, intval); - sc->sc_bmisscount = 0; - ath9k_hw_set_interrupts(ah, sc->sc_imask); - /* - * When using a self-linked beacon descriptor in - * ibss mode load it once here. - */ - if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) - ath_beacon_start_adhoc(sc, 0); - } -#undef TSF_TO_TU -} - -/* Function to collect beacon rssi data and resync beacon if necessary */ - -void ath_beacon_sync(struct ath_softc *sc, int if_id) -{ - /* - * Resync beacon timers using the tsf of the - * beacon frame we just received. - */ - ath_beacon_config(sc, if_id); - sc->sc_beacons = 1; -} |