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) 1377a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic; 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 */ 202210ab3c2SAdrian Chadd m = ieee80211_beacon_alloc(ni); 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), 3507ff1939dSAdrian Chadd rix, 0, AH_TRUE); 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 { 3770cf00015SAdrian Chadd struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; 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 */ 4677a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic; 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 716210ab3c2SAdrian Chadd if (ieee80211_beacon_update(bf->bf_node, 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 } 7290cf00015SAdrian Chadd if ((vap->iv_bcn_off.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 */ 752062cf7d9SAdrian Chadd /* 753062cf7d9SAdrian Chadd * XXX can we even stop TX DMA here? Check what the 754062cf7d9SAdrian Chadd * reference driver does for cabq for beacons, given 755062cf7d9SAdrian Chadd * that stopping TX requires RX is paused. 756062cf7d9SAdrian 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 /* 764*1410ca56SAdrian Chadd * XXX TODO: tie into net80211 for quiet time IE update and program 765*1410ca56SAdrian Chadd * local AP timer if we require it. The process of updating the 766*1410ca56SAdrian Chadd * beacon will also update the IE with the relevant counters. 767*1410ca56SAdrian Chadd */ 768*1410ca56SAdrian Chadd 769*1410ca56SAdrian Chadd /* 770ba5c15d9SAdrian Chadd * Enable the CAB queue before the beacon queue to 771ba5c15d9SAdrian Chadd * insure cab frames are triggered by this beacon. 772ba5c15d9SAdrian Chadd */ 7730cf00015SAdrian Chadd if (vap->iv_bcn_off.bo_tim[4] & 1) { 774ba5c15d9SAdrian Chadd 775ba5c15d9SAdrian Chadd /* NB: only at DTIM */ 776b837332dSAdrian Chadd ATH_TXQ_LOCK(&avp->av_mcastq); 777ba5c15d9SAdrian Chadd if (nmcastq) { 778b6ef0f8aSAdrian Chadd struct ath_buf *bfm, *bfc_last; 779ba5c15d9SAdrian Chadd 780ba5c15d9SAdrian Chadd /* 781ba5c15d9SAdrian Chadd * Move frames from the s/w mcast q to the h/w cab q. 78274ea88c3SAdrian Chadd * 783b837332dSAdrian Chadd * XXX TODO: if we chain together multiple VAPs 784b837332dSAdrian Chadd * worth of CABQ traffic, should we keep the 785b837332dSAdrian Chadd * MORE data bit set on the last frame of each 786b837332dSAdrian Chadd * intermediary VAP (ie, only clear the MORE 787b837332dSAdrian Chadd * bit of the last frame on the last vap?) 788ba5c15d9SAdrian Chadd */ 789ba5c15d9SAdrian Chadd bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q); 790b837332dSAdrian Chadd ATH_TXQ_LOCK(cabq); 791b6ef0f8aSAdrian Chadd 792b6ef0f8aSAdrian Chadd /* 793b6ef0f8aSAdrian Chadd * If there's already a frame on the CABQ, we 794b6ef0f8aSAdrian Chadd * need to link to the end of the last frame. 795b6ef0f8aSAdrian Chadd * We can't use axq_link here because 796b6ef0f8aSAdrian Chadd * EDMA descriptors require some recalculation 797b6ef0f8aSAdrian Chadd * (checksum) to occur. 798b6ef0f8aSAdrian Chadd */ 799b6ef0f8aSAdrian Chadd bfc_last = ATH_TXQ_LAST(cabq, axq_q_s); 800b6ef0f8aSAdrian Chadd if (bfc_last != NULL) { 801b6ef0f8aSAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, 802b6ef0f8aSAdrian Chadd bfc_last->bf_lastds, 803b6ef0f8aSAdrian Chadd bfm->bf_daddr); 804b6ef0f8aSAdrian Chadd } 805ba5c15d9SAdrian Chadd ath_txqmove(cabq, &avp->av_mcastq); 806b837332dSAdrian Chadd ATH_TXQ_UNLOCK(cabq); 807b837332dSAdrian Chadd /* 808b837332dSAdrian Chadd * XXX not entirely accurate, in case a mcast 809b837332dSAdrian Chadd * queue frame arrived before we grabbed the TX 810b837332dSAdrian Chadd * lock. 811b837332dSAdrian Chadd */ 812ba5c15d9SAdrian Chadd sc->sc_stats.ast_cabq_xmit += nmcastq; 813ba5c15d9SAdrian Chadd } 814b837332dSAdrian Chadd ATH_TXQ_UNLOCK(&avp->av_mcastq); 815ba5c15d9SAdrian Chadd } 816ba5c15d9SAdrian Chadd return bf; 817ba5c15d9SAdrian Chadd } 818ba5c15d9SAdrian Chadd 819ba5c15d9SAdrian Chadd void 820ba5c15d9SAdrian Chadd ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) 821ba5c15d9SAdrian Chadd { 822ba5c15d9SAdrian Chadd struct ath_vap *avp = ATH_VAP(vap); 823ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 824ba5c15d9SAdrian Chadd struct ath_buf *bf; 825ba5c15d9SAdrian Chadd struct mbuf *m; 826ba5c15d9SAdrian Chadd int error; 827ba5c15d9SAdrian Chadd 828ba5c15d9SAdrian Chadd KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 829ba5c15d9SAdrian Chadd 830ba5c15d9SAdrian Chadd /* 831ba5c15d9SAdrian Chadd * Update dynamic beacon contents. If this returns 832ba5c15d9SAdrian Chadd * non-zero then we need to remap the memory because 833ba5c15d9SAdrian Chadd * the beacon frame changed size (probably because 834ba5c15d9SAdrian Chadd * of the TIM bitmap). 835ba5c15d9SAdrian Chadd */ 836ba5c15d9SAdrian Chadd bf = avp->av_bcbuf; 837ba5c15d9SAdrian Chadd m = bf->bf_m; 838210ab3c2SAdrian Chadd if (ieee80211_beacon_update(bf->bf_node, m, 0)) { 839ba5c15d9SAdrian Chadd /* XXX too conservative? */ 840ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 841ba5c15d9SAdrian Chadd error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 842ba5c15d9SAdrian Chadd bf->bf_segs, &bf->bf_nseg, 843ba5c15d9SAdrian Chadd BUS_DMA_NOWAIT); 844ba5c15d9SAdrian Chadd if (error != 0) { 845ba5c15d9SAdrian Chadd if_printf(vap->iv_ifp, 846ba5c15d9SAdrian Chadd "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 847ba5c15d9SAdrian Chadd __func__, error); 848ba5c15d9SAdrian Chadd return; 849ba5c15d9SAdrian Chadd } 850ba5c15d9SAdrian Chadd } 851ba5c15d9SAdrian Chadd ath_beacon_setup(sc, bf); 852ba5c15d9SAdrian Chadd bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 853ba5c15d9SAdrian Chadd 854ba5c15d9SAdrian Chadd /* NB: caller is known to have already stopped tx dma */ 855ba5c15d9SAdrian Chadd ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 856ba5c15d9SAdrian Chadd ath_hal_txstart(ah, sc->sc_bhalq); 857ba5c15d9SAdrian Chadd } 858ba5c15d9SAdrian Chadd 859ba5c15d9SAdrian Chadd /* 860ba5c15d9SAdrian Chadd * Reclaim beacon resources and return buffer to the pool. 861ba5c15d9SAdrian Chadd */ 862ba5c15d9SAdrian Chadd void 863ba5c15d9SAdrian Chadd ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) 864ba5c15d9SAdrian Chadd { 865ba5c15d9SAdrian Chadd 866ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_NODE, "%s: free bf=%p, bf_m=%p, bf_node=%p\n", 867ba5c15d9SAdrian Chadd __func__, bf, bf->bf_m, bf->bf_node); 868ba5c15d9SAdrian Chadd if (bf->bf_m != NULL) { 869ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 870ba5c15d9SAdrian Chadd m_freem(bf->bf_m); 871ba5c15d9SAdrian Chadd bf->bf_m = NULL; 872ba5c15d9SAdrian Chadd } 873ba5c15d9SAdrian Chadd if (bf->bf_node != NULL) { 874ba5c15d9SAdrian Chadd ieee80211_free_node(bf->bf_node); 875ba5c15d9SAdrian Chadd bf->bf_node = NULL; 876ba5c15d9SAdrian Chadd } 877ba5c15d9SAdrian Chadd TAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); 878ba5c15d9SAdrian Chadd } 879ba5c15d9SAdrian Chadd 880ba5c15d9SAdrian Chadd /* 881ba5c15d9SAdrian Chadd * Reclaim beacon resources. 882ba5c15d9SAdrian Chadd */ 883ba5c15d9SAdrian Chadd void 884ba5c15d9SAdrian Chadd ath_beacon_free(struct ath_softc *sc) 885ba5c15d9SAdrian Chadd { 886ba5c15d9SAdrian Chadd struct ath_buf *bf; 887ba5c15d9SAdrian Chadd 888ba5c15d9SAdrian Chadd TAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { 889ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_NODE, 890ba5c15d9SAdrian Chadd "%s: free bf=%p, bf_m=%p, bf_node=%p\n", 891ba5c15d9SAdrian Chadd __func__, bf, bf->bf_m, bf->bf_node); 892ba5c15d9SAdrian Chadd if (bf->bf_m != NULL) { 893ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 894ba5c15d9SAdrian Chadd m_freem(bf->bf_m); 895ba5c15d9SAdrian Chadd bf->bf_m = NULL; 896ba5c15d9SAdrian Chadd } 897ba5c15d9SAdrian Chadd if (bf->bf_node != NULL) { 898ba5c15d9SAdrian Chadd ieee80211_free_node(bf->bf_node); 899ba5c15d9SAdrian Chadd bf->bf_node = NULL; 900ba5c15d9SAdrian Chadd } 901ba5c15d9SAdrian Chadd } 902ba5c15d9SAdrian Chadd } 903ba5c15d9SAdrian Chadd 904ba5c15d9SAdrian Chadd /* 905ba5c15d9SAdrian Chadd * Configure the beacon and sleep timers. 906ba5c15d9SAdrian Chadd * 907ba5c15d9SAdrian Chadd * When operating as an AP this resets the TSF and sets 908ba5c15d9SAdrian Chadd * up the hardware to notify us when we need to issue beacons. 909ba5c15d9SAdrian Chadd * 910ba5c15d9SAdrian Chadd * When operating in station mode this sets up the beacon 911ba5c15d9SAdrian Chadd * timers according to the timestamp of the last received 912ba5c15d9SAdrian Chadd * beacon and the current TSF, configures PCF and DTIM 913ba5c15d9SAdrian Chadd * handling, programs the sleep registers so the hardware 914ba5c15d9SAdrian Chadd * will wakeup in time to receive beacons, and configures 915ba5c15d9SAdrian Chadd * the beacon miss handling so we'll receive a BMISS 916ba5c15d9SAdrian Chadd * interrupt when we stop seeing beacons from the AP 917ba5c15d9SAdrian Chadd * we've associated with. 918ba5c15d9SAdrian Chadd */ 919ba5c15d9SAdrian Chadd void 920ba5c15d9SAdrian Chadd ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 921ba5c15d9SAdrian Chadd { 922ba5c15d9SAdrian Chadd #define TSF_TO_TU(_h,_l) \ 923ba5c15d9SAdrian Chadd ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 924ba5c15d9SAdrian Chadd #define FUDGE 2 925ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 926*1410ca56SAdrian Chadd struct ath_vap *avp; 9277a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic; 928ba5c15d9SAdrian Chadd struct ieee80211_node *ni; 929ba5c15d9SAdrian Chadd u_int32_t nexttbtt, intval, tsftu; 930e1252ce1SAdrian Chadd u_int32_t nexttbtt_u8, intval_u8; 931f5c30c4eSAdrian Chadd u_int64_t tsf, tsf_beacon; 932ba5c15d9SAdrian Chadd 933ba5c15d9SAdrian Chadd if (vap == NULL) 934ba5c15d9SAdrian Chadd vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ 93561cd9692SAdrian Chadd /* 93661cd9692SAdrian Chadd * Just ensure that we aren't being called when the last 93761cd9692SAdrian Chadd * VAP is destroyed. 93861cd9692SAdrian Chadd */ 93961cd9692SAdrian Chadd if (vap == NULL) { 94061cd9692SAdrian Chadd device_printf(sc->sc_dev, "%s: called with no VAPs\n", 94161cd9692SAdrian Chadd __func__); 94261cd9692SAdrian Chadd return; 94361cd9692SAdrian Chadd } 94461cd9692SAdrian Chadd 945*1410ca56SAdrian Chadd /* Now that we have a vap, we can do this bit */ 946*1410ca56SAdrian Chadd avp = ATH_VAP(vap); 947*1410ca56SAdrian Chadd 948ba5c15d9SAdrian Chadd ni = ieee80211_ref_node(vap->iv_bss); 949ba5c15d9SAdrian Chadd 950f5c30c4eSAdrian Chadd ATH_LOCK(sc); 951f5c30c4eSAdrian Chadd ath_power_set_power_state(sc, HAL_PM_AWAKE); 952f5c30c4eSAdrian Chadd ATH_UNLOCK(sc); 953f5c30c4eSAdrian Chadd 954*1410ca56SAdrian Chadd /* Always clear the quiet IE timers; let the next update program them */ 955*1410ca56SAdrian Chadd ath_hal_set_quiet(ah, 0, 0, 0, HAL_QUIET_DISABLE); 956*1410ca56SAdrian Chadd memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); 957*1410ca56SAdrian Chadd 958ba5c15d9SAdrian Chadd /* extract tstamp from last beacon and convert to TU */ 95931021a2bSAndriy Voskoboinyk nexttbtt = TSF_TO_TU(le32dec(ni->ni_tstamp.data + 4), 96031021a2bSAndriy Voskoboinyk le32dec(ni->ni_tstamp.data)); 961f5c30c4eSAdrian Chadd 96231021a2bSAndriy Voskoboinyk tsf_beacon = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32; 96331021a2bSAndriy Voskoboinyk tsf_beacon |= le32dec(ni->ni_tstamp.data); 964f5c30c4eSAdrian Chadd 965ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_HOSTAP || 966ba5c15d9SAdrian Chadd ic->ic_opmode == IEEE80211_M_MBSS) { 967ba5c15d9SAdrian Chadd /* 968ba5c15d9SAdrian Chadd * For multi-bss ap/mesh support beacons are either staggered 969ba5c15d9SAdrian Chadd * evenly over N slots or burst together. For the former 970ba5c15d9SAdrian Chadd * arrange for the SWBA to be delivered for each slot. 971ba5c15d9SAdrian Chadd * Slots that are not occupied will generate nothing. 972ba5c15d9SAdrian Chadd */ 973ba5c15d9SAdrian Chadd /* NB: the beacon interval is kept internally in TU's */ 974ba5c15d9SAdrian Chadd intval = ni->ni_intval & HAL_BEACON_PERIOD; 975ba5c15d9SAdrian Chadd if (sc->sc_stagbeacons) 976ba5c15d9SAdrian Chadd intval /= ATH_BCBUF; 977ba5c15d9SAdrian Chadd } else { 978ba5c15d9SAdrian Chadd /* NB: the beacon interval is kept internally in TU's */ 979ba5c15d9SAdrian Chadd intval = ni->ni_intval & HAL_BEACON_PERIOD; 980ba5c15d9SAdrian Chadd } 9810ffc652eSAdrian Chadd 9820ffc652eSAdrian Chadd /* 983872f3a66SAdrian Chadd * Note: rounding up to the next intval can cause problems with 984872f3a66SAdrian Chadd * bad APs when we're in powersave mode. 9850ffc652eSAdrian Chadd * 9860ffc652eSAdrian Chadd * In STA mode with powersave enabled, beacons are only received 9870ffc652eSAdrian Chadd * whenever the beacon timer fires to wake up the hardware. 9880ffc652eSAdrian Chadd * Now, if this is rounded up to the next intval, it assumes 9890ffc652eSAdrian Chadd * that the AP has started transmitting beacons at TSF values that 9900ffc652eSAdrian Chadd * are multiples of intval, versus say being 25 TU off. 9910ffc652eSAdrian Chadd * 992872f3a66SAdrian Chadd * The specification (802.11-2012 10.1.3.2 - Beacon Generation in 993872f3a66SAdrian Chadd * Infrastructure Networks) requires APs be beaconing at a 994872f3a66SAdrian Chadd * mutiple of intval. So, if bintval=100, then we shouldn't 995872f3a66SAdrian Chadd * get beacons at intervals other than around multiples of 100. 9960ffc652eSAdrian Chadd */ 997ba5c15d9SAdrian Chadd if (nexttbtt == 0) /* e.g. for ap mode */ 998ba5c15d9SAdrian Chadd nexttbtt = intval; 999872f3a66SAdrian Chadd else 1000ba5c15d9SAdrian Chadd nexttbtt = roundup(nexttbtt, intval); 10010ffc652eSAdrian Chadd 1002ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", 1003ba5c15d9SAdrian Chadd __func__, nexttbtt, intval, ni->ni_intval); 1004ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { 1005ba5c15d9SAdrian Chadd HAL_BEACON_STATE bs; 1006ba5c15d9SAdrian Chadd int dtimperiod, dtimcount; 1007ba5c15d9SAdrian Chadd int cfpperiod, cfpcount; 1008ba5c15d9SAdrian Chadd 1009ba5c15d9SAdrian Chadd /* 1010ba5c15d9SAdrian Chadd * Setup dtim and cfp parameters according to 1011ba5c15d9SAdrian Chadd * last beacon we received (which may be none). 1012ba5c15d9SAdrian Chadd */ 1013ba5c15d9SAdrian Chadd dtimperiod = ni->ni_dtim_period; 1014ba5c15d9SAdrian Chadd if (dtimperiod <= 0) /* NB: 0 if not known */ 1015ba5c15d9SAdrian Chadd dtimperiod = 1; 1016ba5c15d9SAdrian Chadd dtimcount = ni->ni_dtim_count; 1017ba5c15d9SAdrian Chadd if (dtimcount >= dtimperiod) /* NB: sanity check */ 1018ba5c15d9SAdrian Chadd dtimcount = 0; /* XXX? */ 1019ba5c15d9SAdrian Chadd cfpperiod = 1; /* NB: no PCF support yet */ 1020ba5c15d9SAdrian Chadd cfpcount = 0; 1021ba5c15d9SAdrian Chadd /* 1022ba5c15d9SAdrian Chadd * Pull nexttbtt forward to reflect the current 1023ba5c15d9SAdrian Chadd * TSF and calculate dtim+cfp state for the result. 1024ba5c15d9SAdrian Chadd */ 1025ba5c15d9SAdrian Chadd tsf = ath_hal_gettsf64(ah); 1026ba5c15d9SAdrian Chadd tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 1027f5c30c4eSAdrian Chadd 1028f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1029f5c30c4eSAdrian Chadd "%s: beacon tsf=%llu, hw tsf=%llu, nexttbtt=%u, tsftu=%u\n", 1030f5c30c4eSAdrian Chadd __func__, 1031f5c30c4eSAdrian Chadd (unsigned long long) tsf_beacon, 1032f5c30c4eSAdrian Chadd (unsigned long long) tsf, 1033f5c30c4eSAdrian Chadd nexttbtt, 1034f5c30c4eSAdrian Chadd tsftu); 1035f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1036f5c30c4eSAdrian Chadd "%s: beacon tsf=%llu, hw tsf=%llu, tsf delta=%lld\n", 1037f5c30c4eSAdrian Chadd __func__, 1038f5c30c4eSAdrian Chadd (unsigned long long) tsf_beacon, 1039f5c30c4eSAdrian Chadd (unsigned long long) tsf, 1040f5c30c4eSAdrian Chadd (long long) tsf - 1041f5c30c4eSAdrian Chadd (long long) tsf_beacon); 1042f5c30c4eSAdrian Chadd 1043f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1044f5c30c4eSAdrian Chadd "%s: nexttbtt=%llu, beacon tsf delta=%lld\n", 1045f5c30c4eSAdrian Chadd __func__, 1046f5c30c4eSAdrian Chadd (unsigned long long) nexttbtt, 1047f5c30c4eSAdrian Chadd (long long) ((long long) nexttbtt * 1024LL) - (long long) tsf_beacon); 1048f5c30c4eSAdrian Chadd 1049f5c30c4eSAdrian Chadd /* XXX cfpcount? */ 1050f5c30c4eSAdrian Chadd 1051f5c30c4eSAdrian Chadd if (nexttbtt > tsftu) { 1052f5c30c4eSAdrian Chadd uint32_t countdiff, oldtbtt, remainder; 1053f5c30c4eSAdrian Chadd 1054f5c30c4eSAdrian Chadd oldtbtt = nexttbtt; 1055f5c30c4eSAdrian Chadd remainder = (nexttbtt - tsftu) % intval; 1056f5c30c4eSAdrian Chadd nexttbtt = tsftu + remainder; 1057f5c30c4eSAdrian Chadd 1058f5c30c4eSAdrian Chadd countdiff = (oldtbtt - nexttbtt) / intval % dtimperiod; 1059f5c30c4eSAdrian Chadd if (dtimcount > countdiff) { 1060f5c30c4eSAdrian Chadd dtimcount -= countdiff; 1061f5c30c4eSAdrian Chadd } else { 1062f5c30c4eSAdrian Chadd dtimcount += dtimperiod - countdiff; 1063ba5c15d9SAdrian Chadd } 1064f5c30c4eSAdrian Chadd } else { //nexttbtt <= tsftu 1065f5c30c4eSAdrian Chadd uint32_t countdiff, oldtbtt, remainder; 1066f5c30c4eSAdrian Chadd 1067f5c30c4eSAdrian Chadd oldtbtt = nexttbtt; 1068f5c30c4eSAdrian Chadd remainder = (tsftu - nexttbtt) % intval; 1069f5c30c4eSAdrian Chadd nexttbtt = tsftu - remainder + intval; 1070f5c30c4eSAdrian Chadd countdiff = (nexttbtt - oldtbtt) / intval % dtimperiod; 1071f5c30c4eSAdrian Chadd if (dtimcount > countdiff) { 1072f5c30c4eSAdrian Chadd dtimcount -= countdiff; 1073f5c30c4eSAdrian Chadd } else { 1074f5c30c4eSAdrian Chadd dtimcount += dtimperiod - countdiff; 1075f5c30c4eSAdrian Chadd } 1076f5c30c4eSAdrian Chadd } 1077f5c30c4eSAdrian Chadd 1078f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1079f5c30c4eSAdrian Chadd "%s: adj nexttbtt=%llu, rx tsf delta=%lld\n", 1080f5c30c4eSAdrian Chadd __func__, 1081f5c30c4eSAdrian Chadd (unsigned long long) nexttbtt, 1082f5c30c4eSAdrian Chadd (long long) ((long long)nexttbtt * 1024LL) - (long long)tsf); 1083f5c30c4eSAdrian Chadd 1084ba5c15d9SAdrian Chadd memset(&bs, 0, sizeof(bs)); 1085ba5c15d9SAdrian Chadd bs.bs_intval = intval; 1086ba5c15d9SAdrian Chadd bs.bs_nexttbtt = nexttbtt; 1087ba5c15d9SAdrian Chadd bs.bs_dtimperiod = dtimperiod*intval; 1088ba5c15d9SAdrian Chadd bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 1089ba5c15d9SAdrian Chadd bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 1090ba5c15d9SAdrian Chadd bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 1091ba5c15d9SAdrian Chadd bs.bs_cfpmaxduration = 0; 1092ba5c15d9SAdrian Chadd #if 0 1093ba5c15d9SAdrian Chadd /* 1094ba5c15d9SAdrian Chadd * The 802.11 layer records the offset to the DTIM 1095ba5c15d9SAdrian Chadd * bitmap while receiving beacons; use it here to 1096ba5c15d9SAdrian Chadd * enable h/w detection of our AID being marked in 1097ba5c15d9SAdrian Chadd * the bitmap vector (to indicate frames for us are 1098ba5c15d9SAdrian Chadd * pending at the AP). 1099ba5c15d9SAdrian Chadd * XXX do DTIM handling in s/w to WAR old h/w bugs 1100ba5c15d9SAdrian Chadd * XXX enable based on h/w rev for newer chips 1101ba5c15d9SAdrian Chadd */ 1102ba5c15d9SAdrian Chadd bs.bs_timoffset = ni->ni_timoff; 1103ba5c15d9SAdrian Chadd #endif 1104ba5c15d9SAdrian Chadd /* 1105ba5c15d9SAdrian Chadd * Calculate the number of consecutive beacons to miss 1106ba5c15d9SAdrian Chadd * before taking a BMISS interrupt. 1107ba5c15d9SAdrian Chadd * Note that we clamp the result to at most 10 beacons. 1108ba5c15d9SAdrian Chadd */ 1109ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = vap->iv_bmissthreshold; 1110ba5c15d9SAdrian Chadd if (bs.bs_bmissthreshold > 10) 1111ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = 10; 1112ba5c15d9SAdrian Chadd else if (bs.bs_bmissthreshold <= 0) 1113ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = 1; 1114ba5c15d9SAdrian Chadd 1115ba5c15d9SAdrian Chadd /* 1116ba5c15d9SAdrian Chadd * Calculate sleep duration. The configuration is 1117ba5c15d9SAdrian Chadd * given in ms. We insure a multiple of the beacon 1118ba5c15d9SAdrian Chadd * period is used. Also, if the sleep duration is 1119ba5c15d9SAdrian Chadd * greater than the DTIM period then it makes senses 1120ba5c15d9SAdrian Chadd * to make it a multiple of that. 1121ba5c15d9SAdrian Chadd * 1122ba5c15d9SAdrian Chadd * XXX fixed at 100ms 1123ba5c15d9SAdrian Chadd */ 1124ba5c15d9SAdrian Chadd bs.bs_sleepduration = 1125ba5c15d9SAdrian Chadd roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); 1126ba5c15d9SAdrian Chadd if (bs.bs_sleepduration > bs.bs_dtimperiod) 1127ba5c15d9SAdrian Chadd bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); 1128ba5c15d9SAdrian Chadd 1129ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1130f5c30c4eSAdrian Chadd "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u " 1131f5c30c4eSAdrian Chadd "nextdtim %u bmiss %u sleep %u cfp:period %u " 1132f5c30c4eSAdrian Chadd "maxdur %u next %u timoffset %u\n" 1133ba5c15d9SAdrian Chadd , __func__ 1134f5c30c4eSAdrian Chadd , tsf 1135f5c30c4eSAdrian Chadd , tsftu 1136ba5c15d9SAdrian Chadd , bs.bs_intval 1137ba5c15d9SAdrian Chadd , bs.bs_nexttbtt 1138ba5c15d9SAdrian Chadd , bs.bs_dtimperiod 1139ba5c15d9SAdrian Chadd , bs.bs_nextdtim 1140ba5c15d9SAdrian Chadd , bs.bs_bmissthreshold 1141ba5c15d9SAdrian Chadd , bs.bs_sleepduration 1142ba5c15d9SAdrian Chadd , bs.bs_cfpperiod 1143ba5c15d9SAdrian Chadd , bs.bs_cfpmaxduration 1144ba5c15d9SAdrian Chadd , bs.bs_cfpnext 1145ba5c15d9SAdrian Chadd , bs.bs_timoffset 1146ba5c15d9SAdrian Chadd ); 1147ba5c15d9SAdrian Chadd ath_hal_intrset(ah, 0); 1148ba5c15d9SAdrian Chadd ath_hal_beacontimers(ah, &bs); 1149ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_BMISS; 1150ba5c15d9SAdrian Chadd ath_hal_intrset(ah, sc->sc_imask); 1151ba5c15d9SAdrian Chadd } else { 1152ba5c15d9SAdrian Chadd ath_hal_intrset(ah, 0); 1153ba5c15d9SAdrian Chadd if (nexttbtt == intval) 1154ba5c15d9SAdrian Chadd intval |= HAL_BEACON_RESET_TSF; 1155ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_IBSS) { 1156ba5c15d9SAdrian Chadd /* 1157ba5c15d9SAdrian Chadd * In IBSS mode enable the beacon timers but only 1158ba5c15d9SAdrian Chadd * enable SWBA interrupts if we need to manually 1159ba5c15d9SAdrian Chadd * prepare beacon frames. Otherwise we use a 1160ba5c15d9SAdrian Chadd * self-linked tx descriptor and let the hardware 1161ba5c15d9SAdrian Chadd * deal with things. 1162ba5c15d9SAdrian Chadd */ 1163ba5c15d9SAdrian Chadd intval |= HAL_BEACON_ENA; 1164ba5c15d9SAdrian Chadd if (!sc->sc_hasveol) 1165ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_SWBA; 1166ba5c15d9SAdrian Chadd if ((intval & HAL_BEACON_RESET_TSF) == 0) { 1167ba5c15d9SAdrian Chadd /* 1168ba5c15d9SAdrian Chadd * Pull nexttbtt forward to reflect 1169ba5c15d9SAdrian Chadd * the current TSF. 1170ba5c15d9SAdrian Chadd */ 1171ba5c15d9SAdrian Chadd tsf = ath_hal_gettsf64(ah); 1172ba5c15d9SAdrian Chadd tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 1173ba5c15d9SAdrian Chadd do { 1174ba5c15d9SAdrian Chadd nexttbtt += intval; 1175ba5c15d9SAdrian Chadd } while (nexttbtt < tsftu); 1176ba5c15d9SAdrian Chadd } 1177ba5c15d9SAdrian Chadd ath_beaconq_config(sc); 1178ba5c15d9SAdrian Chadd } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || 1179ba5c15d9SAdrian Chadd ic->ic_opmode == IEEE80211_M_MBSS) { 1180ba5c15d9SAdrian Chadd /* 1181ba5c15d9SAdrian Chadd * In AP/mesh mode we enable the beacon timers 1182ba5c15d9SAdrian Chadd * and SWBA interrupts to prepare beacon frames. 1183ba5c15d9SAdrian Chadd */ 1184ba5c15d9SAdrian Chadd intval |= HAL_BEACON_ENA; 1185ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ 1186ba5c15d9SAdrian Chadd ath_beaconq_config(sc); 1187ba5c15d9SAdrian Chadd } 1188e1252ce1SAdrian Chadd 1189e1252ce1SAdrian Chadd /* 1190e1252ce1SAdrian Chadd * Now dirty things because for now, the EDMA HAL has 1191e1252ce1SAdrian Chadd * nexttbtt and intval is TU/8. 1192e1252ce1SAdrian Chadd */ 1193e1252ce1SAdrian Chadd if (sc->sc_isedma) { 1194e1252ce1SAdrian Chadd nexttbtt_u8 = (nexttbtt << 3); 1195e1252ce1SAdrian Chadd intval_u8 = (intval << 3); 1196e1252ce1SAdrian Chadd if (intval & HAL_BEACON_ENA) 1197e1252ce1SAdrian Chadd intval_u8 |= HAL_BEACON_ENA; 1198e1252ce1SAdrian Chadd if (intval & HAL_BEACON_RESET_TSF) 1199e1252ce1SAdrian Chadd intval_u8 |= HAL_BEACON_RESET_TSF; 1200e1252ce1SAdrian Chadd ath_hal_beaconinit(ah, nexttbtt_u8, intval_u8); 1201e1252ce1SAdrian Chadd } else 1202ba5c15d9SAdrian Chadd ath_hal_beaconinit(ah, nexttbtt, intval); 1203ba5c15d9SAdrian Chadd sc->sc_bmisscount = 0; 1204ba5c15d9SAdrian Chadd ath_hal_intrset(ah, sc->sc_imask); 1205ba5c15d9SAdrian Chadd /* 1206ba5c15d9SAdrian Chadd * When using a self-linked beacon descriptor in 1207ba5c15d9SAdrian Chadd * ibss mode load it once here. 1208ba5c15d9SAdrian Chadd */ 1209ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) 1210ba5c15d9SAdrian Chadd ath_beacon_start_adhoc(sc, vap); 1211ba5c15d9SAdrian Chadd } 1212ba5c15d9SAdrian Chadd ieee80211_free_node(ni); 1213f5c30c4eSAdrian Chadd 1214f5c30c4eSAdrian Chadd ATH_LOCK(sc); 1215f5c30c4eSAdrian Chadd ath_power_restore_power_state(sc); 1216f5c30c4eSAdrian Chadd ATH_UNLOCK(sc); 1217ba5c15d9SAdrian Chadd #undef FUDGE 1218ba5c15d9SAdrian Chadd #undef TSF_TO_TU 1219ba5c15d9SAdrian Chadd } 1220