1ba5c15d9SAdrian Chadd /*- 2ba5c15d9SAdrian Chadd * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3ba5c15d9SAdrian Chadd * All rights reserved. 4ba5c15d9SAdrian Chadd * 5ba5c15d9SAdrian Chadd * Redistribution and use in source and binary forms, with or without 6ba5c15d9SAdrian Chadd * modification, are permitted provided that the following conditions 7ba5c15d9SAdrian Chadd * are met: 8ba5c15d9SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 9ba5c15d9SAdrian Chadd * notice, this list of conditions and the following disclaimer, 10ba5c15d9SAdrian Chadd * without modification. 11ba5c15d9SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12ba5c15d9SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13ba5c15d9SAdrian Chadd * redistribution must be conditioned upon including a substantially 14ba5c15d9SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 15ba5c15d9SAdrian Chadd * 16ba5c15d9SAdrian Chadd * NO WARRANTY 17ba5c15d9SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18ba5c15d9SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19ba5c15d9SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20ba5c15d9SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21ba5c15d9SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22ba5c15d9SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23ba5c15d9SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24ba5c15d9SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25ba5c15d9SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26ba5c15d9SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27ba5c15d9SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 28ba5c15d9SAdrian Chadd */ 29ba5c15d9SAdrian Chadd 30ba5c15d9SAdrian Chadd #include <sys/cdefs.h> 31ba5c15d9SAdrian Chadd __FBSDID("$FreeBSD$"); 32ba5c15d9SAdrian Chadd 33ba5c15d9SAdrian Chadd /* 34ba5c15d9SAdrian Chadd * Driver for the Atheros Wireless LAN controller. 35ba5c15d9SAdrian Chadd * 36ba5c15d9SAdrian Chadd * This software is derived from work of Atsushi Onoe; his contribution 37ba5c15d9SAdrian Chadd * is greatly appreciated. 38ba5c15d9SAdrian Chadd */ 39ba5c15d9SAdrian Chadd 40ba5c15d9SAdrian Chadd #include "opt_inet.h" 41ba5c15d9SAdrian Chadd #include "opt_ath.h" 42ba5c15d9SAdrian Chadd /* 43ba5c15d9SAdrian Chadd * This is needed for register operations which are performed 44ba5c15d9SAdrian Chadd * by the driver - eg, calls to ath_hal_gettsf32(). 45ba5c15d9SAdrian Chadd * 46ba5c15d9SAdrian Chadd * It's also required for any AH_DEBUG checks in here, eg the 47ba5c15d9SAdrian Chadd * module dependencies. 48ba5c15d9SAdrian Chadd */ 49ba5c15d9SAdrian Chadd #include "opt_ah.h" 50ba5c15d9SAdrian Chadd #include "opt_wlan.h" 51ba5c15d9SAdrian Chadd 52ba5c15d9SAdrian Chadd #include <sys/param.h> 53ba5c15d9SAdrian Chadd #include <sys/systm.h> 54ba5c15d9SAdrian Chadd #include <sys/sysctl.h> 55ba5c15d9SAdrian Chadd #include <sys/mbuf.h> 56ba5c15d9SAdrian Chadd #include <sys/malloc.h> 57ba5c15d9SAdrian Chadd #include <sys/lock.h> 58ba5c15d9SAdrian Chadd #include <sys/mutex.h> 59ba5c15d9SAdrian Chadd #include <sys/kernel.h> 60ba5c15d9SAdrian Chadd #include <sys/socket.h> 61ba5c15d9SAdrian Chadd #include <sys/sockio.h> 62ba5c15d9SAdrian Chadd #include <sys/errno.h> 63ba5c15d9SAdrian Chadd #include <sys/callout.h> 64ba5c15d9SAdrian Chadd #include <sys/bus.h> 65ba5c15d9SAdrian Chadd #include <sys/endian.h> 66ba5c15d9SAdrian Chadd #include <sys/kthread.h> 67ba5c15d9SAdrian Chadd #include <sys/taskqueue.h> 68ba5c15d9SAdrian Chadd #include <sys/priv.h> 69ba5c15d9SAdrian Chadd #include <sys/module.h> 70ba5c15d9SAdrian Chadd #include <sys/ktr.h> 71ba5c15d9SAdrian Chadd #include <sys/smp.h> /* for mp_ncpus */ 72ba5c15d9SAdrian Chadd 73ba5c15d9SAdrian Chadd #include <machine/bus.h> 74ba5c15d9SAdrian Chadd 75ba5c15d9SAdrian Chadd #include <net/if.h> 7676039bc8SGleb Smirnoff #include <net/if_var.h> 77ba5c15d9SAdrian Chadd #include <net/if_dl.h> 78ba5c15d9SAdrian Chadd #include <net/if_media.h> 79ba5c15d9SAdrian Chadd #include <net/if_types.h> 80ba5c15d9SAdrian Chadd #include <net/if_arp.h> 81ba5c15d9SAdrian Chadd #include <net/ethernet.h> 82ba5c15d9SAdrian Chadd #include <net/if_llc.h> 83ba5c15d9SAdrian Chadd 84ba5c15d9SAdrian Chadd #include <net80211/ieee80211_var.h> 85ba5c15d9SAdrian Chadd #include <net80211/ieee80211_regdomain.h> 86ba5c15d9SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG 87ba5c15d9SAdrian Chadd #include <net80211/ieee80211_superg.h> 88ba5c15d9SAdrian Chadd #endif 89ba5c15d9SAdrian Chadd 90ba5c15d9SAdrian Chadd #include <net/bpf.h> 91ba5c15d9SAdrian Chadd 92ba5c15d9SAdrian Chadd #ifdef INET 93ba5c15d9SAdrian Chadd #include <netinet/in.h> 94ba5c15d9SAdrian Chadd #include <netinet/if_ether.h> 95ba5c15d9SAdrian Chadd #endif 96ba5c15d9SAdrian Chadd 97ba5c15d9SAdrian Chadd #include <dev/ath/if_athvar.h> 98ba5c15d9SAdrian Chadd 99ba5c15d9SAdrian Chadd #include <dev/ath/if_ath_debug.h> 100ba5c15d9SAdrian Chadd #include <dev/ath/if_ath_misc.h> 101ba5c15d9SAdrian Chadd #include <dev/ath/if_ath_tx.h> 102ba5c15d9SAdrian Chadd #include <dev/ath/if_ath_beacon.h> 103ba5c15d9SAdrian Chadd 104ba5c15d9SAdrian Chadd #ifdef ATH_TX99_DIAG 105ba5c15d9SAdrian Chadd #include <dev/ath/ath_tx99/ath_tx99.h> 106ba5c15d9SAdrian Chadd #endif 107ba5c15d9SAdrian Chadd 108ba5c15d9SAdrian Chadd /* 109ba5c15d9SAdrian Chadd * Setup a h/w transmit queue for beacons. 110ba5c15d9SAdrian Chadd */ 111ba5c15d9SAdrian Chadd int 112e1252ce1SAdrian Chadd ath_beaconq_setup(struct ath_softc *sc) 113ba5c15d9SAdrian Chadd { 114e1252ce1SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 115ba5c15d9SAdrian Chadd HAL_TXQ_INFO qi; 116ba5c15d9SAdrian Chadd 117ba5c15d9SAdrian Chadd memset(&qi, 0, sizeof(qi)); 118ba5c15d9SAdrian Chadd qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 119ba5c15d9SAdrian Chadd qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 120ba5c15d9SAdrian Chadd qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 121ba5c15d9SAdrian Chadd /* NB: for dynamic turbo, don't enable any other interrupts */ 122ba5c15d9SAdrian Chadd qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; 123e1252ce1SAdrian Chadd if (sc->sc_isedma) 124e1252ce1SAdrian Chadd qi.tqi_qflags |= HAL_TXQ_TXOKINT_ENABLE | 125e1252ce1SAdrian Chadd HAL_TXQ_TXERRINT_ENABLE; 126e1252ce1SAdrian Chadd 127ba5c15d9SAdrian Chadd return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); 128ba5c15d9SAdrian Chadd } 129ba5c15d9SAdrian Chadd 130ba5c15d9SAdrian Chadd /* 131ba5c15d9SAdrian Chadd * Setup the transmit queue parameters for the beacon queue. 132ba5c15d9SAdrian Chadd */ 133ba5c15d9SAdrian Chadd int 134ba5c15d9SAdrian Chadd ath_beaconq_config(struct ath_softc *sc) 135ba5c15d9SAdrian Chadd { 136ba5c15d9SAdrian Chadd #define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1) 137ba5c15d9SAdrian Chadd struct ieee80211com *ic = sc->sc_ifp->if_l2com; 138ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 139ba5c15d9SAdrian Chadd HAL_TXQ_INFO qi; 140ba5c15d9SAdrian Chadd 141ba5c15d9SAdrian Chadd ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi); 142ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_HOSTAP || 143ba5c15d9SAdrian Chadd ic->ic_opmode == IEEE80211_M_MBSS) { 144ba5c15d9SAdrian Chadd /* 145ba5c15d9SAdrian Chadd * Always burst out beacon and CAB traffic. 146ba5c15d9SAdrian Chadd */ 147ba5c15d9SAdrian Chadd qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; 148ba5c15d9SAdrian Chadd qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT; 149ba5c15d9SAdrian Chadd qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT; 150ba5c15d9SAdrian Chadd } else { 151ba5c15d9SAdrian Chadd struct wmeParams *wmep = 152ba5c15d9SAdrian Chadd &ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE]; 153ba5c15d9SAdrian Chadd /* 154ba5c15d9SAdrian Chadd * Adhoc mode; important thing is to use 2x cwmin. 155ba5c15d9SAdrian Chadd */ 156ba5c15d9SAdrian Chadd qi.tqi_aifs = wmep->wmep_aifsn; 157ba5c15d9SAdrian Chadd qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 158ba5c15d9SAdrian Chadd qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 159ba5c15d9SAdrian Chadd } 160ba5c15d9SAdrian Chadd 161ba5c15d9SAdrian Chadd if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) { 162ba5c15d9SAdrian Chadd device_printf(sc->sc_dev, "unable to update parameters for " 163ba5c15d9SAdrian Chadd "beacon hardware queue!\n"); 164ba5c15d9SAdrian Chadd return 0; 165ba5c15d9SAdrian Chadd } else { 166ba5c15d9SAdrian Chadd ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ 167ba5c15d9SAdrian Chadd return 1; 168ba5c15d9SAdrian Chadd } 169ba5c15d9SAdrian Chadd #undef ATH_EXPONENT_TO_VALUE 170ba5c15d9SAdrian Chadd } 171ba5c15d9SAdrian Chadd 172ba5c15d9SAdrian Chadd /* 173ba5c15d9SAdrian Chadd * Allocate and setup an initial beacon frame. 174ba5c15d9SAdrian Chadd */ 175ba5c15d9SAdrian Chadd int 176ba5c15d9SAdrian Chadd ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) 177ba5c15d9SAdrian Chadd { 178ba5c15d9SAdrian Chadd struct ieee80211vap *vap = ni->ni_vap; 179ba5c15d9SAdrian Chadd struct ath_vap *avp = ATH_VAP(vap); 180ba5c15d9SAdrian Chadd struct ath_buf *bf; 181ba5c15d9SAdrian Chadd struct mbuf *m; 182ba5c15d9SAdrian Chadd int error; 183ba5c15d9SAdrian Chadd 184ba5c15d9SAdrian Chadd bf = avp->av_bcbuf; 185ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_NODE, "%s: bf_m=%p, bf_node=%p\n", 186ba5c15d9SAdrian Chadd __func__, bf->bf_m, bf->bf_node); 187ba5c15d9SAdrian Chadd if (bf->bf_m != NULL) { 188ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 189ba5c15d9SAdrian Chadd m_freem(bf->bf_m); 190ba5c15d9SAdrian Chadd bf->bf_m = NULL; 191ba5c15d9SAdrian Chadd } 192ba5c15d9SAdrian Chadd if (bf->bf_node != NULL) { 193ba5c15d9SAdrian Chadd ieee80211_free_node(bf->bf_node); 194ba5c15d9SAdrian Chadd bf->bf_node = NULL; 195ba5c15d9SAdrian Chadd } 196ba5c15d9SAdrian Chadd 197ba5c15d9SAdrian Chadd /* 198ba5c15d9SAdrian Chadd * NB: the beacon data buffer must be 32-bit aligned; 199ba5c15d9SAdrian Chadd * we assume the mbuf routines will return us something 200ba5c15d9SAdrian Chadd * with this alignment (perhaps should assert). 201ba5c15d9SAdrian Chadd */ 202ba5c15d9SAdrian Chadd m = ieee80211_beacon_alloc(ni, &avp->av_boff); 203ba5c15d9SAdrian Chadd if (m == NULL) { 204ba5c15d9SAdrian Chadd device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__); 205ba5c15d9SAdrian Chadd sc->sc_stats.ast_be_nombuf++; 206ba5c15d9SAdrian Chadd return ENOMEM; 207ba5c15d9SAdrian Chadd } 208ba5c15d9SAdrian Chadd error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 209ba5c15d9SAdrian Chadd bf->bf_segs, &bf->bf_nseg, 210ba5c15d9SAdrian Chadd BUS_DMA_NOWAIT); 211ba5c15d9SAdrian Chadd if (error != 0) { 212ba5c15d9SAdrian Chadd device_printf(sc->sc_dev, 213ba5c15d9SAdrian Chadd "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n", 214ba5c15d9SAdrian Chadd __func__, error); 215ba5c15d9SAdrian Chadd m_freem(m); 216ba5c15d9SAdrian Chadd return error; 217ba5c15d9SAdrian Chadd } 218ba5c15d9SAdrian Chadd 219ba5c15d9SAdrian Chadd /* 220ba5c15d9SAdrian Chadd * Calculate a TSF adjustment factor required for staggered 221ba5c15d9SAdrian Chadd * beacons. Note that we assume the format of the beacon 222ba5c15d9SAdrian Chadd * frame leaves the tstamp field immediately following the 223ba5c15d9SAdrian Chadd * header. 224ba5c15d9SAdrian Chadd */ 225ba5c15d9SAdrian Chadd if (sc->sc_stagbeacons && avp->av_bslot > 0) { 226ba5c15d9SAdrian Chadd uint64_t tsfadjust; 227ba5c15d9SAdrian Chadd struct ieee80211_frame *wh; 228ba5c15d9SAdrian Chadd 229ba5c15d9SAdrian Chadd /* 230ba5c15d9SAdrian Chadd * The beacon interval is in TU's; the TSF is in usecs. 231ba5c15d9SAdrian Chadd * We figure out how many TU's to add to align the timestamp 232ba5c15d9SAdrian Chadd * then convert to TSF units and handle byte swapping before 233ba5c15d9SAdrian Chadd * inserting it in the frame. The hardware will then add this 234ba5c15d9SAdrian Chadd * each time a beacon frame is sent. Note that we align vap's 235ba5c15d9SAdrian Chadd * 1..N and leave vap 0 untouched. This means vap 0 has a 236ba5c15d9SAdrian Chadd * timestamp in one beacon interval while the others get a 237ba5c15d9SAdrian Chadd * timstamp aligned to the next interval. 238ba5c15d9SAdrian Chadd */ 239ba5c15d9SAdrian Chadd tsfadjust = ni->ni_intval * 240ba5c15d9SAdrian Chadd (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; 241ba5c15d9SAdrian Chadd tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ 242ba5c15d9SAdrian Chadd 243ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 244ba5c15d9SAdrian Chadd "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", 245ba5c15d9SAdrian Chadd __func__, sc->sc_stagbeacons ? "stagger" : "burst", 246ba5c15d9SAdrian Chadd avp->av_bslot, ni->ni_intval, 247ba5c15d9SAdrian Chadd (long long unsigned) le64toh(tsfadjust)); 248ba5c15d9SAdrian Chadd 249ba5c15d9SAdrian Chadd wh = mtod(m, struct ieee80211_frame *); 250ba5c15d9SAdrian Chadd memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); 251ba5c15d9SAdrian Chadd } 252ba5c15d9SAdrian Chadd bf->bf_m = m; 253ba5c15d9SAdrian Chadd bf->bf_node = ieee80211_ref_node(ni); 254ba5c15d9SAdrian Chadd 255ba5c15d9SAdrian Chadd return 0; 256ba5c15d9SAdrian Chadd } 257ba5c15d9SAdrian Chadd 258ba5c15d9SAdrian Chadd /* 259ba5c15d9SAdrian Chadd * Setup the beacon frame for transmit. 260ba5c15d9SAdrian Chadd */ 261ba5c15d9SAdrian Chadd static void 262ba5c15d9SAdrian Chadd ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) 263ba5c15d9SAdrian Chadd { 264ba5c15d9SAdrian Chadd #define USE_SHPREAMBLE(_ic) \ 265ba5c15d9SAdrian Chadd (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ 266ba5c15d9SAdrian Chadd == IEEE80211_F_SHPREAMBLE) 267ba5c15d9SAdrian Chadd struct ieee80211_node *ni = bf->bf_node; 268ba5c15d9SAdrian Chadd struct ieee80211com *ic = ni->ni_ic; 269ba5c15d9SAdrian Chadd struct mbuf *m = bf->bf_m; 270ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 271ba5c15d9SAdrian Chadd struct ath_desc *ds; 272ba5c15d9SAdrian Chadd int flags, antenna; 273ba5c15d9SAdrian Chadd const HAL_RATE_TABLE *rt; 274ba5c15d9SAdrian Chadd u_int8_t rix, rate; 27546634305SAdrian Chadd HAL_DMA_ADDR bufAddrList[4]; 27646634305SAdrian Chadd uint32_t segLenList[4]; 277e1252ce1SAdrian Chadd HAL_11N_RATE_SERIES rc[4]; 278ba5c15d9SAdrian Chadd 279ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n", 280ba5c15d9SAdrian Chadd __func__, m, m->m_len); 281ba5c15d9SAdrian Chadd 282ba5c15d9SAdrian Chadd /* setup descriptors */ 283ba5c15d9SAdrian Chadd ds = bf->bf_desc; 284ba5c15d9SAdrian Chadd bf->bf_last = bf; 285ba5c15d9SAdrian Chadd bf->bf_lastds = ds; 286ba5c15d9SAdrian Chadd 287ba5c15d9SAdrian Chadd flags = HAL_TXDESC_NOACK; 288ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { 289bb069955SAdrian Chadd /* self-linked descriptor */ 290bb069955SAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, ds, bf->bf_daddr); 291ba5c15d9SAdrian Chadd flags |= HAL_TXDESC_VEOL; 292ba5c15d9SAdrian Chadd /* 293ba5c15d9SAdrian Chadd * Let hardware handle antenna switching. 294ba5c15d9SAdrian Chadd */ 295ba5c15d9SAdrian Chadd antenna = sc->sc_txantenna; 296ba5c15d9SAdrian Chadd } else { 297bb069955SAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, ds, 0); 298ba5c15d9SAdrian Chadd /* 299ba5c15d9SAdrian Chadd * Switch antenna every 4 beacons. 300ba5c15d9SAdrian Chadd * XXX assumes two antenna 301ba5c15d9SAdrian Chadd */ 302ba5c15d9SAdrian Chadd if (sc->sc_txantenna != 0) 303ba5c15d9SAdrian Chadd antenna = sc->sc_txantenna; 304ba5c15d9SAdrian Chadd else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0) 305ba5c15d9SAdrian Chadd antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1); 306ba5c15d9SAdrian Chadd else 307ba5c15d9SAdrian Chadd antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1); 308ba5c15d9SAdrian Chadd } 309ba5c15d9SAdrian Chadd 310ba5c15d9SAdrian Chadd KASSERT(bf->bf_nseg == 1, 311ba5c15d9SAdrian Chadd ("multi-segment beacon frame; nseg %u", bf->bf_nseg)); 31246634305SAdrian Chadd 313ba5c15d9SAdrian Chadd /* 314ba5c15d9SAdrian Chadd * Calculate rate code. 315ba5c15d9SAdrian Chadd * XXX everything at min xmit rate 316ba5c15d9SAdrian Chadd */ 317ba5c15d9SAdrian Chadd rix = 0; 318ba5c15d9SAdrian Chadd rt = sc->sc_currates; 319ba5c15d9SAdrian Chadd rate = rt->info[rix].rateCode; 320ba5c15d9SAdrian Chadd if (USE_SHPREAMBLE(ic)) 321ba5c15d9SAdrian Chadd rate |= rt->info[rix].shortPreamble; 322ba5c15d9SAdrian Chadd ath_hal_setuptxdesc(ah, ds 323ba5c15d9SAdrian Chadd , m->m_len + IEEE80211_CRC_LEN /* frame length */ 324ba5c15d9SAdrian Chadd , sizeof(struct ieee80211_frame)/* header length */ 325ba5c15d9SAdrian Chadd , HAL_PKT_TYPE_BEACON /* Atheros packet type */ 32612087a07SAdrian Chadd , ieee80211_get_node_txpower(ni) /* txpower XXX */ 327ba5c15d9SAdrian Chadd , rate, 1 /* series 0 rate/tries */ 328ba5c15d9SAdrian Chadd , HAL_TXKEYIX_INVALID /* no encryption */ 329ba5c15d9SAdrian Chadd , antenna /* antenna mode */ 330ba5c15d9SAdrian Chadd , flags /* no ack, veol for beacons */ 331ba5c15d9SAdrian Chadd , 0 /* rts/cts rate */ 332ba5c15d9SAdrian Chadd , 0 /* rts/cts duration */ 333ba5c15d9SAdrian Chadd ); 334e1252ce1SAdrian Chadd 335e1252ce1SAdrian Chadd /* 336e1252ce1SAdrian Chadd * The EDMA HAL currently assumes that _all_ rate control 337e1252ce1SAdrian Chadd * settings are done in ath_hal_set11nratescenario(), rather 338e1252ce1SAdrian Chadd * than in ath_hal_setuptxdesc(). 339e1252ce1SAdrian Chadd */ 340e1252ce1SAdrian Chadd if (sc->sc_isedma) { 341e1252ce1SAdrian Chadd memset(&rc, 0, sizeof(rc)); 342e1252ce1SAdrian Chadd 343e1252ce1SAdrian Chadd rc[0].ChSel = sc->sc_txchainmask; 344e1252ce1SAdrian Chadd rc[0].Tries = 1; 345e1252ce1SAdrian Chadd rc[0].Rate = rt->info[rix].rateCode; 346e1252ce1SAdrian Chadd rc[0].RateIndex = rix; 347e1252ce1SAdrian Chadd rc[0].tx_power_cap = 0x3f; 348e1252ce1SAdrian Chadd rc[0].PktDuration = 349e1252ce1SAdrian Chadd ath_hal_computetxtime(ah, rt, roundup(m->m_len, 4), 350e1252ce1SAdrian Chadd rix, 0); 351e1252ce1SAdrian Chadd ath_hal_set11nratescenario(ah, ds, 0, 0, rc, 4, flags); 352e1252ce1SAdrian Chadd } 353e1252ce1SAdrian Chadd 354ba5c15d9SAdrian Chadd /* NB: beacon's BufLen must be a multiple of 4 bytes */ 35546634305SAdrian Chadd segLenList[0] = roundup(m->m_len, 4); 35646634305SAdrian Chadd segLenList[1] = segLenList[2] = segLenList[3] = 0; 35746634305SAdrian Chadd bufAddrList[0] = bf->bf_segs[0].ds_addr; 35846634305SAdrian Chadd bufAddrList[1] = bufAddrList[2] = bufAddrList[3] = 0; 359ba5c15d9SAdrian Chadd ath_hal_filltxdesc(ah, ds 36046634305SAdrian Chadd , bufAddrList 36146634305SAdrian Chadd , segLenList 36246634305SAdrian Chadd , 0 /* XXX desc id */ 36346634305SAdrian Chadd , sc->sc_bhalq /* hardware TXQ */ 364ba5c15d9SAdrian Chadd , AH_TRUE /* first segment */ 365ba5c15d9SAdrian Chadd , AH_TRUE /* last segment */ 366ba5c15d9SAdrian Chadd , ds /* first descriptor */ 367ba5c15d9SAdrian Chadd ); 368ba5c15d9SAdrian Chadd #if 0 369ba5c15d9SAdrian Chadd ath_desc_swap(ds); 370ba5c15d9SAdrian Chadd #endif 371ba5c15d9SAdrian Chadd #undef USE_SHPREAMBLE 372ba5c15d9SAdrian Chadd } 373ba5c15d9SAdrian Chadd 374ba5c15d9SAdrian Chadd void 375ba5c15d9SAdrian Chadd ath_beacon_update(struct ieee80211vap *vap, int item) 376ba5c15d9SAdrian Chadd { 377ba5c15d9SAdrian Chadd struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff; 378ba5c15d9SAdrian Chadd 379ba5c15d9SAdrian Chadd setbit(bo->bo_flags, item); 380ba5c15d9SAdrian Chadd } 381ba5c15d9SAdrian Chadd 382ba5c15d9SAdrian Chadd /* 383b837332dSAdrian Chadd * Handle a beacon miss. 384b837332dSAdrian Chadd */ 385f5c30c4eSAdrian Chadd void 386b837332dSAdrian Chadd ath_beacon_miss(struct ath_softc *sc) 387b837332dSAdrian Chadd { 388b837332dSAdrian Chadd HAL_SURVEY_SAMPLE hs; 389b837332dSAdrian Chadd HAL_BOOL ret; 390b837332dSAdrian Chadd uint32_t hangs; 391b837332dSAdrian Chadd 392b837332dSAdrian Chadd bzero(&hs, sizeof(hs)); 393b837332dSAdrian Chadd 394b837332dSAdrian Chadd ret = ath_hal_get_mib_cycle_counts(sc->sc_ah, &hs); 395b837332dSAdrian Chadd 396b837332dSAdrian Chadd if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { 397b837332dSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 398b837332dSAdrian Chadd "%s: hang=0x%08x\n", 399b837332dSAdrian Chadd __func__, 400b837332dSAdrian Chadd hangs); 401b837332dSAdrian Chadd } 402b837332dSAdrian Chadd 403370f81faSAdrian Chadd #ifdef ATH_DEBUG_ALQ 404370f81faSAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_MISSED_BEACON)) 405370f81faSAdrian Chadd if_ath_alq_post(&sc->sc_alq, ATH_ALQ_MISSED_BEACON, 0, NULL); 406370f81faSAdrian Chadd #endif 407370f81faSAdrian Chadd 408b837332dSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 409b837332dSAdrian Chadd "%s: valid=%d, txbusy=%u, rxbusy=%u, chanbusy=%u, " 410b837332dSAdrian Chadd "extchanbusy=%u, cyclecount=%u\n", 411b837332dSAdrian Chadd __func__, 412b837332dSAdrian Chadd ret, 413b837332dSAdrian Chadd hs.tx_busy, 414b837332dSAdrian Chadd hs.rx_busy, 415b837332dSAdrian Chadd hs.chan_busy, 416b837332dSAdrian Chadd hs.ext_chan_busy, 417b837332dSAdrian Chadd hs.cycle_count); 418b837332dSAdrian Chadd } 419b837332dSAdrian Chadd 420b837332dSAdrian Chadd /* 421ba5c15d9SAdrian Chadd * Transmit a beacon frame at SWBA. Dynamic updates to the 422ba5c15d9SAdrian Chadd * frame contents are done as needed and the slot time is 423ba5c15d9SAdrian Chadd * also adjusted based on current state. 424ba5c15d9SAdrian Chadd */ 425ba5c15d9SAdrian Chadd void 426ba5c15d9SAdrian Chadd ath_beacon_proc(void *arg, int pending) 427ba5c15d9SAdrian Chadd { 428ba5c15d9SAdrian Chadd struct ath_softc *sc = arg; 429ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 430ba5c15d9SAdrian Chadd struct ieee80211vap *vap; 431ba5c15d9SAdrian Chadd struct ath_buf *bf; 432ba5c15d9SAdrian Chadd int slot, otherant; 433ba5c15d9SAdrian Chadd uint32_t bfaddr; 434ba5c15d9SAdrian Chadd 435ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n", 436ba5c15d9SAdrian Chadd __func__, pending); 437ba5c15d9SAdrian Chadd /* 438ba5c15d9SAdrian Chadd * Check if the previous beacon has gone out. If 439ba5c15d9SAdrian Chadd * not don't try to post another, skip this period 440ba5c15d9SAdrian Chadd * and wait for the next. Missed beacons indicate 441ba5c15d9SAdrian Chadd * a problem and should not occur. If we miss too 442ba5c15d9SAdrian Chadd * many consecutive beacons reset the device. 443ba5c15d9SAdrian Chadd */ 444ba5c15d9SAdrian Chadd if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 445ba5c15d9SAdrian Chadd sc->sc_bmisscount++; 446ba5c15d9SAdrian Chadd sc->sc_stats.ast_be_missed++; 447b837332dSAdrian Chadd ath_beacon_miss(sc); 448ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 449ba5c15d9SAdrian Chadd "%s: missed %u consecutive beacons\n", 450ba5c15d9SAdrian Chadd __func__, sc->sc_bmisscount); 451ba5c15d9SAdrian Chadd if (sc->sc_bmisscount >= ath_bstuck_threshold) 452ba5c15d9SAdrian Chadd taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 453ba5c15d9SAdrian Chadd return; 454ba5c15d9SAdrian Chadd } 455ba5c15d9SAdrian Chadd if (sc->sc_bmisscount != 0) { 456ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 457ba5c15d9SAdrian Chadd "%s: resume beacon xmit after %u misses\n", 458ba5c15d9SAdrian Chadd __func__, sc->sc_bmisscount); 459ba5c15d9SAdrian Chadd sc->sc_bmisscount = 0; 460370f81faSAdrian Chadd #ifdef ATH_DEBUG_ALQ 461370f81faSAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_RESUME_BEACON)) 462370f81faSAdrian Chadd if_ath_alq_post(&sc->sc_alq, ATH_ALQ_RESUME_BEACON, 0, NULL); 463370f81faSAdrian Chadd #endif 464ba5c15d9SAdrian Chadd } 465ba5c15d9SAdrian Chadd 466ba5c15d9SAdrian Chadd if (sc->sc_stagbeacons) { /* staggered beacons */ 467ba5c15d9SAdrian Chadd struct ieee80211com *ic = sc->sc_ifp->if_l2com; 468ba5c15d9SAdrian Chadd uint32_t tsftu; 469ba5c15d9SAdrian Chadd 470ba5c15d9SAdrian Chadd tsftu = ath_hal_gettsf32(ah) >> 10; 471ba5c15d9SAdrian Chadd /* XXX lintval */ 472ba5c15d9SAdrian Chadd slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval; 473ba5c15d9SAdrian Chadd vap = sc->sc_bslot[(slot+1) % ATH_BCBUF]; 474ba5c15d9SAdrian Chadd bfaddr = 0; 475ba5c15d9SAdrian Chadd if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { 476ba5c15d9SAdrian Chadd bf = ath_beacon_generate(sc, vap); 477ba5c15d9SAdrian Chadd if (bf != NULL) 478ba5c15d9SAdrian Chadd bfaddr = bf->bf_daddr; 479ba5c15d9SAdrian Chadd } 480ba5c15d9SAdrian Chadd } else { /* burst'd beacons */ 481ba5c15d9SAdrian Chadd uint32_t *bflink = &bfaddr; 482ba5c15d9SAdrian Chadd 483ba5c15d9SAdrian Chadd for (slot = 0; slot < ATH_BCBUF; slot++) { 484ba5c15d9SAdrian Chadd vap = sc->sc_bslot[slot]; 485ba5c15d9SAdrian Chadd if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { 486ba5c15d9SAdrian Chadd bf = ath_beacon_generate(sc, vap); 48792e84e43SAdrian Chadd /* 48892e84e43SAdrian Chadd * XXX TODO: this should use settxdesclinkptr() 48992e84e43SAdrian Chadd * otherwise it won't work for EDMA chipsets! 49092e84e43SAdrian Chadd */ 491ba5c15d9SAdrian Chadd if (bf != NULL) { 492bb069955SAdrian Chadd /* XXX should do this using the ds */ 493ba5c15d9SAdrian Chadd *bflink = bf->bf_daddr; 494bb069955SAdrian Chadd ath_hal_gettxdesclinkptr(sc->sc_ah, 495bb069955SAdrian Chadd bf->bf_desc, &bflink); 496ba5c15d9SAdrian Chadd } 497ba5c15d9SAdrian Chadd } 498ba5c15d9SAdrian Chadd } 49992e84e43SAdrian Chadd /* 50092e84e43SAdrian Chadd * XXX TODO: this should use settxdesclinkptr() 50192e84e43SAdrian Chadd * otherwise it won't work for EDMA chipsets! 50292e84e43SAdrian Chadd */ 503ba5c15d9SAdrian Chadd *bflink = 0; /* terminate list */ 504ba5c15d9SAdrian Chadd } 505ba5c15d9SAdrian Chadd 506ba5c15d9SAdrian Chadd /* 507ba5c15d9SAdrian Chadd * Handle slot time change when a non-ERP station joins/leaves 508ba5c15d9SAdrian Chadd * an 11g network. The 802.11 layer notifies us via callback, 509ba5c15d9SAdrian Chadd * we mark updateslot, then wait one beacon before effecting 510ba5c15d9SAdrian Chadd * the change. This gives associated stations at least one 511ba5c15d9SAdrian Chadd * beacon interval to note the state change. 512ba5c15d9SAdrian Chadd */ 513ba5c15d9SAdrian Chadd /* XXX locking */ 514ba5c15d9SAdrian Chadd if (sc->sc_updateslot == UPDATE) { 515ba5c15d9SAdrian Chadd sc->sc_updateslot = COMMIT; /* commit next beacon */ 516ba5c15d9SAdrian Chadd sc->sc_slotupdate = slot; 517ba5c15d9SAdrian Chadd } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) 518ba5c15d9SAdrian Chadd ath_setslottime(sc); /* commit change to h/w */ 519ba5c15d9SAdrian Chadd 520ba5c15d9SAdrian Chadd /* 521ba5c15d9SAdrian Chadd * Check recent per-antenna transmit statistics and flip 522ba5c15d9SAdrian Chadd * the default antenna if noticeably more frames went out 523ba5c15d9SAdrian Chadd * on the non-default antenna. 524ba5c15d9SAdrian Chadd * XXX assumes 2 anntenae 525ba5c15d9SAdrian Chadd */ 526ba5c15d9SAdrian Chadd if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) { 527ba5c15d9SAdrian Chadd otherant = sc->sc_defant & 1 ? 2 : 1; 528ba5c15d9SAdrian Chadd if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 529ba5c15d9SAdrian Chadd ath_setdefantenna(sc, otherant); 530ba5c15d9SAdrian Chadd sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 531ba5c15d9SAdrian Chadd } 532ba5c15d9SAdrian Chadd 533b837332dSAdrian Chadd /* Program the CABQ with the contents of the CABQ txq and start it */ 534b837332dSAdrian Chadd ATH_TXQ_LOCK(sc->sc_cabq); 535b837332dSAdrian Chadd ath_beacon_cabq_start(sc); 536b837332dSAdrian Chadd ATH_TXQ_UNLOCK(sc->sc_cabq); 537b837332dSAdrian Chadd 538b837332dSAdrian Chadd /* Program the new beacon frame if we have one for this interval */ 539ba5c15d9SAdrian Chadd if (bfaddr != 0) { 540ba5c15d9SAdrian Chadd /* 541ba5c15d9SAdrian Chadd * Stop any current dma and put the new frame on the queue. 542ba5c15d9SAdrian Chadd * This should never fail since we check above that no frames 543ba5c15d9SAdrian Chadd * are still pending on the queue. 544ba5c15d9SAdrian Chadd */ 545e1252ce1SAdrian Chadd if (! sc->sc_isedma) { 546ba5c15d9SAdrian Chadd if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 547ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_ANY, 548ba5c15d9SAdrian Chadd "%s: beacon queue %u did not stop?\n", 549ba5c15d9SAdrian Chadd __func__, sc->sc_bhalq); 550ba5c15d9SAdrian Chadd } 551e1252ce1SAdrian Chadd } 552ba5c15d9SAdrian Chadd /* NB: cabq traffic should already be queued and primed */ 553e1252ce1SAdrian Chadd 554ba5c15d9SAdrian Chadd ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr); 555ba5c15d9SAdrian Chadd ath_hal_txstart(ah, sc->sc_bhalq); 556ba5c15d9SAdrian Chadd 557ba5c15d9SAdrian Chadd sc->sc_stats.ast_be_xmit++; 558ba5c15d9SAdrian Chadd } 559ba5c15d9SAdrian Chadd } 560ba5c15d9SAdrian Chadd 56192e84e43SAdrian Chadd static void 56292e84e43SAdrian Chadd ath_beacon_cabq_start_edma(struct ath_softc *sc) 56392e84e43SAdrian Chadd { 56492e84e43SAdrian Chadd struct ath_buf *bf, *bf_last; 56592e84e43SAdrian Chadd struct ath_txq *cabq = sc->sc_cabq; 56692e84e43SAdrian Chadd #if 0 56792e84e43SAdrian Chadd struct ath_buf *bfi; 56892e84e43SAdrian Chadd int i = 0; 56992e84e43SAdrian Chadd #endif 57092e84e43SAdrian Chadd 57192e84e43SAdrian Chadd ATH_TXQ_LOCK_ASSERT(cabq); 57292e84e43SAdrian Chadd 57392e84e43SAdrian Chadd if (TAILQ_EMPTY(&cabq->axq_q)) 57492e84e43SAdrian Chadd return; 57592e84e43SAdrian Chadd bf = TAILQ_FIRST(&cabq->axq_q); 57692e84e43SAdrian Chadd bf_last = TAILQ_LAST(&cabq->axq_q, axq_q_s); 57792e84e43SAdrian Chadd 578b837332dSAdrian Chadd /* 57992e84e43SAdrian Chadd * This is a dirty, dirty hack to push the contents of 58092e84e43SAdrian Chadd * the cabq staging queue into the FIFO. 581b837332dSAdrian Chadd * 58292e84e43SAdrian Chadd * This ideally should live in the EDMA code file 58392e84e43SAdrian Chadd * and only push things into the CABQ if there's a FIFO 58492e84e43SAdrian Chadd * slot. 58592e84e43SAdrian Chadd * 58692e84e43SAdrian Chadd * We can't treat this like a normal TX queue because 58792e84e43SAdrian Chadd * in the case of multi-VAP traffic, we may have to flush 58892e84e43SAdrian Chadd * the CABQ each new (staggered) beacon that goes out. 58992e84e43SAdrian Chadd * But for non-staggered beacons, we could in theory 59092e84e43SAdrian Chadd * handle multicast traffic for all VAPs in one FIFO 59192e84e43SAdrian Chadd * push. Just keep all of this in mind if you're wondering 59292e84e43SAdrian Chadd * how to correctly/better handle multi-VAP CABQ traffic 59392e84e43SAdrian Chadd * with EDMA. 594b837332dSAdrian Chadd */ 59592e84e43SAdrian Chadd 59692e84e43SAdrian Chadd /* 59792e84e43SAdrian Chadd * Is the CABQ FIFO free? If not, complain loudly and 59892e84e43SAdrian Chadd * don't queue anything. Maybe we'll flush the CABQ 59992e84e43SAdrian Chadd * traffic, maybe we won't. But that'll happen next 60092e84e43SAdrian Chadd * beacon interval. 60192e84e43SAdrian Chadd */ 60292e84e43SAdrian Chadd if (cabq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) { 60392e84e43SAdrian Chadd device_printf(sc->sc_dev, 60492e84e43SAdrian Chadd "%s: Q%d: CAB FIFO queue=%d?\n", 60592e84e43SAdrian Chadd __func__, 60692e84e43SAdrian Chadd cabq->axq_qnum, 60792e84e43SAdrian Chadd cabq->axq_fifo_depth); 60892e84e43SAdrian Chadd return; 60992e84e43SAdrian Chadd } 61092e84e43SAdrian Chadd 61192e84e43SAdrian Chadd /* 61292e84e43SAdrian Chadd * Ok, so here's the gymnastics reqiured to make this 61392e84e43SAdrian Chadd * all sensible. 61492e84e43SAdrian Chadd */ 61592e84e43SAdrian Chadd 61692e84e43SAdrian Chadd /* 61792e84e43SAdrian Chadd * Tag the first/last buffer appropriately. 61892e84e43SAdrian Chadd */ 61992e84e43SAdrian Chadd bf->bf_flags |= ATH_BUF_FIFOPTR; 62092e84e43SAdrian Chadd bf_last->bf_flags |= ATH_BUF_FIFOEND; 62192e84e43SAdrian Chadd 62292e84e43SAdrian Chadd #if 0 62392e84e43SAdrian Chadd i = 0; 62492e84e43SAdrian Chadd TAILQ_FOREACH(bfi, &cabq->axq_q, bf_list) { 62592e84e43SAdrian Chadd ath_printtxbuf(sc, bf, cabq->axq_qnum, i, 0); 62692e84e43SAdrian Chadd i++; 62792e84e43SAdrian Chadd } 62892e84e43SAdrian Chadd #endif 62992e84e43SAdrian Chadd 63092e84e43SAdrian Chadd /* 63192e84e43SAdrian Chadd * We now need to push this set of frames onto the tail 63292e84e43SAdrian Chadd * of the FIFO queue. We don't adjust the aggregate 63392e84e43SAdrian Chadd * count, only the queue depth counter(s). 63492e84e43SAdrian Chadd * We also need to blank the link pointer now. 63592e84e43SAdrian Chadd */ 63692e84e43SAdrian Chadd TAILQ_CONCAT(&cabq->fifo.axq_q, &cabq->axq_q, bf_list); 63792e84e43SAdrian Chadd cabq->axq_link = NULL; 63892e84e43SAdrian Chadd cabq->fifo.axq_depth += cabq->axq_depth; 63992e84e43SAdrian Chadd cabq->axq_depth = 0; 64092e84e43SAdrian Chadd 64192e84e43SAdrian Chadd /* Bump FIFO queue */ 64292e84e43SAdrian Chadd cabq->axq_fifo_depth++; 64392e84e43SAdrian Chadd 64492e84e43SAdrian Chadd /* Push the first entry into the hardware */ 64592e84e43SAdrian Chadd ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr); 6469be82a42SAdrian Chadd cabq->axq_flags |= ATH_TXQ_PUTRUNNING; 64792e84e43SAdrian Chadd 64892e84e43SAdrian Chadd /* NB: gated by beacon so safe to start here */ 64992e84e43SAdrian Chadd ath_hal_txstart(sc->sc_ah, cabq->axq_qnum); 65092e84e43SAdrian Chadd 65192e84e43SAdrian Chadd } 65292e84e43SAdrian Chadd 65392e84e43SAdrian Chadd static void 65492e84e43SAdrian Chadd ath_beacon_cabq_start_legacy(struct ath_softc *sc) 655b837332dSAdrian Chadd { 656b837332dSAdrian Chadd struct ath_buf *bf; 657b837332dSAdrian Chadd struct ath_txq *cabq = sc->sc_cabq; 658b837332dSAdrian Chadd 659b837332dSAdrian Chadd ATH_TXQ_LOCK_ASSERT(cabq); 660b837332dSAdrian Chadd if (TAILQ_EMPTY(&cabq->axq_q)) 661b837332dSAdrian Chadd return; 662b837332dSAdrian Chadd bf = TAILQ_FIRST(&cabq->axq_q); 663b837332dSAdrian Chadd 664b837332dSAdrian Chadd /* Push the first entry into the hardware */ 665b837332dSAdrian Chadd ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr); 6669be82a42SAdrian Chadd cabq->axq_flags |= ATH_TXQ_PUTRUNNING; 667b837332dSAdrian Chadd 668b837332dSAdrian Chadd /* NB: gated by beacon so safe to start here */ 669b837332dSAdrian Chadd ath_hal_txstart(sc->sc_ah, cabq->axq_qnum); 670b837332dSAdrian Chadd } 671b837332dSAdrian Chadd 67292e84e43SAdrian Chadd /* 67392e84e43SAdrian Chadd * Start CABQ transmission - this assumes that all frames are prepped 67492e84e43SAdrian Chadd * and ready in the CABQ. 67592e84e43SAdrian Chadd */ 67692e84e43SAdrian Chadd void 67792e84e43SAdrian Chadd ath_beacon_cabq_start(struct ath_softc *sc) 67892e84e43SAdrian Chadd { 67992e84e43SAdrian Chadd struct ath_txq *cabq = sc->sc_cabq; 68092e84e43SAdrian Chadd 68192e84e43SAdrian Chadd ATH_TXQ_LOCK_ASSERT(cabq); 68292e84e43SAdrian Chadd 68392e84e43SAdrian Chadd if (TAILQ_EMPTY(&cabq->axq_q)) 68492e84e43SAdrian Chadd return; 68592e84e43SAdrian Chadd 68692e84e43SAdrian Chadd if (sc->sc_isedma) 68792e84e43SAdrian Chadd ath_beacon_cabq_start_edma(sc); 68892e84e43SAdrian Chadd else 68992e84e43SAdrian Chadd ath_beacon_cabq_start_legacy(sc); 69092e84e43SAdrian Chadd } 69192e84e43SAdrian Chadd 692ba5c15d9SAdrian Chadd struct ath_buf * 693ba5c15d9SAdrian Chadd ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) 694ba5c15d9SAdrian Chadd { 695ba5c15d9SAdrian Chadd struct ath_vap *avp = ATH_VAP(vap); 696ba5c15d9SAdrian Chadd struct ath_txq *cabq = sc->sc_cabq; 697ba5c15d9SAdrian Chadd struct ath_buf *bf; 698ba5c15d9SAdrian Chadd struct mbuf *m; 699ba5c15d9SAdrian Chadd int nmcastq, error; 700ba5c15d9SAdrian Chadd 701ba5c15d9SAdrian Chadd KASSERT(vap->iv_state >= IEEE80211_S_RUN, 702ba5c15d9SAdrian Chadd ("not running, state %d", vap->iv_state)); 703ba5c15d9SAdrian Chadd KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 704ba5c15d9SAdrian Chadd 705ba5c15d9SAdrian Chadd /* 706ba5c15d9SAdrian Chadd * Update dynamic beacon contents. If this returns 707ba5c15d9SAdrian Chadd * non-zero then we need to remap the memory because 708ba5c15d9SAdrian Chadd * the beacon frame changed size (probably because 709ba5c15d9SAdrian Chadd * of the TIM bitmap). 710ba5c15d9SAdrian Chadd */ 711ba5c15d9SAdrian Chadd bf = avp->av_bcbuf; 712ba5c15d9SAdrian Chadd m = bf->bf_m; 713ba5c15d9SAdrian Chadd /* XXX lock mcastq? */ 714ba5c15d9SAdrian Chadd nmcastq = avp->av_mcastq.axq_depth; 715ba5c15d9SAdrian Chadd 716ba5c15d9SAdrian Chadd if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) { 717ba5c15d9SAdrian Chadd /* XXX too conservative? */ 718ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 719ba5c15d9SAdrian Chadd error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 720ba5c15d9SAdrian Chadd bf->bf_segs, &bf->bf_nseg, 721ba5c15d9SAdrian Chadd BUS_DMA_NOWAIT); 722ba5c15d9SAdrian Chadd if (error != 0) { 723ba5c15d9SAdrian Chadd if_printf(vap->iv_ifp, 724ba5c15d9SAdrian Chadd "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 725ba5c15d9SAdrian Chadd __func__, error); 726ba5c15d9SAdrian Chadd return NULL; 727ba5c15d9SAdrian Chadd } 728ba5c15d9SAdrian Chadd } 729ba5c15d9SAdrian Chadd if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) { 730ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 731ba5c15d9SAdrian Chadd "%s: cabq did not drain, mcastq %u cabq %u\n", 732ba5c15d9SAdrian Chadd __func__, nmcastq, cabq->axq_depth); 733ba5c15d9SAdrian Chadd sc->sc_stats.ast_cabq_busy++; 734ba5c15d9SAdrian Chadd if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) { 735ba5c15d9SAdrian Chadd /* 736ba5c15d9SAdrian Chadd * CABQ traffic from a previous vap is still pending. 737ba5c15d9SAdrian Chadd * We must drain the q before this beacon frame goes 738ba5c15d9SAdrian Chadd * out as otherwise this vap's stations will get cab 739ba5c15d9SAdrian Chadd * frames from a different vap. 740ba5c15d9SAdrian Chadd * XXX could be slow causing us to miss DBA 741ba5c15d9SAdrian Chadd */ 7429be82a42SAdrian Chadd /* 7439be82a42SAdrian Chadd * XXX TODO: this doesn't stop CABQ DMA - it assumes 7449be82a42SAdrian Chadd * that since we're about to transmit a beacon, we've 7459be82a42SAdrian Chadd * already stopped transmitting on the CABQ. But this 7469be82a42SAdrian Chadd * doesn't at all mean that the CABQ DMA QCU will 7479be82a42SAdrian Chadd * accept a new TXDP! So what, should we do a DMA 7489be82a42SAdrian Chadd * stop? What if it fails? 7499be82a42SAdrian Chadd * 7509be82a42SAdrian Chadd * More thought is required here. 7519be82a42SAdrian Chadd */ 752*062cf7d9SAdrian Chadd /* 753*062cf7d9SAdrian Chadd * XXX can we even stop TX DMA here? Check what the 754*062cf7d9SAdrian Chadd * reference driver does for cabq for beacons, given 755*062cf7d9SAdrian Chadd * that stopping TX requires RX is paused. 756*062cf7d9SAdrian Chadd */ 757ba5c15d9SAdrian Chadd ath_tx_draintxq(sc, cabq); 758ba5c15d9SAdrian Chadd } 759ba5c15d9SAdrian Chadd } 760ba5c15d9SAdrian Chadd ath_beacon_setup(sc, bf); 761ba5c15d9SAdrian Chadd bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 762ba5c15d9SAdrian Chadd 763ba5c15d9SAdrian Chadd /* 764ba5c15d9SAdrian Chadd * Enable the CAB queue before the beacon queue to 765ba5c15d9SAdrian Chadd * insure cab frames are triggered by this beacon. 766ba5c15d9SAdrian Chadd */ 767ba5c15d9SAdrian Chadd if (avp->av_boff.bo_tim[4] & 1) { 768ba5c15d9SAdrian Chadd 769ba5c15d9SAdrian Chadd /* NB: only at DTIM */ 770b837332dSAdrian Chadd ATH_TXQ_LOCK(&avp->av_mcastq); 771ba5c15d9SAdrian Chadd if (nmcastq) { 772b6ef0f8aSAdrian Chadd struct ath_buf *bfm, *bfc_last; 773ba5c15d9SAdrian Chadd 774ba5c15d9SAdrian Chadd /* 775ba5c15d9SAdrian Chadd * Move frames from the s/w mcast q to the h/w cab q. 77674ea88c3SAdrian Chadd * 777b837332dSAdrian Chadd * XXX TODO: if we chain together multiple VAPs 778b837332dSAdrian Chadd * worth of CABQ traffic, should we keep the 779b837332dSAdrian Chadd * MORE data bit set on the last frame of each 780b837332dSAdrian Chadd * intermediary VAP (ie, only clear the MORE 781b837332dSAdrian Chadd * bit of the last frame on the last vap?) 782ba5c15d9SAdrian Chadd */ 783ba5c15d9SAdrian Chadd bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q); 784b837332dSAdrian Chadd ATH_TXQ_LOCK(cabq); 785b6ef0f8aSAdrian Chadd 786b6ef0f8aSAdrian Chadd /* 787b6ef0f8aSAdrian Chadd * If there's already a frame on the CABQ, we 788b6ef0f8aSAdrian Chadd * need to link to the end of the last frame. 789b6ef0f8aSAdrian Chadd * We can't use axq_link here because 790b6ef0f8aSAdrian Chadd * EDMA descriptors require some recalculation 791b6ef0f8aSAdrian Chadd * (checksum) to occur. 792b6ef0f8aSAdrian Chadd */ 793b6ef0f8aSAdrian Chadd bfc_last = ATH_TXQ_LAST(cabq, axq_q_s); 794b6ef0f8aSAdrian Chadd if (bfc_last != NULL) { 795b6ef0f8aSAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, 796b6ef0f8aSAdrian Chadd bfc_last->bf_lastds, 797b6ef0f8aSAdrian Chadd bfm->bf_daddr); 798b6ef0f8aSAdrian Chadd } 799ba5c15d9SAdrian Chadd ath_txqmove(cabq, &avp->av_mcastq); 800b837332dSAdrian Chadd ATH_TXQ_UNLOCK(cabq); 801b837332dSAdrian Chadd /* 802b837332dSAdrian Chadd * XXX not entirely accurate, in case a mcast 803b837332dSAdrian Chadd * queue frame arrived before we grabbed the TX 804b837332dSAdrian Chadd * lock. 805b837332dSAdrian Chadd */ 806ba5c15d9SAdrian Chadd sc->sc_stats.ast_cabq_xmit += nmcastq; 807ba5c15d9SAdrian Chadd } 808b837332dSAdrian Chadd ATH_TXQ_UNLOCK(&avp->av_mcastq); 809ba5c15d9SAdrian Chadd } 810ba5c15d9SAdrian Chadd return bf; 811ba5c15d9SAdrian Chadd } 812ba5c15d9SAdrian Chadd 813ba5c15d9SAdrian Chadd void 814ba5c15d9SAdrian Chadd ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) 815ba5c15d9SAdrian Chadd { 816ba5c15d9SAdrian Chadd struct ath_vap *avp = ATH_VAP(vap); 817ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 818ba5c15d9SAdrian Chadd struct ath_buf *bf; 819ba5c15d9SAdrian Chadd struct mbuf *m; 820ba5c15d9SAdrian Chadd int error; 821ba5c15d9SAdrian Chadd 822ba5c15d9SAdrian Chadd KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 823ba5c15d9SAdrian Chadd 824ba5c15d9SAdrian Chadd /* 825ba5c15d9SAdrian Chadd * Update dynamic beacon contents. If this returns 826ba5c15d9SAdrian Chadd * non-zero then we need to remap the memory because 827ba5c15d9SAdrian Chadd * the beacon frame changed size (probably because 828ba5c15d9SAdrian Chadd * of the TIM bitmap). 829ba5c15d9SAdrian Chadd */ 830ba5c15d9SAdrian Chadd bf = avp->av_bcbuf; 831ba5c15d9SAdrian Chadd m = bf->bf_m; 832ba5c15d9SAdrian Chadd if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) { 833ba5c15d9SAdrian Chadd /* XXX too conservative? */ 834ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 835ba5c15d9SAdrian Chadd error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 836ba5c15d9SAdrian Chadd bf->bf_segs, &bf->bf_nseg, 837ba5c15d9SAdrian Chadd BUS_DMA_NOWAIT); 838ba5c15d9SAdrian Chadd if (error != 0) { 839ba5c15d9SAdrian Chadd if_printf(vap->iv_ifp, 840ba5c15d9SAdrian Chadd "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 841ba5c15d9SAdrian Chadd __func__, error); 842ba5c15d9SAdrian Chadd return; 843ba5c15d9SAdrian Chadd } 844ba5c15d9SAdrian Chadd } 845ba5c15d9SAdrian Chadd ath_beacon_setup(sc, bf); 846ba5c15d9SAdrian Chadd bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 847ba5c15d9SAdrian Chadd 848ba5c15d9SAdrian Chadd /* NB: caller is known to have already stopped tx dma */ 849ba5c15d9SAdrian Chadd ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 850ba5c15d9SAdrian Chadd ath_hal_txstart(ah, sc->sc_bhalq); 851ba5c15d9SAdrian Chadd } 852ba5c15d9SAdrian Chadd 853ba5c15d9SAdrian Chadd /* 854ba5c15d9SAdrian Chadd * Reclaim beacon resources and return buffer to the pool. 855ba5c15d9SAdrian Chadd */ 856ba5c15d9SAdrian Chadd void 857ba5c15d9SAdrian Chadd ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) 858ba5c15d9SAdrian Chadd { 859ba5c15d9SAdrian Chadd 860ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_NODE, "%s: free bf=%p, bf_m=%p, bf_node=%p\n", 861ba5c15d9SAdrian Chadd __func__, bf, bf->bf_m, bf->bf_node); 862ba5c15d9SAdrian Chadd if (bf->bf_m != NULL) { 863ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 864ba5c15d9SAdrian Chadd m_freem(bf->bf_m); 865ba5c15d9SAdrian Chadd bf->bf_m = NULL; 866ba5c15d9SAdrian Chadd } 867ba5c15d9SAdrian Chadd if (bf->bf_node != NULL) { 868ba5c15d9SAdrian Chadd ieee80211_free_node(bf->bf_node); 869ba5c15d9SAdrian Chadd bf->bf_node = NULL; 870ba5c15d9SAdrian Chadd } 871ba5c15d9SAdrian Chadd TAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); 872ba5c15d9SAdrian Chadd } 873ba5c15d9SAdrian Chadd 874ba5c15d9SAdrian Chadd /* 875ba5c15d9SAdrian Chadd * Reclaim beacon resources. 876ba5c15d9SAdrian Chadd */ 877ba5c15d9SAdrian Chadd void 878ba5c15d9SAdrian Chadd ath_beacon_free(struct ath_softc *sc) 879ba5c15d9SAdrian Chadd { 880ba5c15d9SAdrian Chadd struct ath_buf *bf; 881ba5c15d9SAdrian Chadd 882ba5c15d9SAdrian Chadd TAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { 883ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_NODE, 884ba5c15d9SAdrian Chadd "%s: free bf=%p, bf_m=%p, bf_node=%p\n", 885ba5c15d9SAdrian Chadd __func__, bf, bf->bf_m, bf->bf_node); 886ba5c15d9SAdrian Chadd if (bf->bf_m != NULL) { 887ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 888ba5c15d9SAdrian Chadd m_freem(bf->bf_m); 889ba5c15d9SAdrian Chadd bf->bf_m = NULL; 890ba5c15d9SAdrian Chadd } 891ba5c15d9SAdrian Chadd if (bf->bf_node != NULL) { 892ba5c15d9SAdrian Chadd ieee80211_free_node(bf->bf_node); 893ba5c15d9SAdrian Chadd bf->bf_node = NULL; 894ba5c15d9SAdrian Chadd } 895ba5c15d9SAdrian Chadd } 896ba5c15d9SAdrian Chadd } 897ba5c15d9SAdrian Chadd 898ba5c15d9SAdrian Chadd /* 899ba5c15d9SAdrian Chadd * Configure the beacon and sleep timers. 900ba5c15d9SAdrian Chadd * 901ba5c15d9SAdrian Chadd * When operating as an AP this resets the TSF and sets 902ba5c15d9SAdrian Chadd * up the hardware to notify us when we need to issue beacons. 903ba5c15d9SAdrian Chadd * 904ba5c15d9SAdrian Chadd * When operating in station mode this sets up the beacon 905ba5c15d9SAdrian Chadd * timers according to the timestamp of the last received 906ba5c15d9SAdrian Chadd * beacon and the current TSF, configures PCF and DTIM 907ba5c15d9SAdrian Chadd * handling, programs the sleep registers so the hardware 908ba5c15d9SAdrian Chadd * will wakeup in time to receive beacons, and configures 909ba5c15d9SAdrian Chadd * the beacon miss handling so we'll receive a BMISS 910ba5c15d9SAdrian Chadd * interrupt when we stop seeing beacons from the AP 911ba5c15d9SAdrian Chadd * we've associated with. 912ba5c15d9SAdrian Chadd */ 913ba5c15d9SAdrian Chadd void 914ba5c15d9SAdrian Chadd ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 915ba5c15d9SAdrian Chadd { 916ba5c15d9SAdrian Chadd #define TSF_TO_TU(_h,_l) \ 917ba5c15d9SAdrian Chadd ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 918ba5c15d9SAdrian Chadd #define FUDGE 2 919ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 920ba5c15d9SAdrian Chadd struct ieee80211com *ic = sc->sc_ifp->if_l2com; 921ba5c15d9SAdrian Chadd struct ieee80211_node *ni; 922ba5c15d9SAdrian Chadd u_int32_t nexttbtt, intval, tsftu; 923e1252ce1SAdrian Chadd u_int32_t nexttbtt_u8, intval_u8; 924f5c30c4eSAdrian Chadd u_int64_t tsf, tsf_beacon; 925ba5c15d9SAdrian Chadd 926ba5c15d9SAdrian Chadd if (vap == NULL) 927ba5c15d9SAdrian Chadd vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 92861cd9692SAdrian Chadd /* 92961cd9692SAdrian Chadd * Just ensure that we aren't being called when the last 93061cd9692SAdrian Chadd * VAP is destroyed. 93161cd9692SAdrian Chadd */ 93261cd9692SAdrian Chadd if (vap == NULL) { 93361cd9692SAdrian Chadd device_printf(sc->sc_dev, "%s: called with no VAPs\n", 93461cd9692SAdrian Chadd __func__); 93561cd9692SAdrian Chadd return; 93661cd9692SAdrian Chadd } 93761cd9692SAdrian Chadd 938ba5c15d9SAdrian Chadd ni = ieee80211_ref_node(vap->iv_bss); 939ba5c15d9SAdrian Chadd 940f5c30c4eSAdrian Chadd ATH_LOCK(sc); 941f5c30c4eSAdrian Chadd ath_power_set_power_state(sc, HAL_PM_AWAKE); 942f5c30c4eSAdrian Chadd ATH_UNLOCK(sc); 943f5c30c4eSAdrian Chadd 944ba5c15d9SAdrian Chadd /* extract tstamp from last beacon and convert to TU */ 945ba5c15d9SAdrian Chadd nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4), 946ba5c15d9SAdrian Chadd LE_READ_4(ni->ni_tstamp.data)); 947f5c30c4eSAdrian Chadd 948f5c30c4eSAdrian Chadd tsf_beacon = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; 949f5c30c4eSAdrian Chadd tsf_beacon |= LE_READ_4(ni->ni_tstamp.data); 950f5c30c4eSAdrian Chadd 951ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_HOSTAP || 952ba5c15d9SAdrian Chadd ic->ic_opmode == IEEE80211_M_MBSS) { 953ba5c15d9SAdrian Chadd /* 954ba5c15d9SAdrian Chadd * For multi-bss ap/mesh support beacons are either staggered 955ba5c15d9SAdrian Chadd * evenly over N slots or burst together. For the former 956ba5c15d9SAdrian Chadd * arrange for the SWBA to be delivered for each slot. 957ba5c15d9SAdrian Chadd * Slots that are not occupied will generate nothing. 958ba5c15d9SAdrian Chadd */ 959ba5c15d9SAdrian Chadd /* NB: the beacon interval is kept internally in TU's */ 960ba5c15d9SAdrian Chadd intval = ni->ni_intval & HAL_BEACON_PERIOD; 961ba5c15d9SAdrian Chadd if (sc->sc_stagbeacons) 962ba5c15d9SAdrian Chadd intval /= ATH_BCBUF; 963ba5c15d9SAdrian Chadd } else { 964ba5c15d9SAdrian Chadd /* NB: the beacon interval is kept internally in TU's */ 965ba5c15d9SAdrian Chadd intval = ni->ni_intval & HAL_BEACON_PERIOD; 966ba5c15d9SAdrian Chadd } 967ba5c15d9SAdrian Chadd if (nexttbtt == 0) /* e.g. for ap mode */ 968ba5c15d9SAdrian Chadd nexttbtt = intval; 969ba5c15d9SAdrian Chadd else if (intval) /* NB: can be 0 for monitor mode */ 970ba5c15d9SAdrian Chadd nexttbtt = roundup(nexttbtt, intval); 971ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", 972ba5c15d9SAdrian Chadd __func__, nexttbtt, intval, ni->ni_intval); 973ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { 974ba5c15d9SAdrian Chadd HAL_BEACON_STATE bs; 975ba5c15d9SAdrian Chadd int dtimperiod, dtimcount; 976ba5c15d9SAdrian Chadd int cfpperiod, cfpcount; 977ba5c15d9SAdrian Chadd 978ba5c15d9SAdrian Chadd /* 979ba5c15d9SAdrian Chadd * Setup dtim and cfp parameters according to 980ba5c15d9SAdrian Chadd * last beacon we received (which may be none). 981ba5c15d9SAdrian Chadd */ 982ba5c15d9SAdrian Chadd dtimperiod = ni->ni_dtim_period; 983ba5c15d9SAdrian Chadd if (dtimperiod <= 0) /* NB: 0 if not known */ 984ba5c15d9SAdrian Chadd dtimperiod = 1; 985ba5c15d9SAdrian Chadd dtimcount = ni->ni_dtim_count; 986ba5c15d9SAdrian Chadd if (dtimcount >= dtimperiod) /* NB: sanity check */ 987ba5c15d9SAdrian Chadd dtimcount = 0; /* XXX? */ 988ba5c15d9SAdrian Chadd cfpperiod = 1; /* NB: no PCF support yet */ 989ba5c15d9SAdrian Chadd cfpcount = 0; 990ba5c15d9SAdrian Chadd /* 991ba5c15d9SAdrian Chadd * Pull nexttbtt forward to reflect the current 992ba5c15d9SAdrian Chadd * TSF and calculate dtim+cfp state for the result. 993ba5c15d9SAdrian Chadd */ 994ba5c15d9SAdrian Chadd tsf = ath_hal_gettsf64(ah); 995ba5c15d9SAdrian Chadd tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 996f5c30c4eSAdrian Chadd 997f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 998f5c30c4eSAdrian Chadd "%s: beacon tsf=%llu, hw tsf=%llu, nexttbtt=%u, tsftu=%u\n", 999f5c30c4eSAdrian Chadd __func__, 1000f5c30c4eSAdrian Chadd (unsigned long long) tsf_beacon, 1001f5c30c4eSAdrian Chadd (unsigned long long) tsf, 1002f5c30c4eSAdrian Chadd nexttbtt, 1003f5c30c4eSAdrian Chadd tsftu); 1004f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1005f5c30c4eSAdrian Chadd "%s: beacon tsf=%llu, hw tsf=%llu, tsf delta=%lld\n", 1006f5c30c4eSAdrian Chadd __func__, 1007f5c30c4eSAdrian Chadd (unsigned long long) tsf_beacon, 1008f5c30c4eSAdrian Chadd (unsigned long long) tsf, 1009f5c30c4eSAdrian Chadd (long long) tsf - 1010f5c30c4eSAdrian Chadd (long long) tsf_beacon); 1011f5c30c4eSAdrian Chadd 1012f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1013f5c30c4eSAdrian Chadd "%s: nexttbtt=%llu, beacon tsf delta=%lld\n", 1014f5c30c4eSAdrian Chadd __func__, 1015f5c30c4eSAdrian Chadd (unsigned long long) nexttbtt, 1016f5c30c4eSAdrian Chadd (long long) ((long long) nexttbtt * 1024LL) - (long long) tsf_beacon); 1017f5c30c4eSAdrian Chadd 1018f5c30c4eSAdrian Chadd /* XXX cfpcount? */ 1019f5c30c4eSAdrian Chadd 1020f5c30c4eSAdrian Chadd if (nexttbtt > tsftu) { 1021f5c30c4eSAdrian Chadd uint32_t countdiff, oldtbtt, remainder; 1022f5c30c4eSAdrian Chadd 1023f5c30c4eSAdrian Chadd oldtbtt = nexttbtt; 1024f5c30c4eSAdrian Chadd remainder = (nexttbtt - tsftu) % intval; 1025f5c30c4eSAdrian Chadd nexttbtt = tsftu + remainder; 1026f5c30c4eSAdrian Chadd 1027f5c30c4eSAdrian Chadd countdiff = (oldtbtt - nexttbtt) / intval % dtimperiod; 1028f5c30c4eSAdrian Chadd if (dtimcount > countdiff) { 1029f5c30c4eSAdrian Chadd dtimcount -= countdiff; 1030f5c30c4eSAdrian Chadd } else { 1031f5c30c4eSAdrian Chadd dtimcount += dtimperiod - countdiff; 1032ba5c15d9SAdrian Chadd } 1033f5c30c4eSAdrian Chadd } else { //nexttbtt <= tsftu 1034f5c30c4eSAdrian Chadd uint32_t countdiff, oldtbtt, remainder; 1035f5c30c4eSAdrian Chadd 1036f5c30c4eSAdrian Chadd oldtbtt = nexttbtt; 1037f5c30c4eSAdrian Chadd remainder = (tsftu - nexttbtt) % intval; 1038f5c30c4eSAdrian Chadd nexttbtt = tsftu - remainder + intval; 1039f5c30c4eSAdrian Chadd countdiff = (nexttbtt - oldtbtt) / intval % dtimperiod; 1040f5c30c4eSAdrian Chadd if (dtimcount > countdiff) { 1041f5c30c4eSAdrian Chadd dtimcount -= countdiff; 1042f5c30c4eSAdrian Chadd } else { 1043f5c30c4eSAdrian Chadd dtimcount += dtimperiod - countdiff; 1044f5c30c4eSAdrian Chadd } 1045f5c30c4eSAdrian Chadd } 1046f5c30c4eSAdrian Chadd 1047f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1048f5c30c4eSAdrian Chadd "%s: adj nexttbtt=%llu, rx tsf delta=%lld\n", 1049f5c30c4eSAdrian Chadd __func__, 1050f5c30c4eSAdrian Chadd (unsigned long long) nexttbtt, 1051f5c30c4eSAdrian Chadd (long long) ((long long)nexttbtt * 1024LL) - (long long)tsf); 1052f5c30c4eSAdrian Chadd 1053ba5c15d9SAdrian Chadd memset(&bs, 0, sizeof(bs)); 1054ba5c15d9SAdrian Chadd bs.bs_intval = intval; 1055ba5c15d9SAdrian Chadd bs.bs_nexttbtt = nexttbtt; 1056ba5c15d9SAdrian Chadd bs.bs_dtimperiod = dtimperiod*intval; 1057ba5c15d9SAdrian Chadd bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 1058ba5c15d9SAdrian Chadd bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 1059ba5c15d9SAdrian Chadd bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 1060ba5c15d9SAdrian Chadd bs.bs_cfpmaxduration = 0; 1061ba5c15d9SAdrian Chadd #if 0 1062ba5c15d9SAdrian Chadd /* 1063ba5c15d9SAdrian Chadd * The 802.11 layer records the offset to the DTIM 1064ba5c15d9SAdrian Chadd * bitmap while receiving beacons; use it here to 1065ba5c15d9SAdrian Chadd * enable h/w detection of our AID being marked in 1066ba5c15d9SAdrian Chadd * the bitmap vector (to indicate frames for us are 1067ba5c15d9SAdrian Chadd * pending at the AP). 1068ba5c15d9SAdrian Chadd * XXX do DTIM handling in s/w to WAR old h/w bugs 1069ba5c15d9SAdrian Chadd * XXX enable based on h/w rev for newer chips 1070ba5c15d9SAdrian Chadd */ 1071ba5c15d9SAdrian Chadd bs.bs_timoffset = ni->ni_timoff; 1072ba5c15d9SAdrian Chadd #endif 1073ba5c15d9SAdrian Chadd /* 1074ba5c15d9SAdrian Chadd * Calculate the number of consecutive beacons to miss 1075ba5c15d9SAdrian Chadd * before taking a BMISS interrupt. 1076ba5c15d9SAdrian Chadd * Note that we clamp the result to at most 10 beacons. 1077ba5c15d9SAdrian Chadd */ 1078ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = vap->iv_bmissthreshold; 1079ba5c15d9SAdrian Chadd if (bs.bs_bmissthreshold > 10) 1080ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = 10; 1081ba5c15d9SAdrian Chadd else if (bs.bs_bmissthreshold <= 0) 1082ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = 1; 1083ba5c15d9SAdrian Chadd 1084ba5c15d9SAdrian Chadd /* 1085ba5c15d9SAdrian Chadd * Calculate sleep duration. The configuration is 1086ba5c15d9SAdrian Chadd * given in ms. We insure a multiple of the beacon 1087ba5c15d9SAdrian Chadd * period is used. Also, if the sleep duration is 1088ba5c15d9SAdrian Chadd * greater than the DTIM period then it makes senses 1089ba5c15d9SAdrian Chadd * to make it a multiple of that. 1090ba5c15d9SAdrian Chadd * 1091ba5c15d9SAdrian Chadd * XXX fixed at 100ms 1092ba5c15d9SAdrian Chadd */ 1093ba5c15d9SAdrian Chadd bs.bs_sleepduration = 1094ba5c15d9SAdrian Chadd roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); 1095ba5c15d9SAdrian Chadd if (bs.bs_sleepduration > bs.bs_dtimperiod) 1096ba5c15d9SAdrian Chadd bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); 1097ba5c15d9SAdrian Chadd 1098ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1099f5c30c4eSAdrian Chadd "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u " 1100f5c30c4eSAdrian Chadd "nextdtim %u bmiss %u sleep %u cfp:period %u " 1101f5c30c4eSAdrian Chadd "maxdur %u next %u timoffset %u\n" 1102ba5c15d9SAdrian Chadd , __func__ 1103f5c30c4eSAdrian Chadd , tsf 1104f5c30c4eSAdrian Chadd , tsftu 1105ba5c15d9SAdrian Chadd , bs.bs_intval 1106ba5c15d9SAdrian Chadd , bs.bs_nexttbtt 1107ba5c15d9SAdrian Chadd , bs.bs_dtimperiod 1108ba5c15d9SAdrian Chadd , bs.bs_nextdtim 1109ba5c15d9SAdrian Chadd , bs.bs_bmissthreshold 1110ba5c15d9SAdrian Chadd , bs.bs_sleepduration 1111ba5c15d9SAdrian Chadd , bs.bs_cfpperiod 1112ba5c15d9SAdrian Chadd , bs.bs_cfpmaxduration 1113ba5c15d9SAdrian Chadd , bs.bs_cfpnext 1114ba5c15d9SAdrian Chadd , bs.bs_timoffset 1115ba5c15d9SAdrian Chadd ); 1116ba5c15d9SAdrian Chadd ath_hal_intrset(ah, 0); 1117ba5c15d9SAdrian Chadd ath_hal_beacontimers(ah, &bs); 1118ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_BMISS; 1119ba5c15d9SAdrian Chadd ath_hal_intrset(ah, sc->sc_imask); 1120ba5c15d9SAdrian Chadd } else { 1121ba5c15d9SAdrian Chadd ath_hal_intrset(ah, 0); 1122ba5c15d9SAdrian Chadd if (nexttbtt == intval) 1123ba5c15d9SAdrian Chadd intval |= HAL_BEACON_RESET_TSF; 1124ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_IBSS) { 1125ba5c15d9SAdrian Chadd /* 1126ba5c15d9SAdrian Chadd * In IBSS mode enable the beacon timers but only 1127ba5c15d9SAdrian Chadd * enable SWBA interrupts if we need to manually 1128ba5c15d9SAdrian Chadd * prepare beacon frames. Otherwise we use a 1129ba5c15d9SAdrian Chadd * self-linked tx descriptor and let the hardware 1130ba5c15d9SAdrian Chadd * deal with things. 1131ba5c15d9SAdrian Chadd */ 1132ba5c15d9SAdrian Chadd intval |= HAL_BEACON_ENA; 1133ba5c15d9SAdrian Chadd if (!sc->sc_hasveol) 1134ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_SWBA; 1135ba5c15d9SAdrian Chadd if ((intval & HAL_BEACON_RESET_TSF) == 0) { 1136ba5c15d9SAdrian Chadd /* 1137ba5c15d9SAdrian Chadd * Pull nexttbtt forward to reflect 1138ba5c15d9SAdrian Chadd * the current TSF. 1139ba5c15d9SAdrian Chadd */ 1140ba5c15d9SAdrian Chadd tsf = ath_hal_gettsf64(ah); 1141ba5c15d9SAdrian Chadd tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 1142ba5c15d9SAdrian Chadd do { 1143ba5c15d9SAdrian Chadd nexttbtt += intval; 1144ba5c15d9SAdrian Chadd } while (nexttbtt < tsftu); 1145ba5c15d9SAdrian Chadd } 1146ba5c15d9SAdrian Chadd ath_beaconq_config(sc); 1147ba5c15d9SAdrian Chadd } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || 1148ba5c15d9SAdrian Chadd ic->ic_opmode == IEEE80211_M_MBSS) { 1149ba5c15d9SAdrian Chadd /* 1150ba5c15d9SAdrian Chadd * In AP/mesh mode we enable the beacon timers 1151ba5c15d9SAdrian Chadd * and SWBA interrupts to prepare beacon frames. 1152ba5c15d9SAdrian Chadd */ 1153ba5c15d9SAdrian Chadd intval |= HAL_BEACON_ENA; 1154ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ 1155ba5c15d9SAdrian Chadd ath_beaconq_config(sc); 1156ba5c15d9SAdrian Chadd } 1157e1252ce1SAdrian Chadd 1158e1252ce1SAdrian Chadd /* 1159e1252ce1SAdrian Chadd * Now dirty things because for now, the EDMA HAL has 1160e1252ce1SAdrian Chadd * nexttbtt and intval is TU/8. 1161e1252ce1SAdrian Chadd */ 1162e1252ce1SAdrian Chadd if (sc->sc_isedma) { 1163e1252ce1SAdrian Chadd nexttbtt_u8 = (nexttbtt << 3); 1164e1252ce1SAdrian Chadd intval_u8 = (intval << 3); 1165e1252ce1SAdrian Chadd if (intval & HAL_BEACON_ENA) 1166e1252ce1SAdrian Chadd intval_u8 |= HAL_BEACON_ENA; 1167e1252ce1SAdrian Chadd if (intval & HAL_BEACON_RESET_TSF) 1168e1252ce1SAdrian Chadd intval_u8 |= HAL_BEACON_RESET_TSF; 1169e1252ce1SAdrian Chadd ath_hal_beaconinit(ah, nexttbtt_u8, intval_u8); 1170e1252ce1SAdrian Chadd } else 1171ba5c15d9SAdrian Chadd ath_hal_beaconinit(ah, nexttbtt, intval); 1172ba5c15d9SAdrian Chadd sc->sc_bmisscount = 0; 1173ba5c15d9SAdrian Chadd ath_hal_intrset(ah, sc->sc_imask); 1174ba5c15d9SAdrian Chadd /* 1175ba5c15d9SAdrian Chadd * When using a self-linked beacon descriptor in 1176ba5c15d9SAdrian Chadd * ibss mode load it once here. 1177ba5c15d9SAdrian Chadd */ 1178ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) 1179ba5c15d9SAdrian Chadd ath_beacon_start_adhoc(sc, vap); 1180ba5c15d9SAdrian Chadd } 1181ba5c15d9SAdrian Chadd ieee80211_free_node(ni); 1182f5c30c4eSAdrian Chadd 1183f5c30c4eSAdrian Chadd ATH_LOCK(sc); 1184f5c30c4eSAdrian Chadd ath_power_restore_power_state(sc); 1185f5c30c4eSAdrian Chadd ATH_UNLOCK(sc); 1186ba5c15d9SAdrian Chadd #undef FUDGE 1187ba5c15d9SAdrian Chadd #undef TSF_TO_TU 1188ba5c15d9SAdrian Chadd } 1189