diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-03-30 07:04:53 +0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-03-30 07:04:53 +0400 |
commit | 9ff9a26b786c35ee8d2a66222924a807ec851a9f (patch) | |
tree | db432a17bccca1ca2c16907f0ee83ac449ed4012 /drivers/net/wireless/ath9k/beacon.c | |
parent | 0a3108beea9143225119d5e7c72a8e2c64f3eb7d (diff) | |
parent | 0d34fb8e93ceba7b6dad0062dbb4a0813bacd75b (diff) | |
download | linux-9ff9a26b786c35ee8d2a66222924a807ec851a9f.tar.xz |
Merge commit 'origin/master' into next
Manual merge of:
arch/powerpc/include/asm/elf.h
drivers/i2c/busses/i2c-mpc.c
Diffstat (limited to 'drivers/net/wireless/ath9k/beacon.c')
-rw-r--r-- | drivers/net/wireless/ath9k/beacon.c | 814 |
1 files changed, 353 insertions, 461 deletions
diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c index 3ab0b43aaf93..ec995730632d 100644 --- a/drivers/net/wireless/ath9k/beacon.c +++ b/drivers/net/wireless/ath9k/beacon.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2008-2009 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 @@ -14,7 +14,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "core.h" +#include "ath9k.h" + +#define FUDGE 2 /* * This function will modify certain transmit queue properties depending on @@ -23,11 +25,11 @@ */ static int ath_beaconq_config(struct ath_softc *sc) { - struct ath_hal *ah = sc->sc_ah; + struct ath_hw *ah = sc->sc_ah; struct ath9k_tx_queue_info qi; ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); - if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) { + if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { /* Always burst out beacon and CAB traffic. */ qi.tqi_aifs = 1; qi.tqi_cwmin = 0; @@ -44,46 +46,33 @@ static int ath_beaconq_config(struct ath_softc *sc) "unable to update h/w beacon queue parameters\n"); return 0; } else { - ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); /* push to h/w */ + ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); return 1; } } -static void ath_bstuck_process(struct ath_softc *sc) -{ - DPRINTF(sc, ATH_DBG_BEACON, - "stuck beacon; resetting (bmiss count %u)\n", - sc->beacon.bmisscnt); - ath_reset(sc, false); -} - /* * 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) +static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, + struct ath_buf *bf) { struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; - struct ath_hal *ah = sc->sc_ah; + struct ath_hw *ah = sc->sc_ah; struct ath_desc *ds; struct ath9k_11n_rate_series series[4]; struct ath_rate_table *rt; - int flags, antenna; - u8 rix, rate; - int ctsrate = 0; - int ctsduration = 0; - - DPRINTF(sc, ATH_DBG_BEACON, "m %p len %u\n", skb, skb->len); + int flags, antenna, ctsrate = 0, ctsduration = 0; + u8 rate; - /* setup descriptors */ ds = bf->bf_desc; - flags = ATH9K_TXDESC_NOACK; - if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC && - (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) { + if (((sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) && + (ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { ds->ds_link = bf->bf_daddr; /* self-linked */ flags |= ATH9K_TXDESC_VEOL; /* Let hardware handle antenna switching. */ @@ -92,65 +81,53 @@ static void ath_beacon_setup(struct ath_softc *sc, ds->ds_link = 0; /* * Switch antenna every beacon. - * Should only switch every beacon period, not for every - * SWBA's - * XXX assumes two antenna + * Should only switch every beacon period, not for every SWBA + * XXX assumes two antennae */ - antenna = ((sc->beacon.ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1); + antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1); } ds->ds_data = bf->bf_buf_addr; - /* - * Calculate rate code. - * XXX everything at min xmit rate - */ - rix = 0; rt = sc->cur_rate_table; - rate = rt->info[rix].ratecode; + rate = rt->info[0].ratecode; if (sc->sc_flags & SC_OP_PREAMBLE_SHORT) - rate |= rt->info[rix].short_preamble; - - ath9k_hw_set11n_txdesc(ah, ds, - skb->len + FCS_LEN, /* frame length */ - ATH9K_PKT_TYPE_BEACON, /* Atheros packet type */ - MAX_RATE_POWER, /* FIXME */ - ATH9K_TXKEYIX_INVALID, /* no encryption */ - ATH9K_KEY_TYPE_CLEAR, /* no encryption */ - flags /* no ack, - veol for beacons */ - ); + rate |= rt->info[0].short_preamble; + + ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN, + ATH9K_PKT_TYPE_BEACON, + MAX_RATE_POWER, + ATH9K_TXKEYIX_INVALID, + ATH9K_KEY_TYPE_CLEAR, + flags); /* 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 */ - ); + ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4), + true, true, ds); memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4); series[0].Tries = 1; series[0].Rate = rate; - series[0].ChSel = sc->sc_tx_chainmask; + series[0].ChSel = 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); + ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration, + series, 4, 0); } -/* Generate beacon frame and queue cab data for a vap */ -static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) +static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; struct ath_buf *bf; - struct ath_vap *avp; + struct ath_vif *avp; struct sk_buff *skb; struct ath_txq *cabq; - struct ieee80211_vif *vif; struct ieee80211_tx_info *info; int cabq_depth; - vif = sc->sc_vaps[if_id]; - ASSERT(vif); + if (aphy->state != ATH_WIPHY_ACTIVE) + return NULL; avp = (void *)vif->drv_priv; cabq = sc->beacon.cabq; @@ -161,19 +138,24 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) return NULL; } + /* Release the old beacon first */ + bf = avp->av_bcbuf; skb = (struct sk_buff *)bf->bf_mpdu; if (skb) { - pci_unmap_single(sc->pdev, bf->bf_dmacontext, - skb->len, - PCI_DMA_TODEVICE); + dma_unmap_single(sc->dev, bf->bf_dmacontext, + skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); } - skb = ieee80211_beacon_get(sc->hw, vif); + /* Get a new beacon from mac80211 */ + + skb = ieee80211_beacon_get(hw, vif); bf->bf_mpdu = skb; if (skb == NULL) return NULL; + ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = + avp->tsf_adjust; info = IEEE80211_SKB_CB(skb); if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { @@ -188,54 +170,42 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) } bf->bf_buf_addr = bf->bf_dmacontext = - pci_map_single(sc->pdev, skb->data, - skb->len, - PCI_DMA_TODEVICE); - if (unlikely(pci_dma_mapping_error(sc->pdev, bf->bf_buf_addr))) { + dma_map_single(sc->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; - DPRINTF(sc, ATH_DBG_CONFIG, - "pci_dma_mapping_error() on beaconing\n"); + DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error on beaconing\n"); return NULL; } - skb = ieee80211_get_buffered_bc(sc->hw, vif); + skb = ieee80211_get_buffered_bc(hw, vif); /* * 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 + * 1) if there is only one vif let the cab traffic continue. + * 2) if there are more than one vif 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. + * the cabq so that the current vifs cab traffic can be scheduled. */ spin_lock_bh(&cabq->axq_lock); cabq_depth = cabq->axq_depth; spin_unlock_bh(&cabq->axq_lock); if (skb && 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); + if (sc->nvifs > 1) { DPRINTF(sc, ATH_DBG_BEACON, - "flush previous cabq traffic\n"); + "Flushing previous cabq traffic\n"); + ath_draintxq(sc, cabq, false); } } - /* 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. - */ while (skb) { - ath_tx_cabq(sc, skb); - skb = ieee80211_get_buffered_bc(sc->hw, vif); + ath_tx_cabq(hw, skb); + skb = ieee80211_get_buffered_bc(hw, vif); } return bf; @@ -245,28 +215,22 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) * 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) +static void ath_beacon_start_adhoc(struct ath_softc *sc, + struct ieee80211_vif *vif) { - struct ieee80211_vif *vif; - struct ath_hal *ah = sc->sc_ah; + struct ath_hw *ah = sc->sc_ah; struct ath_buf *bf; - struct ath_vap *avp; + struct ath_vif *avp; struct sk_buff *skb; - vif = sc->sc_vaps[if_id]; - ASSERT(vif); - avp = (void *)vif->drv_priv; - if (avp->av_bcbuf == NULL) { - DPRINTF(sc, ATH_DBG_BEACON, "avp=%p av_bcbuf=%p\n", - avp, avp != NULL ? avp->av_bcbuf : NULL); + if (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 */ @@ -276,7 +240,7 @@ static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id) sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc); } -int ath_beaconq_setup(struct ath_hal *ah) +int ath_beaconq_setup(struct ath_hw *ah) { struct ath9k_tx_queue_info qi; @@ -288,18 +252,14 @@ int ath_beaconq_setup(struct ath_hal *ah) return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); } -int ath_beacon_alloc(struct ath_softc *sc, int if_id) +int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) { - struct ieee80211_vif *vif; - struct ath_vap *avp; - struct ieee80211_hdr *hdr; + struct ath_softc *sc = aphy->sc; + struct ath_vif *avp; struct ath_buf *bf; struct sk_buff *skb; __le64 tstamp; - vif = sc->sc_vaps[if_id]; - ASSERT(vif); - avp = (void *)vif->drv_priv; /* Allocate a beacon descriptor if we haven't done so. */ @@ -310,51 +270,46 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id) struct ath_buf, list); list_del(&avp->av_bcbuf->list); - if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP || - !(sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) { + if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || + !(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) { int slot; /* - * Assign the vap to a beacon xmit slot. As + * Assign the vif 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->beacon.bslot[slot] == ATH_IF_ID_ANY) { + if (sc->beacon.bslot[slot] == NULL) { /* * XXX hack, space out slots to better * deal with misses */ if (slot+1 < ATH_BCBUF && - sc->beacon.bslot[slot+1] == - ATH_IF_ID_ANY) { + sc->beacon.bslot[slot+1] == NULL) { avp->av_bslot = slot+1; break; } avp->av_bslot = slot; /* NB: keep looking for a double slot */ } - BUG_ON(sc->beacon.bslot[avp->av_bslot] != ATH_IF_ID_ANY); - sc->beacon.bslot[avp->av_bslot] = if_id; - sc->sc_nbcnvaps++; + BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL); + sc->beacon.bslot[avp->av_bslot] = vif; + sc->beacon.bslot_aphy[avp->av_bslot] = aphy; + sc->nbcnvifs++; } } - /* release the previous beacon frame , if it already exists. */ + /* 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; - pci_unmap_single(sc->pdev, bf->bf_dmacontext, - skb->len, - PCI_DMA_TODEVICE); + dma_unmap_single(sc->dev, bf->bf_dmacontext, + skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; } - /* - * NB: the beacon data buffer must be 32-bit aligned. - * FIXME: Fill avp->av_btxctl.txpower and - * avp->av_btxctl.shortPreamble - */ + /* NB: the beacon data buffer must be 32-bit aligned. */ skb = ieee80211_beacon_get(sc->hw, vif); if (skb == NULL) { DPRINTF(sc, ATH_DBG_BEACON, "cannot get skb\n"); @@ -363,75 +318,65 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id) tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; sc->beacon.bc_tstamp = le64_to_cpu(tstamp); - - /* - * 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. - */ + /* Calculate a TSF adjustment factor required for staggered beacons. */ if (avp->av_bslot > 0) { u64 tsfadjust; - __le64 val; int intval; intval = sc->hw->conf.beacon_int ? sc->hw->conf.beacon_int : 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. + * Calculate the TSF offset for this beacon slot, i.e., the + * number of usecs that need to be added to the timestamp field + * in Beacon and Probe Response frames. Beacon slot 0 is + * processed at the correct offset, so it does not require TSF + * adjustment. Other slots are adjusted to get the timestamp + * close to the TBTT for the BSS. */ - tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF; - val = cpu_to_le64(tsfadjust << 10); /* TU->TSF */ + tsfadjust = intval * avp->av_bslot / ATH_BCBUF; + avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); DPRINTF(sc, ATH_DBG_BEACON, "stagger beacons, bslot %d intval %u tsfadjust %llu\n", avp->av_bslot, intval, (unsigned long long)tsfadjust); - hdr = (struct ieee80211_hdr *)skb->data; - memcpy(&hdr[1], &val, sizeof(val)); - } + ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp = + avp->tsf_adjust; + } else + avp->tsf_adjust = cpu_to_le64(0); bf->bf_mpdu = skb; bf->bf_buf_addr = bf->bf_dmacontext = - pci_map_single(sc->pdev, skb->data, - skb->len, - PCI_DMA_TODEVICE); - if (unlikely(pci_dma_mapping_error(sc->pdev, bf->bf_buf_addr))) { + dma_map_single(sc->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) { dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; - DPRINTF(sc, ATH_DBG_CONFIG, - "pci_dma_mapping_error() on beacon alloc\n"); + DPRINTF(sc, ATH_DBG_FATAL, + "dma_mapping_error on beacon alloc\n"); return -ENOMEM; } return 0; } -void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp) +void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp) { if (avp->av_bcbuf != NULL) { struct ath_buf *bf; if (avp->av_bslot != -1) { - sc->beacon.bslot[avp->av_bslot] = ATH_IF_ID_ANY; - sc->sc_nbcnvaps--; + sc->beacon.bslot[avp->av_bslot] = NULL; + sc->beacon.bslot_aphy[avp->av_bslot] = NULL; + sc->nbcnvifs--; } bf = avp->av_bcbuf; if (bf->bf_mpdu != NULL) { struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; - pci_unmap_single(sc->pdev, bf->bf_dmacontext, - skb->len, - PCI_DMA_TODEVICE); + dma_unmap_single(sc->dev, bf->bf_dmacontext, + skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); bf->bf_mpdu = NULL; } @@ -441,92 +386,45 @@ void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp) } } -void ath9k_beacon_tasklet(unsigned long data) +void ath_beacon_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; - struct ath_hal *ah = sc->sc_ah; + struct ath_hw *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 */ + struct ieee80211_vif *vif; + struct ath_wiphy *aphy; + int slot; + u32 bfaddr, bc = 0, tsftu; u64 tsf; - u32 tsftu; u16 intval; - if (sc->sc_flags & SC_OP_NO_RESET) { - 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. - * - * FIXME: Clean up this mess !! */ if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { sc->beacon.bmisscnt++; - /* 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->beacon.bmisscnt < BSTUCK_THRESH) { - if (sc->sc_flags & SC_OP_NO_RESET) { - DPRINTF(sc, ATH_DBG_BEACON, - "missed %u consecutive beacons\n", - sc->beacon.bmisscnt); - if (show_cycles) { - /* - * Display cycle counter stats from HW - * to aide in debug of stickiness. - */ - DPRINTF(sc, ATH_DBG_BEACON, - "busy times: rx_clear=%d, " - "rx_frame=%d, tx_frame=%d\n", - rx_clear, rx_frame, - tx_frame); - } else { - DPRINTF(sc, ATH_DBG_BEACON, - "unable to obtain " - "busy times\n"); - } - } else { - DPRINTF(sc, ATH_DBG_BEACON, - "missed %u consecutive beacons\n", - sc->beacon.bmisscnt); - } + DPRINTF(sc, ATH_DBG_BEACON, + "missed %u consecutive beacons\n", + sc->beacon.bmisscnt); } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { - if (sc->sc_flags & SC_OP_NO_RESET) { - if (sc->beacon.bmisscnt == BSTUCK_THRESH) { - DPRINTF(sc, ATH_DBG_BEACON, - "beacon is officially " - "stuck\n"); - } - } else { - DPRINTF(sc, ATH_DBG_BEACON, - "beacon is officially stuck\n"); - ath_bstuck_process(sc); - } + DPRINTF(sc, ATH_DBG_BEACON, + "beacon is officially stuck\n"); + ath_reset(sc, false); } + return; } if (sc->beacon.bmisscnt != 0) { - if (sc->sc_flags & SC_OP_NO_RESET) { - DPRINTF(sc, ATH_DBG_BEACON, - "resume beacon xmit after %u misses\n", - sc->beacon.bmisscnt); - } else { - DPRINTF(sc, ATH_DBG_BEACON, - "resume beacon xmit after %u misses\n", - sc->beacon.bmisscnt); - } + DPRINTF(sc, ATH_DBG_BEACON, + "resume beacon xmit after %u misses\n", + sc->beacon.bmisscnt); sc->beacon.bmisscnt = 0; } @@ -542,21 +440,30 @@ void ath9k_beacon_tasklet(unsigned long data) tsf = ath9k_hw_gettsf64(ah); tsftu = TSF_TO_TU(tsf>>32, tsf); slot = ((tsftu % intval) * ATH_BCBUF) / intval; - if_id = sc->beacon.bslot[(slot + 1) % ATH_BCBUF]; + /* + * Reverse the slot order to get slot 0 on the TBTT offset that does + * not require TSF adjustment and other slots adding + * slot/ATH_BCBUF * beacon_int to timestamp. For example, with + * ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 .. + * and slot 0 is at correct offset to TBTT. + */ + slot = ATH_BCBUF - slot - 1; + vif = sc->beacon.bslot[slot]; + aphy = sc->beacon.bslot_aphy[slot]; DPRINTF(sc, ATH_DBG_BEACON, - "slot %d [tsf %llu tsftu %u intval %u] if_id %d\n", - slot, (unsigned long long)tsf, tsftu, - intval, if_id); + "slot %d [tsf %llu tsftu %u intval %u] vif %p\n", + slot, tsf, tsftu, intval, vif); bfaddr = 0; - if (if_id != ATH_IF_ID_ANY) { - bf = ath_beacon_generate(sc, if_id); + if (vif) { + bf = ath_beacon_generate(aphy->hw, vif); 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, @@ -573,7 +480,6 @@ void ath9k_beacon_tasklet(unsigned long data) * interval has passed. When bursting slot is always left * set to ATH_BCBUF so this check is a noop. */ - /* XXX locking */ if (sc->beacon.updateslot == UPDATE) { sc->beacon.updateslot = COMMIT; /* commit next beacon */ sc->beacon.slotupdate = slot; @@ -590,265 +496,251 @@ void ath9k_beacon_tasklet(unsigned long data) if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) { DPRINTF(sc, ATH_DBG_FATAL, "beacon queue %u did not stop?\n", sc->beacon.beaconq); - /* NB: the HAL still stops DMA, so proceed */ } /* NB: cabq traffic should already be queued and primed */ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr); ath9k_hw_txstart(ah, sc->beacon.beaconq); - sc->beacon.ast_be_xmit += bc; /* XXX per-vap? */ + sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */ } } /* - * 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. + * 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. */ -void ath_beacon_config(struct ath_softc *sc, int if_id) +static void ath_beacon_config_ap(struct ath_softc *sc, + struct ath_beacon_config *conf, + struct ath_vif *avp) { - struct ieee80211_vif *vif; - struct ath_hal *ah = sc->sc_ah; - struct ath_beacon_config conf; - struct ath_vap *avp; - enum nl80211_iftype opmode; u32 nexttbtt, intval; - if (if_id != ATH_IF_ID_ANY) { - vif = sc->sc_vaps[if_id]; - ASSERT(vif); - avp = (void *)vif->drv_priv; - opmode = avp->av_opmode; - } else { - opmode = sc->sc_ah->ah_opmode; - } + /* Configure the timers only when the TSF has to be reset */ - memset(&conf, 0, sizeof(struct ath_beacon_config)); + if (!(sc->sc_flags & SC_OP_TSF_RESET)) + return; - conf.beacon_interval = sc->hw->conf.beacon_int ? - sc->hw->conf.beacon_int : 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; + /* NB: the beacon interval is kept internally in TU's */ + intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; + intval /= ATH_BCBUF; /* for staggered beacons */ + nexttbtt = intval; + intval |= ATH9K_BEACON_RESET_TSF; - /* extract tstamp from last beacon and convert to TU */ - nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp); + /* + * In AP mode we enable the beacon timers and SWBA interrupts to + * prepare beacon frames. + */ + intval |= ATH9K_BEACON_ENA; + sc->imask |= ATH9K_INT_SWBA; + ath_beaconq_config(sc); - /* XXX conditionalize multi-bss support? */ - if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) { - /* - * 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; - } + /* Set the computed AP beacon timers */ - if (nexttbtt == 0) /* e.g. for ap mode */ - nexttbtt = intval; - else if (intval) /* NB: can be 0 for monitor mode */ - nexttbtt = roundup(nexttbtt, intval); + ath9k_hw_set_interrupts(sc->sc_ah, 0); + ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval); + sc->beacon.bmisscnt = 0; + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); - DPRINTF(sc, ATH_DBG_BEACON, "nexttbtt %u intval %u (%u)\n", - nexttbtt, intval, conf.beacon_interval); + /* Clear the reset TSF flag, so that subsequent beacon updation + will not reset the HW TSF. */ - /* Check for NL80211_IFTYPE_AP and sc_nostabeacons for WDS client */ - if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_STATION) { - struct ath9k_beacon_state bs; - u64 tsf; - u32 tsftu; - int dtimperiod, dtimcount, sleepduration; - int cfpperiod, cfpcount; + sc->sc_flags &= ~SC_OP_TSF_RESET; +} - /* - * 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; - cfpperiod = 1; /* NB: no PCF support yet */ - cfpcount = 0; - - sleepduration = conf.listen_interval * intval; - if (sleepduration <= 0) - sleepduration = intval; +/* + * 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. + */ +static void ath_beacon_config_sta(struct ath_softc *sc, + struct ath_beacon_config *conf, + struct ath_vif *avp) +{ + struct ath9k_beacon_state bs; + int dtimperiod, dtimcount, sleepduration; + int cfpperiod, cfpcount; + u32 nexttbtt = 0, intval, tsftu; + u64 tsf; -#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 - memset(&bs, 0, 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; + memset(&bs, 0, sizeof(bs)); + intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; - /* - * 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 = - DIV_ROUND_UP(conf.bmiss_timeout, intval); - if (bs.bs_bmissthreshold > 15) - bs.bs_bmissthreshold = 15; - else if (bs.bs_bmissthreshold <= 0) - bs.bs_bmissthreshold = 1; - } + /* + * 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; + cfpperiod = 1; /* NB: no PCF support yet */ + cfpcount = 0; + + sleepduration = conf->listen_interval * intval; + if (sleepduration <= 0) + sleepduration = intval; - /* - * 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 - */ + /* + * Pull nexttbtt forward to reflect the current + * TSF and calculate dtim+cfp state for the result. + */ + tsf = ath9k_hw_gettsf64(sc->sc_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); - bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), - sleepduration); - if (bs.bs_sleepduration > bs.bs_dtimperiod) - bs.bs_sleepduration = bs.bs_dtimperiod; + 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; - DPRINTF(sc, ATH_DBG_BEACON, - "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", - (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); + /* + * 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 { - u64 tsf; - u32 tsftu; - ath9k_hw_set_interrupts(ah, 0); - if (nexttbtt == intval) - intval |= ATH9K_BEACON_RESET_TSF; - if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC) { - /* - * 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, - "IBSS nexttbtt %u intval %u (%u)\n", - 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.hw_caps & ATH9K_HW_CAP_VEOL)) - sc->sc_imask |= ATH9K_INT_SWBA; - ath_beaconq_config(sc); - } else if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) { - /* - * 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->beacon.bmisscnt = 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_ah->ah_opmode == NL80211_IFTYPE_ADHOC && - (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) - ath_beacon_start_adhoc(sc, 0); + bs.bs_bmissthreshold = DIV_ROUND_UP(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 ensure 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; + + /* TSF out of range threshold fixed at 1 second */ + bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; + + DPRINTF(sc, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); + DPRINTF(sc, ATH_DBG_BEACON, + "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", + bs.bs_bmissthreshold, bs.bs_sleepduration, + bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); + + /* Set the computed STA beacon timers */ + + ath9k_hw_set_interrupts(sc->sc_ah, 0); + ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs); + sc->imask |= ATH9K_INT_BMISS; + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); } -void ath_beacon_sync(struct ath_softc *sc, int if_id) +static void ath_beacon_config_adhoc(struct ath_softc *sc, + struct ath_beacon_config *conf, + struct ath_vif *avp, + struct ieee80211_vif *vif) { + u64 tsf; + u32 tsftu, intval, nexttbtt; + + intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; + + /* Pull nexttbtt forward to reflect the current TSF */ + + nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp); + if (nexttbtt == 0) + nexttbtt = intval; + else if (intval) + nexttbtt = roundup(nexttbtt, intval); + + tsf = ath9k_hw_gettsf64(sc->sc_ah); + tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE; + do { + nexttbtt += intval; + } while (nexttbtt < tsftu); + + DPRINTF(sc, ATH_DBG_BEACON, + "IBSS nexttbtt %u intval %u (%u)\n", + nexttbtt, intval, conf->beacon_interval); + /* - * Resync beacon timers using the tsf of the - * beacon frame we just received. + * 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. */ - ath_beacon_config(sc, if_id); - sc->sc_flags |= SC_OP_BEACONS; + intval |= ATH9K_BEACON_ENA; + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) + sc->imask |= ATH9K_INT_SWBA; + + ath_beaconq_config(sc); + + /* Set the computed ADHOC beacon timers */ + + ath9k_hw_set_interrupts(sc->sc_ah, 0); + ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval); + sc->beacon.bmisscnt = 0; + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL) + ath_beacon_start_adhoc(sc, vif); +} + +void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) +{ + struct ath_beacon_config conf; + + /* Setup the beacon configuration parameters */ + + memset(&conf, 0, sizeof(struct ath_beacon_config)); + conf.beacon_interval = sc->hw->conf.beacon_int ? + sc->hw->conf.beacon_int : 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; + + if (vif) { + struct ath_vif *avp = (struct ath_vif *)vif->drv_priv; + + switch(avp->av_opmode) { + case NL80211_IFTYPE_AP: + ath_beacon_config_ap(sc, &conf, avp); + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + ath_beacon_config_adhoc(sc, &conf, avp, vif); + break; + case NL80211_IFTYPE_STATION: + ath_beacon_config_sta(sc, &conf, avp); + break; + default: + DPRINTF(sc, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); + return; + } + + sc->sc_flags |= SC_OP_BEACONS; + } } |