1ba5c15d9SAdrian Chadd /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4ba5c15d9SAdrian Chadd * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 5ba5c15d9SAdrian Chadd * All rights reserved. 6ba5c15d9SAdrian Chadd * 7ba5c15d9SAdrian Chadd * Redistribution and use in source and binary forms, with or without 8ba5c15d9SAdrian Chadd * modification, are permitted provided that the following conditions 9ba5c15d9SAdrian Chadd * are met: 10ba5c15d9SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 11ba5c15d9SAdrian Chadd * notice, this list of conditions and the following disclaimer, 12ba5c15d9SAdrian Chadd * without modification. 13ba5c15d9SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 14ba5c15d9SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 15ba5c15d9SAdrian Chadd * redistribution must be conditioned upon including a substantially 16ba5c15d9SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 17ba5c15d9SAdrian Chadd * 18ba5c15d9SAdrian Chadd * NO WARRANTY 19ba5c15d9SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20ba5c15d9SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21ba5c15d9SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 22ba5c15d9SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 23ba5c15d9SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 24ba5c15d9SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25ba5c15d9SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26ba5c15d9SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 27ba5c15d9SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28ba5c15d9SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29ba5c15d9SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 30ba5c15d9SAdrian Chadd */ 31ba5c15d9SAdrian Chadd 32ba5c15d9SAdrian Chadd #include <sys/cdefs.h> 33ba5c15d9SAdrian Chadd __FBSDID("$FreeBSD$"); 34ba5c15d9SAdrian Chadd 35ba5c15d9SAdrian Chadd /* 36ba5c15d9SAdrian Chadd * Driver for the Atheros Wireless LAN controller. 37ba5c15d9SAdrian Chadd * 38ba5c15d9SAdrian Chadd * This software is derived from work of Atsushi Onoe; his contribution 39ba5c15d9SAdrian Chadd * is greatly appreciated. 40ba5c15d9SAdrian Chadd */ 41ba5c15d9SAdrian Chadd 42ba5c15d9SAdrian Chadd #include "opt_inet.h" 43ba5c15d9SAdrian Chadd #include "opt_ath.h" 44ba5c15d9SAdrian Chadd /* 45ba5c15d9SAdrian Chadd * This is needed for register operations which are performed 46ba5c15d9SAdrian Chadd * by the driver - eg, calls to ath_hal_gettsf32(). 47ba5c15d9SAdrian Chadd * 48ba5c15d9SAdrian Chadd * It's also required for any AH_DEBUG checks in here, eg the 49ba5c15d9SAdrian Chadd * module dependencies. 50ba5c15d9SAdrian Chadd */ 51ba5c15d9SAdrian Chadd #include "opt_ah.h" 52ba5c15d9SAdrian Chadd #include "opt_wlan.h" 53ba5c15d9SAdrian Chadd 54ba5c15d9SAdrian Chadd #include <sys/param.h> 55ba5c15d9SAdrian Chadd #include <sys/systm.h> 56ba5c15d9SAdrian Chadd #include <sys/sysctl.h> 57ba5c15d9SAdrian Chadd #include <sys/mbuf.h> 58ba5c15d9SAdrian Chadd #include <sys/malloc.h> 59ba5c15d9SAdrian Chadd #include <sys/lock.h> 60ba5c15d9SAdrian Chadd #include <sys/mutex.h> 61ba5c15d9SAdrian Chadd #include <sys/kernel.h> 62ba5c15d9SAdrian Chadd #include <sys/socket.h> 63ba5c15d9SAdrian Chadd #include <sys/sockio.h> 64ba5c15d9SAdrian Chadd #include <sys/errno.h> 65ba5c15d9SAdrian Chadd #include <sys/callout.h> 66ba5c15d9SAdrian Chadd #include <sys/bus.h> 67ba5c15d9SAdrian Chadd #include <sys/endian.h> 68ba5c15d9SAdrian Chadd #include <sys/kthread.h> 69ba5c15d9SAdrian Chadd #include <sys/taskqueue.h> 70ba5c15d9SAdrian Chadd #include <sys/priv.h> 71ba5c15d9SAdrian Chadd #include <sys/module.h> 72ba5c15d9SAdrian Chadd #include <sys/ktr.h> 73ba5c15d9SAdrian Chadd #include <sys/smp.h> /* for mp_ncpus */ 74ba5c15d9SAdrian Chadd 75ba5c15d9SAdrian Chadd #include <machine/bus.h> 76ba5c15d9SAdrian Chadd 77ba5c15d9SAdrian Chadd #include <net/if.h> 7876039bc8SGleb Smirnoff #include <net/if_var.h> 79ba5c15d9SAdrian Chadd #include <net/if_dl.h> 80ba5c15d9SAdrian Chadd #include <net/if_media.h> 81ba5c15d9SAdrian Chadd #include <net/if_types.h> 82ba5c15d9SAdrian Chadd #include <net/if_arp.h> 83ba5c15d9SAdrian Chadd #include <net/ethernet.h> 84ba5c15d9SAdrian Chadd #include <net/if_llc.h> 85ba5c15d9SAdrian Chadd 86ba5c15d9SAdrian Chadd #include <net80211/ieee80211_var.h> 87ba5c15d9SAdrian Chadd #include <net80211/ieee80211_regdomain.h> 88ba5c15d9SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG 89ba5c15d9SAdrian Chadd #include <net80211/ieee80211_superg.h> 90ba5c15d9SAdrian Chadd #endif 91ba5c15d9SAdrian Chadd 92ba5c15d9SAdrian Chadd #include <net/bpf.h> 93ba5c15d9SAdrian Chadd 94ba5c15d9SAdrian Chadd #ifdef INET 95ba5c15d9SAdrian Chadd #include <netinet/in.h> 96ba5c15d9SAdrian Chadd #include <netinet/if_ether.h> 97ba5c15d9SAdrian Chadd #endif 98ba5c15d9SAdrian Chadd 99ba5c15d9SAdrian Chadd #include <dev/ath/if_athvar.h> 100ba5c15d9SAdrian Chadd 101ba5c15d9SAdrian Chadd #include <dev/ath/if_ath_debug.h> 102ba5c15d9SAdrian Chadd #include <dev/ath/if_ath_misc.h> 103ba5c15d9SAdrian Chadd #include <dev/ath/if_ath_tx.h> 104ba5c15d9SAdrian Chadd #include <dev/ath/if_ath_beacon.h> 105ba5c15d9SAdrian Chadd 106ba5c15d9SAdrian Chadd #ifdef ATH_TX99_DIAG 107ba5c15d9SAdrian Chadd #include <dev/ath/ath_tx99/ath_tx99.h> 108ba5c15d9SAdrian Chadd #endif 109ba5c15d9SAdrian Chadd 110ba5c15d9SAdrian Chadd /* 111ba5c15d9SAdrian Chadd * Setup a h/w transmit queue for beacons. 112ba5c15d9SAdrian Chadd */ 113ba5c15d9SAdrian Chadd int 114e1252ce1SAdrian Chadd ath_beaconq_setup(struct ath_softc *sc) 115ba5c15d9SAdrian Chadd { 116e1252ce1SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 117ba5c15d9SAdrian Chadd HAL_TXQ_INFO qi; 118ba5c15d9SAdrian Chadd 119ba5c15d9SAdrian Chadd memset(&qi, 0, sizeof(qi)); 120ba5c15d9SAdrian Chadd qi.tqi_aifs = HAL_TXQ_USEDEFAULT; 121ba5c15d9SAdrian Chadd qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; 122ba5c15d9SAdrian Chadd qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; 123ba5c15d9SAdrian Chadd /* NB: for dynamic turbo, don't enable any other interrupts */ 124ba5c15d9SAdrian Chadd qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; 125e1252ce1SAdrian Chadd if (sc->sc_isedma) 126e1252ce1SAdrian Chadd qi.tqi_qflags |= HAL_TXQ_TXOKINT_ENABLE | 127e1252ce1SAdrian Chadd HAL_TXQ_TXERRINT_ENABLE; 128e1252ce1SAdrian Chadd 129ba5c15d9SAdrian Chadd return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); 130ba5c15d9SAdrian Chadd } 131ba5c15d9SAdrian Chadd 132ba5c15d9SAdrian Chadd /* 133ba5c15d9SAdrian Chadd * Setup the transmit queue parameters for the beacon queue. 134ba5c15d9SAdrian Chadd */ 135ba5c15d9SAdrian Chadd int 136ba5c15d9SAdrian Chadd ath_beaconq_config(struct ath_softc *sc) 137ba5c15d9SAdrian Chadd { 138ba5c15d9SAdrian Chadd #define ATH_EXPONENT_TO_VALUE(v) ((1<<(v))-1) 1397a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic; 140ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 141ba5c15d9SAdrian Chadd HAL_TXQ_INFO qi; 142ba5c15d9SAdrian Chadd 143ba5c15d9SAdrian Chadd ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi); 144ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_HOSTAP || 145ba5c15d9SAdrian Chadd ic->ic_opmode == IEEE80211_M_MBSS) { 146ba5c15d9SAdrian Chadd /* 147ba5c15d9SAdrian Chadd * Always burst out beacon and CAB traffic. 148ba5c15d9SAdrian Chadd */ 149ba5c15d9SAdrian Chadd qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; 150ba5c15d9SAdrian Chadd qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT; 151ba5c15d9SAdrian Chadd qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT; 152ba5c15d9SAdrian Chadd } else { 1539fbe631aSAdrian Chadd struct chanAccParams chp; 1549fbe631aSAdrian Chadd struct wmeParams *wmep; 1559fbe631aSAdrian Chadd 1569fbe631aSAdrian Chadd ieee80211_wme_ic_getparams(ic, &chp); 1579fbe631aSAdrian Chadd wmep = &chp.cap_wmeParams[WME_AC_BE]; 1589fbe631aSAdrian Chadd 159ba5c15d9SAdrian Chadd /* 160ba5c15d9SAdrian Chadd * Adhoc mode; important thing is to use 2x cwmin. 161ba5c15d9SAdrian Chadd */ 162ba5c15d9SAdrian Chadd qi.tqi_aifs = wmep->wmep_aifsn; 163ba5c15d9SAdrian Chadd qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); 164ba5c15d9SAdrian Chadd qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); 165ba5c15d9SAdrian Chadd } 166ba5c15d9SAdrian Chadd 167ba5c15d9SAdrian Chadd if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) { 168ba5c15d9SAdrian Chadd device_printf(sc->sc_dev, "unable to update parameters for " 169ba5c15d9SAdrian Chadd "beacon hardware queue!\n"); 170ba5c15d9SAdrian Chadd return 0; 171ba5c15d9SAdrian Chadd } else { 172ba5c15d9SAdrian Chadd ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ 173ba5c15d9SAdrian Chadd return 1; 174ba5c15d9SAdrian Chadd } 175ba5c15d9SAdrian Chadd #undef ATH_EXPONENT_TO_VALUE 176ba5c15d9SAdrian Chadd } 177ba5c15d9SAdrian Chadd 178ba5c15d9SAdrian Chadd /* 179ba5c15d9SAdrian Chadd * Allocate and setup an initial beacon frame. 180ba5c15d9SAdrian Chadd */ 181ba5c15d9SAdrian Chadd int 182ba5c15d9SAdrian Chadd ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) 183ba5c15d9SAdrian Chadd { 184ba5c15d9SAdrian Chadd struct ieee80211vap *vap = ni->ni_vap; 185ba5c15d9SAdrian Chadd struct ath_vap *avp = ATH_VAP(vap); 186ba5c15d9SAdrian Chadd struct ath_buf *bf; 187ba5c15d9SAdrian Chadd struct mbuf *m; 188ba5c15d9SAdrian Chadd int error; 189ba5c15d9SAdrian Chadd 190ba5c15d9SAdrian Chadd bf = avp->av_bcbuf; 191ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_NODE, "%s: bf_m=%p, bf_node=%p\n", 192ba5c15d9SAdrian Chadd __func__, bf->bf_m, bf->bf_node); 193ba5c15d9SAdrian Chadd if (bf->bf_m != NULL) { 194ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 195ba5c15d9SAdrian Chadd m_freem(bf->bf_m); 196ba5c15d9SAdrian Chadd bf->bf_m = NULL; 197ba5c15d9SAdrian Chadd } 198ba5c15d9SAdrian Chadd if (bf->bf_node != NULL) { 199ba5c15d9SAdrian Chadd ieee80211_free_node(bf->bf_node); 200ba5c15d9SAdrian Chadd bf->bf_node = NULL; 201ba5c15d9SAdrian Chadd } 202ba5c15d9SAdrian Chadd 203ba5c15d9SAdrian Chadd /* 204ba5c15d9SAdrian Chadd * NB: the beacon data buffer must be 32-bit aligned; 205ba5c15d9SAdrian Chadd * we assume the mbuf routines will return us something 206ba5c15d9SAdrian Chadd * with this alignment (perhaps should assert). 207ba5c15d9SAdrian Chadd */ 208210ab3c2SAdrian Chadd m = ieee80211_beacon_alloc(ni); 209ba5c15d9SAdrian Chadd if (m == NULL) { 210ba5c15d9SAdrian Chadd device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__); 211ba5c15d9SAdrian Chadd sc->sc_stats.ast_be_nombuf++; 212ba5c15d9SAdrian Chadd return ENOMEM; 213ba5c15d9SAdrian Chadd } 214ba5c15d9SAdrian Chadd error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 215ba5c15d9SAdrian Chadd bf->bf_segs, &bf->bf_nseg, 216ba5c15d9SAdrian Chadd BUS_DMA_NOWAIT); 217ba5c15d9SAdrian Chadd if (error != 0) { 218ba5c15d9SAdrian Chadd device_printf(sc->sc_dev, 219ba5c15d9SAdrian Chadd "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n", 220ba5c15d9SAdrian Chadd __func__, error); 221ba5c15d9SAdrian Chadd m_freem(m); 222ba5c15d9SAdrian Chadd return error; 223ba5c15d9SAdrian Chadd } 224ba5c15d9SAdrian Chadd 225ba5c15d9SAdrian Chadd /* 226ba5c15d9SAdrian Chadd * Calculate a TSF adjustment factor required for staggered 227ba5c15d9SAdrian Chadd * beacons. Note that we assume the format of the beacon 228ba5c15d9SAdrian Chadd * frame leaves the tstamp field immediately following the 229ba5c15d9SAdrian Chadd * header. 230ba5c15d9SAdrian Chadd */ 231ba5c15d9SAdrian Chadd if (sc->sc_stagbeacons && avp->av_bslot > 0) { 232ba5c15d9SAdrian Chadd uint64_t tsfadjust; 233ba5c15d9SAdrian Chadd struct ieee80211_frame *wh; 234ba5c15d9SAdrian Chadd 235ba5c15d9SAdrian Chadd /* 236ba5c15d9SAdrian Chadd * The beacon interval is in TU's; the TSF is in usecs. 237ba5c15d9SAdrian Chadd * We figure out how many TU's to add to align the timestamp 238ba5c15d9SAdrian Chadd * then convert to TSF units and handle byte swapping before 239ba5c15d9SAdrian Chadd * inserting it in the frame. The hardware will then add this 240ba5c15d9SAdrian Chadd * each time a beacon frame is sent. Note that we align vap's 241ba5c15d9SAdrian Chadd * 1..N and leave vap 0 untouched. This means vap 0 has a 242ba5c15d9SAdrian Chadd * timestamp in one beacon interval while the others get a 243ba5c15d9SAdrian Chadd * timstamp aligned to the next interval. 244ba5c15d9SAdrian Chadd */ 245ba5c15d9SAdrian Chadd tsfadjust = ni->ni_intval * 246ba5c15d9SAdrian Chadd (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF; 247ba5c15d9SAdrian Chadd tsfadjust = htole64(tsfadjust << 10); /* TU -> TSF */ 248ba5c15d9SAdrian Chadd 249ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 250ba5c15d9SAdrian Chadd "%s: %s beacons bslot %d intval %u tsfadjust %llu\n", 251ba5c15d9SAdrian Chadd __func__, sc->sc_stagbeacons ? "stagger" : "burst", 252ba5c15d9SAdrian Chadd avp->av_bslot, ni->ni_intval, 253ba5c15d9SAdrian Chadd (long long unsigned) le64toh(tsfadjust)); 254ba5c15d9SAdrian Chadd 255ba5c15d9SAdrian Chadd wh = mtod(m, struct ieee80211_frame *); 256ba5c15d9SAdrian Chadd memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust)); 257ba5c15d9SAdrian Chadd } 258ba5c15d9SAdrian Chadd bf->bf_m = m; 259ba5c15d9SAdrian Chadd bf->bf_node = ieee80211_ref_node(ni); 260ba5c15d9SAdrian Chadd 261ba5c15d9SAdrian Chadd return 0; 262ba5c15d9SAdrian Chadd } 263ba5c15d9SAdrian Chadd 264ba5c15d9SAdrian Chadd /* 265ba5c15d9SAdrian Chadd * Setup the beacon frame for transmit. 266ba5c15d9SAdrian Chadd */ 267ba5c15d9SAdrian Chadd static void 268ba5c15d9SAdrian Chadd ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf) 269ba5c15d9SAdrian Chadd { 270ba5c15d9SAdrian Chadd #define USE_SHPREAMBLE(_ic) \ 271ba5c15d9SAdrian Chadd (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ 272ba5c15d9SAdrian Chadd == IEEE80211_F_SHPREAMBLE) 273ba5c15d9SAdrian Chadd struct ieee80211_node *ni = bf->bf_node; 274ba5c15d9SAdrian Chadd struct ieee80211com *ic = ni->ni_ic; 275ba5c15d9SAdrian Chadd struct mbuf *m = bf->bf_m; 276ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 277ba5c15d9SAdrian Chadd struct ath_desc *ds; 278ba5c15d9SAdrian Chadd int flags, antenna; 279ba5c15d9SAdrian Chadd const HAL_RATE_TABLE *rt; 280ba5c15d9SAdrian Chadd u_int8_t rix, rate; 28146634305SAdrian Chadd HAL_DMA_ADDR bufAddrList[4]; 28246634305SAdrian Chadd uint32_t segLenList[4]; 283e1252ce1SAdrian Chadd HAL_11N_RATE_SERIES rc[4]; 284ba5c15d9SAdrian Chadd 285ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n", 286ba5c15d9SAdrian Chadd __func__, m, m->m_len); 287ba5c15d9SAdrian Chadd 288ba5c15d9SAdrian Chadd /* setup descriptors */ 289ba5c15d9SAdrian Chadd ds = bf->bf_desc; 290ba5c15d9SAdrian Chadd bf->bf_last = bf; 291ba5c15d9SAdrian Chadd bf->bf_lastds = ds; 292ba5c15d9SAdrian Chadd 293ba5c15d9SAdrian Chadd flags = HAL_TXDESC_NOACK; 294ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) { 295bb069955SAdrian Chadd /* self-linked descriptor */ 296bb069955SAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, ds, bf->bf_daddr); 297ba5c15d9SAdrian Chadd flags |= HAL_TXDESC_VEOL; 298ba5c15d9SAdrian Chadd /* 299ba5c15d9SAdrian Chadd * Let hardware handle antenna switching. 300ba5c15d9SAdrian Chadd */ 301ba5c15d9SAdrian Chadd antenna = sc->sc_txantenna; 302ba5c15d9SAdrian Chadd } else { 303bb069955SAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, ds, 0); 304ba5c15d9SAdrian Chadd /* 305ba5c15d9SAdrian Chadd * Switch antenna every 4 beacons. 306ba5c15d9SAdrian Chadd * XXX assumes two antenna 307ba5c15d9SAdrian Chadd */ 308ba5c15d9SAdrian Chadd if (sc->sc_txantenna != 0) 309ba5c15d9SAdrian Chadd antenna = sc->sc_txantenna; 310ba5c15d9SAdrian Chadd else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0) 311ba5c15d9SAdrian Chadd antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1); 312ba5c15d9SAdrian Chadd else 313ba5c15d9SAdrian Chadd antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1); 314ba5c15d9SAdrian Chadd } 315ba5c15d9SAdrian Chadd 316ba5c15d9SAdrian Chadd KASSERT(bf->bf_nseg == 1, 317ba5c15d9SAdrian Chadd ("multi-segment beacon frame; nseg %u", bf->bf_nseg)); 31846634305SAdrian Chadd 319ba5c15d9SAdrian Chadd /* 320ba5c15d9SAdrian Chadd * Calculate rate code. 321ba5c15d9SAdrian Chadd * XXX everything at min xmit rate 322ba5c15d9SAdrian Chadd */ 323ba5c15d9SAdrian Chadd rix = 0; 324ba5c15d9SAdrian Chadd rt = sc->sc_currates; 325ba5c15d9SAdrian Chadd rate = rt->info[rix].rateCode; 326ba5c15d9SAdrian Chadd if (USE_SHPREAMBLE(ic)) 327ba5c15d9SAdrian Chadd rate |= rt->info[rix].shortPreamble; 328ba5c15d9SAdrian Chadd ath_hal_setuptxdesc(ah, ds 329ba5c15d9SAdrian Chadd , m->m_len + IEEE80211_CRC_LEN /* frame length */ 330ba5c15d9SAdrian Chadd , sizeof(struct ieee80211_frame)/* header length */ 331ba5c15d9SAdrian Chadd , HAL_PKT_TYPE_BEACON /* Atheros packet type */ 33212087a07SAdrian Chadd , ieee80211_get_node_txpower(ni) /* txpower XXX */ 333ba5c15d9SAdrian Chadd , rate, 1 /* series 0 rate/tries */ 334ba5c15d9SAdrian Chadd , HAL_TXKEYIX_INVALID /* no encryption */ 335ba5c15d9SAdrian Chadd , antenna /* antenna mode */ 336ba5c15d9SAdrian Chadd , flags /* no ack, veol for beacons */ 337ba5c15d9SAdrian Chadd , 0 /* rts/cts rate */ 338ba5c15d9SAdrian Chadd , 0 /* rts/cts duration */ 339ba5c15d9SAdrian Chadd ); 340e1252ce1SAdrian Chadd 341e1252ce1SAdrian Chadd /* 342e1252ce1SAdrian Chadd * The EDMA HAL currently assumes that _all_ rate control 343e1252ce1SAdrian Chadd * settings are done in ath_hal_set11nratescenario(), rather 344e1252ce1SAdrian Chadd * than in ath_hal_setuptxdesc(). 345e1252ce1SAdrian Chadd */ 346e1252ce1SAdrian Chadd if (sc->sc_isedma) { 347e1252ce1SAdrian Chadd memset(&rc, 0, sizeof(rc)); 348e1252ce1SAdrian Chadd 349e1252ce1SAdrian Chadd rc[0].ChSel = sc->sc_txchainmask; 350e1252ce1SAdrian Chadd rc[0].Tries = 1; 351e1252ce1SAdrian Chadd rc[0].Rate = rt->info[rix].rateCode; 352e1252ce1SAdrian Chadd rc[0].RateIndex = rix; 353e1252ce1SAdrian Chadd rc[0].tx_power_cap = 0x3f; 354e1252ce1SAdrian Chadd rc[0].PktDuration = 355e1252ce1SAdrian Chadd ath_hal_computetxtime(ah, rt, roundup(m->m_len, 4), 3567ff1939dSAdrian Chadd rix, 0, AH_TRUE); 357e1252ce1SAdrian Chadd ath_hal_set11nratescenario(ah, ds, 0, 0, rc, 4, flags); 358e1252ce1SAdrian Chadd } 359e1252ce1SAdrian Chadd 360ba5c15d9SAdrian Chadd /* NB: beacon's BufLen must be a multiple of 4 bytes */ 36146634305SAdrian Chadd segLenList[0] = roundup(m->m_len, 4); 36246634305SAdrian Chadd segLenList[1] = segLenList[2] = segLenList[3] = 0; 36346634305SAdrian Chadd bufAddrList[0] = bf->bf_segs[0].ds_addr; 36446634305SAdrian Chadd bufAddrList[1] = bufAddrList[2] = bufAddrList[3] = 0; 365ba5c15d9SAdrian Chadd ath_hal_filltxdesc(ah, ds 36646634305SAdrian Chadd , bufAddrList 36746634305SAdrian Chadd , segLenList 36846634305SAdrian Chadd , 0 /* XXX desc id */ 36946634305SAdrian Chadd , sc->sc_bhalq /* hardware TXQ */ 370ba5c15d9SAdrian Chadd , AH_TRUE /* first segment */ 371ba5c15d9SAdrian Chadd , AH_TRUE /* last segment */ 372ba5c15d9SAdrian Chadd , ds /* first descriptor */ 373ba5c15d9SAdrian Chadd ); 374ba5c15d9SAdrian Chadd #if 0 375ba5c15d9SAdrian Chadd ath_desc_swap(ds); 376ba5c15d9SAdrian Chadd #endif 377ba5c15d9SAdrian Chadd #undef USE_SHPREAMBLE 378ba5c15d9SAdrian Chadd } 379ba5c15d9SAdrian Chadd 380ba5c15d9SAdrian Chadd void 381ba5c15d9SAdrian Chadd ath_beacon_update(struct ieee80211vap *vap, int item) 382ba5c15d9SAdrian Chadd { 3830cf00015SAdrian Chadd struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; 384ba5c15d9SAdrian Chadd 385ba5c15d9SAdrian Chadd setbit(bo->bo_flags, item); 386ba5c15d9SAdrian Chadd } 387ba5c15d9SAdrian Chadd 388ba5c15d9SAdrian Chadd /* 389b837332dSAdrian Chadd * Handle a beacon miss. 390b837332dSAdrian Chadd */ 391f5c30c4eSAdrian Chadd void 392b837332dSAdrian Chadd ath_beacon_miss(struct ath_softc *sc) 393b837332dSAdrian Chadd { 394b837332dSAdrian Chadd HAL_SURVEY_SAMPLE hs; 395b837332dSAdrian Chadd HAL_BOOL ret; 396b837332dSAdrian Chadd uint32_t hangs; 397b837332dSAdrian Chadd 398b837332dSAdrian Chadd bzero(&hs, sizeof(hs)); 399b837332dSAdrian Chadd 400b837332dSAdrian Chadd ret = ath_hal_get_mib_cycle_counts(sc->sc_ah, &hs); 401b837332dSAdrian Chadd 402b837332dSAdrian Chadd if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { 403b837332dSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 404b837332dSAdrian Chadd "%s: hang=0x%08x\n", 405b837332dSAdrian Chadd __func__, 406b837332dSAdrian Chadd hangs); 407b837332dSAdrian Chadd } 408b837332dSAdrian Chadd 409370f81faSAdrian Chadd #ifdef ATH_DEBUG_ALQ 410370f81faSAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_MISSED_BEACON)) 411370f81faSAdrian Chadd if_ath_alq_post(&sc->sc_alq, ATH_ALQ_MISSED_BEACON, 0, NULL); 412370f81faSAdrian Chadd #endif 413370f81faSAdrian Chadd 414b837332dSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 415b837332dSAdrian Chadd "%s: valid=%d, txbusy=%u, rxbusy=%u, chanbusy=%u, " 416b837332dSAdrian Chadd "extchanbusy=%u, cyclecount=%u\n", 417b837332dSAdrian Chadd __func__, 418b837332dSAdrian Chadd ret, 419b837332dSAdrian Chadd hs.tx_busy, 420b837332dSAdrian Chadd hs.rx_busy, 421b837332dSAdrian Chadd hs.chan_busy, 422b837332dSAdrian Chadd hs.ext_chan_busy, 423b837332dSAdrian Chadd hs.cycle_count); 424b837332dSAdrian Chadd } 425b837332dSAdrian Chadd 426b837332dSAdrian Chadd /* 427ba5c15d9SAdrian Chadd * Transmit a beacon frame at SWBA. Dynamic updates to the 428ba5c15d9SAdrian Chadd * frame contents are done as needed and the slot time is 429ba5c15d9SAdrian Chadd * also adjusted based on current state. 430ba5c15d9SAdrian Chadd */ 431ba5c15d9SAdrian Chadd void 432ba5c15d9SAdrian Chadd ath_beacon_proc(void *arg, int pending) 433ba5c15d9SAdrian Chadd { 434ba5c15d9SAdrian Chadd struct ath_softc *sc = arg; 435ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 436ba5c15d9SAdrian Chadd struct ieee80211vap *vap; 437ba5c15d9SAdrian Chadd struct ath_buf *bf; 438ba5c15d9SAdrian Chadd int slot, otherant; 439ba5c15d9SAdrian Chadd uint32_t bfaddr; 440ba5c15d9SAdrian Chadd 441ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n", 442ba5c15d9SAdrian Chadd __func__, pending); 443ba5c15d9SAdrian Chadd /* 444ba5c15d9SAdrian Chadd * Check if the previous beacon has gone out. If 445ba5c15d9SAdrian Chadd * not don't try to post another, skip this period 446ba5c15d9SAdrian Chadd * and wait for the next. Missed beacons indicate 447ba5c15d9SAdrian Chadd * a problem and should not occur. If we miss too 448ba5c15d9SAdrian Chadd * many consecutive beacons reset the device. 449ba5c15d9SAdrian Chadd */ 450ba5c15d9SAdrian Chadd if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) { 451*f858e928SAdrian Chadd 452ba5c15d9SAdrian Chadd sc->sc_bmisscount++; 453ba5c15d9SAdrian Chadd sc->sc_stats.ast_be_missed++; 454b837332dSAdrian Chadd ath_beacon_miss(sc); 455*f858e928SAdrian Chadd 456ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 457ba5c15d9SAdrian Chadd "%s: missed %u consecutive beacons\n", 458ba5c15d9SAdrian Chadd __func__, sc->sc_bmisscount); 459ba5c15d9SAdrian Chadd if (sc->sc_bmisscount >= ath_bstuck_threshold) 460ba5c15d9SAdrian Chadd taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask); 461ba5c15d9SAdrian Chadd return; 462ba5c15d9SAdrian Chadd } 463ba5c15d9SAdrian Chadd if (sc->sc_bmisscount != 0) { 464ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 465ba5c15d9SAdrian Chadd "%s: resume beacon xmit after %u misses\n", 466ba5c15d9SAdrian Chadd __func__, sc->sc_bmisscount); 467ba5c15d9SAdrian Chadd sc->sc_bmisscount = 0; 468370f81faSAdrian Chadd #ifdef ATH_DEBUG_ALQ 469370f81faSAdrian Chadd if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_RESUME_BEACON)) 470370f81faSAdrian Chadd if_ath_alq_post(&sc->sc_alq, ATH_ALQ_RESUME_BEACON, 0, NULL); 471370f81faSAdrian Chadd #endif 472ba5c15d9SAdrian Chadd } 473ba5c15d9SAdrian Chadd 474ba5c15d9SAdrian Chadd if (sc->sc_stagbeacons) { /* staggered beacons */ 4757a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic; 476ba5c15d9SAdrian Chadd uint32_t tsftu; 477ba5c15d9SAdrian Chadd 478ba5c15d9SAdrian Chadd tsftu = ath_hal_gettsf32(ah) >> 10; 479ba5c15d9SAdrian Chadd /* XXX lintval */ 480ba5c15d9SAdrian Chadd slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval; 481ba5c15d9SAdrian Chadd vap = sc->sc_bslot[(slot+1) % ATH_BCBUF]; 482ba5c15d9SAdrian Chadd bfaddr = 0; 483ba5c15d9SAdrian Chadd if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { 484ba5c15d9SAdrian Chadd bf = ath_beacon_generate(sc, vap); 485ba5c15d9SAdrian Chadd if (bf != NULL) 486ba5c15d9SAdrian Chadd bfaddr = bf->bf_daddr; 487ba5c15d9SAdrian Chadd } 488ba5c15d9SAdrian Chadd } else { /* burst'd beacons */ 489ba5c15d9SAdrian Chadd uint32_t *bflink = &bfaddr; 490ba5c15d9SAdrian Chadd 491ba5c15d9SAdrian Chadd for (slot = 0; slot < ATH_BCBUF; slot++) { 492ba5c15d9SAdrian Chadd vap = sc->sc_bslot[slot]; 493ba5c15d9SAdrian Chadd if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) { 494ba5c15d9SAdrian Chadd bf = ath_beacon_generate(sc, vap); 49592e84e43SAdrian Chadd /* 49692e84e43SAdrian Chadd * XXX TODO: this should use settxdesclinkptr() 49792e84e43SAdrian Chadd * otherwise it won't work for EDMA chipsets! 49892e84e43SAdrian Chadd */ 499ba5c15d9SAdrian Chadd if (bf != NULL) { 500bb069955SAdrian Chadd /* XXX should do this using the ds */ 501ba5c15d9SAdrian Chadd *bflink = bf->bf_daddr; 502bb069955SAdrian Chadd ath_hal_gettxdesclinkptr(sc->sc_ah, 503bb069955SAdrian Chadd bf->bf_desc, &bflink); 504ba5c15d9SAdrian Chadd } 505ba5c15d9SAdrian Chadd } 506ba5c15d9SAdrian Chadd } 50792e84e43SAdrian Chadd /* 50892e84e43SAdrian Chadd * XXX TODO: this should use settxdesclinkptr() 50992e84e43SAdrian Chadd * otherwise it won't work for EDMA chipsets! 51092e84e43SAdrian Chadd */ 511ba5c15d9SAdrian Chadd *bflink = 0; /* terminate list */ 512ba5c15d9SAdrian Chadd } 513ba5c15d9SAdrian Chadd 514ba5c15d9SAdrian Chadd /* 515ba5c15d9SAdrian Chadd * Handle slot time change when a non-ERP station joins/leaves 516ba5c15d9SAdrian Chadd * an 11g network. The 802.11 layer notifies us via callback, 517ba5c15d9SAdrian Chadd * we mark updateslot, then wait one beacon before effecting 518ba5c15d9SAdrian Chadd * the change. This gives associated stations at least one 519ba5c15d9SAdrian Chadd * beacon interval to note the state change. 520ba5c15d9SAdrian Chadd */ 521ba5c15d9SAdrian Chadd /* XXX locking */ 522ba5c15d9SAdrian Chadd if (sc->sc_updateslot == UPDATE) { 523ba5c15d9SAdrian Chadd sc->sc_updateslot = COMMIT; /* commit next beacon */ 524ba5c15d9SAdrian Chadd sc->sc_slotupdate = slot; 525ba5c15d9SAdrian Chadd } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) 526ba5c15d9SAdrian Chadd ath_setslottime(sc); /* commit change to h/w */ 527ba5c15d9SAdrian Chadd 528ba5c15d9SAdrian Chadd /* 529ba5c15d9SAdrian Chadd * Check recent per-antenna transmit statistics and flip 530ba5c15d9SAdrian Chadd * the default antenna if noticeably more frames went out 531ba5c15d9SAdrian Chadd * on the non-default antenna. 532ba5c15d9SAdrian Chadd * XXX assumes 2 anntenae 533ba5c15d9SAdrian Chadd */ 534ba5c15d9SAdrian Chadd if (!sc->sc_diversity && (!sc->sc_stagbeacons || slot == 0)) { 535ba5c15d9SAdrian Chadd otherant = sc->sc_defant & 1 ? 2 : 1; 536ba5c15d9SAdrian Chadd if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2) 537ba5c15d9SAdrian Chadd ath_setdefantenna(sc, otherant); 538ba5c15d9SAdrian Chadd sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0; 539ba5c15d9SAdrian Chadd } 540ba5c15d9SAdrian Chadd 541b837332dSAdrian Chadd /* Program the CABQ with the contents of the CABQ txq and start it */ 542b837332dSAdrian Chadd ATH_TXQ_LOCK(sc->sc_cabq); 543b837332dSAdrian Chadd ath_beacon_cabq_start(sc); 544b837332dSAdrian Chadd ATH_TXQ_UNLOCK(sc->sc_cabq); 545b837332dSAdrian Chadd 546b837332dSAdrian Chadd /* Program the new beacon frame if we have one for this interval */ 547ba5c15d9SAdrian Chadd if (bfaddr != 0) { 548ba5c15d9SAdrian Chadd /* 549ba5c15d9SAdrian Chadd * Stop any current dma and put the new frame on the queue. 550ba5c15d9SAdrian Chadd * This should never fail since we check above that no frames 551ba5c15d9SAdrian Chadd * are still pending on the queue. 552ba5c15d9SAdrian Chadd */ 553e1252ce1SAdrian Chadd if (! sc->sc_isedma) { 554ba5c15d9SAdrian Chadd if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { 555ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_ANY, 556ba5c15d9SAdrian Chadd "%s: beacon queue %u did not stop?\n", 557ba5c15d9SAdrian Chadd __func__, sc->sc_bhalq); 558ba5c15d9SAdrian Chadd } 559e1252ce1SAdrian Chadd } 560ba5c15d9SAdrian Chadd /* NB: cabq traffic should already be queued and primed */ 561e1252ce1SAdrian Chadd 562ba5c15d9SAdrian Chadd ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr); 563ba5c15d9SAdrian Chadd ath_hal_txstart(ah, sc->sc_bhalq); 564ba5c15d9SAdrian Chadd 565ba5c15d9SAdrian Chadd sc->sc_stats.ast_be_xmit++; 566ba5c15d9SAdrian Chadd } 567ba5c15d9SAdrian Chadd } 568ba5c15d9SAdrian Chadd 56992e84e43SAdrian Chadd static void 57092e84e43SAdrian Chadd ath_beacon_cabq_start_edma(struct ath_softc *sc) 57192e84e43SAdrian Chadd { 57292e84e43SAdrian Chadd struct ath_buf *bf, *bf_last; 57392e84e43SAdrian Chadd struct ath_txq *cabq = sc->sc_cabq; 57492e84e43SAdrian Chadd #if 0 57592e84e43SAdrian Chadd struct ath_buf *bfi; 57692e84e43SAdrian Chadd int i = 0; 57792e84e43SAdrian Chadd #endif 57892e84e43SAdrian Chadd 57992e84e43SAdrian Chadd ATH_TXQ_LOCK_ASSERT(cabq); 58092e84e43SAdrian Chadd 58192e84e43SAdrian Chadd if (TAILQ_EMPTY(&cabq->axq_q)) 58292e84e43SAdrian Chadd return; 58392e84e43SAdrian Chadd bf = TAILQ_FIRST(&cabq->axq_q); 58492e84e43SAdrian Chadd bf_last = TAILQ_LAST(&cabq->axq_q, axq_q_s); 58592e84e43SAdrian Chadd 586b837332dSAdrian Chadd /* 58792e84e43SAdrian Chadd * This is a dirty, dirty hack to push the contents of 58892e84e43SAdrian Chadd * the cabq staging queue into the FIFO. 589b837332dSAdrian Chadd * 59092e84e43SAdrian Chadd * This ideally should live in the EDMA code file 59192e84e43SAdrian Chadd * and only push things into the CABQ if there's a FIFO 59292e84e43SAdrian Chadd * slot. 59392e84e43SAdrian Chadd * 59492e84e43SAdrian Chadd * We can't treat this like a normal TX queue because 59592e84e43SAdrian Chadd * in the case of multi-VAP traffic, we may have to flush 59692e84e43SAdrian Chadd * the CABQ each new (staggered) beacon that goes out. 59792e84e43SAdrian Chadd * But for non-staggered beacons, we could in theory 59892e84e43SAdrian Chadd * handle multicast traffic for all VAPs in one FIFO 59992e84e43SAdrian Chadd * push. Just keep all of this in mind if you're wondering 60092e84e43SAdrian Chadd * how to correctly/better handle multi-VAP CABQ traffic 60192e84e43SAdrian Chadd * with EDMA. 602b837332dSAdrian Chadd */ 60392e84e43SAdrian Chadd 60492e84e43SAdrian Chadd /* 60592e84e43SAdrian Chadd * Is the CABQ FIFO free? If not, complain loudly and 60692e84e43SAdrian Chadd * don't queue anything. Maybe we'll flush the CABQ 60792e84e43SAdrian Chadd * traffic, maybe we won't. But that'll happen next 60892e84e43SAdrian Chadd * beacon interval. 60992e84e43SAdrian Chadd */ 61092e84e43SAdrian Chadd if (cabq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) { 61192e84e43SAdrian Chadd device_printf(sc->sc_dev, 61292e84e43SAdrian Chadd "%s: Q%d: CAB FIFO queue=%d?\n", 61392e84e43SAdrian Chadd __func__, 61492e84e43SAdrian Chadd cabq->axq_qnum, 61592e84e43SAdrian Chadd cabq->axq_fifo_depth); 61692e84e43SAdrian Chadd return; 61792e84e43SAdrian Chadd } 61892e84e43SAdrian Chadd 61992e84e43SAdrian Chadd /* 62092e84e43SAdrian Chadd * Ok, so here's the gymnastics reqiured to make this 62192e84e43SAdrian Chadd * all sensible. 62292e84e43SAdrian Chadd */ 62392e84e43SAdrian Chadd 62492e84e43SAdrian Chadd /* 62592e84e43SAdrian Chadd * Tag the first/last buffer appropriately. 62692e84e43SAdrian Chadd */ 62792e84e43SAdrian Chadd bf->bf_flags |= ATH_BUF_FIFOPTR; 62892e84e43SAdrian Chadd bf_last->bf_flags |= ATH_BUF_FIFOEND; 62992e84e43SAdrian Chadd 63092e84e43SAdrian Chadd #if 0 63192e84e43SAdrian Chadd i = 0; 63292e84e43SAdrian Chadd TAILQ_FOREACH(bfi, &cabq->axq_q, bf_list) { 63392e84e43SAdrian Chadd ath_printtxbuf(sc, bf, cabq->axq_qnum, i, 0); 63492e84e43SAdrian Chadd i++; 63592e84e43SAdrian Chadd } 63692e84e43SAdrian Chadd #endif 63792e84e43SAdrian Chadd 63892e84e43SAdrian Chadd /* 63992e84e43SAdrian Chadd * We now need to push this set of frames onto the tail 64092e84e43SAdrian Chadd * of the FIFO queue. We don't adjust the aggregate 64192e84e43SAdrian Chadd * count, only the queue depth counter(s). 64292e84e43SAdrian Chadd * We also need to blank the link pointer now. 64392e84e43SAdrian Chadd */ 64492e84e43SAdrian Chadd TAILQ_CONCAT(&cabq->fifo.axq_q, &cabq->axq_q, bf_list); 64592e84e43SAdrian Chadd cabq->axq_link = NULL; 64692e84e43SAdrian Chadd cabq->fifo.axq_depth += cabq->axq_depth; 64792e84e43SAdrian Chadd cabq->axq_depth = 0; 64892e84e43SAdrian Chadd 64992e84e43SAdrian Chadd /* Bump FIFO queue */ 65092e84e43SAdrian Chadd cabq->axq_fifo_depth++; 65192e84e43SAdrian Chadd 65292e84e43SAdrian Chadd /* Push the first entry into the hardware */ 65392e84e43SAdrian Chadd ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr); 6549be82a42SAdrian Chadd cabq->axq_flags |= ATH_TXQ_PUTRUNNING; 65592e84e43SAdrian Chadd 65692e84e43SAdrian Chadd /* NB: gated by beacon so safe to start here */ 65792e84e43SAdrian Chadd ath_hal_txstart(sc->sc_ah, cabq->axq_qnum); 65892e84e43SAdrian Chadd 65992e84e43SAdrian Chadd } 66092e84e43SAdrian Chadd 66192e84e43SAdrian Chadd static void 66292e84e43SAdrian Chadd ath_beacon_cabq_start_legacy(struct ath_softc *sc) 663b837332dSAdrian Chadd { 664b837332dSAdrian Chadd struct ath_buf *bf; 665b837332dSAdrian Chadd struct ath_txq *cabq = sc->sc_cabq; 666b837332dSAdrian Chadd 667b837332dSAdrian Chadd ATH_TXQ_LOCK_ASSERT(cabq); 668b837332dSAdrian Chadd if (TAILQ_EMPTY(&cabq->axq_q)) 669b837332dSAdrian Chadd return; 670b837332dSAdrian Chadd bf = TAILQ_FIRST(&cabq->axq_q); 671b837332dSAdrian Chadd 672b837332dSAdrian Chadd /* Push the first entry into the hardware */ 673b837332dSAdrian Chadd ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr); 6749be82a42SAdrian Chadd cabq->axq_flags |= ATH_TXQ_PUTRUNNING; 675b837332dSAdrian Chadd 676b837332dSAdrian Chadd /* NB: gated by beacon so safe to start here */ 677b837332dSAdrian Chadd ath_hal_txstart(sc->sc_ah, cabq->axq_qnum); 678b837332dSAdrian Chadd } 679b837332dSAdrian Chadd 68092e84e43SAdrian Chadd /* 68192e84e43SAdrian Chadd * Start CABQ transmission - this assumes that all frames are prepped 68292e84e43SAdrian Chadd * and ready in the CABQ. 68392e84e43SAdrian Chadd */ 68492e84e43SAdrian Chadd void 68592e84e43SAdrian Chadd ath_beacon_cabq_start(struct ath_softc *sc) 68692e84e43SAdrian Chadd { 68792e84e43SAdrian Chadd struct ath_txq *cabq = sc->sc_cabq; 68892e84e43SAdrian Chadd 68992e84e43SAdrian Chadd ATH_TXQ_LOCK_ASSERT(cabq); 69092e84e43SAdrian Chadd 69192e84e43SAdrian Chadd if (TAILQ_EMPTY(&cabq->axq_q)) 69292e84e43SAdrian Chadd return; 69392e84e43SAdrian Chadd 69492e84e43SAdrian Chadd if (sc->sc_isedma) 69592e84e43SAdrian Chadd ath_beacon_cabq_start_edma(sc); 69692e84e43SAdrian Chadd else 69792e84e43SAdrian Chadd ath_beacon_cabq_start_legacy(sc); 69892e84e43SAdrian Chadd } 69992e84e43SAdrian Chadd 700ba5c15d9SAdrian Chadd struct ath_buf * 701ba5c15d9SAdrian Chadd ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) 702ba5c15d9SAdrian Chadd { 703ba5c15d9SAdrian Chadd struct ath_vap *avp = ATH_VAP(vap); 704ba5c15d9SAdrian Chadd struct ath_txq *cabq = sc->sc_cabq; 705ba5c15d9SAdrian Chadd struct ath_buf *bf; 706ba5c15d9SAdrian Chadd struct mbuf *m; 707ba5c15d9SAdrian Chadd int nmcastq, error; 708ba5c15d9SAdrian Chadd 709ba5c15d9SAdrian Chadd KASSERT(vap->iv_state >= IEEE80211_S_RUN, 710ba5c15d9SAdrian Chadd ("not running, state %d", vap->iv_state)); 711ba5c15d9SAdrian Chadd KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 712ba5c15d9SAdrian Chadd 713ba5c15d9SAdrian Chadd /* 714ba5c15d9SAdrian Chadd * Update dynamic beacon contents. If this returns 715ba5c15d9SAdrian Chadd * non-zero then we need to remap the memory because 716ba5c15d9SAdrian Chadd * the beacon frame changed size (probably because 717ba5c15d9SAdrian Chadd * of the TIM bitmap). 718ba5c15d9SAdrian Chadd */ 719ba5c15d9SAdrian Chadd bf = avp->av_bcbuf; 720ba5c15d9SAdrian Chadd m = bf->bf_m; 721ba5c15d9SAdrian Chadd /* XXX lock mcastq? */ 722ba5c15d9SAdrian Chadd nmcastq = avp->av_mcastq.axq_depth; 723ba5c15d9SAdrian Chadd 724210ab3c2SAdrian Chadd if (ieee80211_beacon_update(bf->bf_node, m, nmcastq)) { 725ba5c15d9SAdrian Chadd /* XXX too conservative? */ 726ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 727ba5c15d9SAdrian Chadd error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 728ba5c15d9SAdrian Chadd bf->bf_segs, &bf->bf_nseg, 729ba5c15d9SAdrian Chadd BUS_DMA_NOWAIT); 730ba5c15d9SAdrian Chadd if (error != 0) { 731ba5c15d9SAdrian Chadd if_printf(vap->iv_ifp, 732ba5c15d9SAdrian Chadd "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 733ba5c15d9SAdrian Chadd __func__, error); 734ba5c15d9SAdrian Chadd return NULL; 735ba5c15d9SAdrian Chadd } 736ba5c15d9SAdrian Chadd } 7370cf00015SAdrian Chadd if ((vap->iv_bcn_off.bo_tim[4] & 1) && cabq->axq_depth) { 738ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 739ba5c15d9SAdrian Chadd "%s: cabq did not drain, mcastq %u cabq %u\n", 740ba5c15d9SAdrian Chadd __func__, nmcastq, cabq->axq_depth); 741ba5c15d9SAdrian Chadd sc->sc_stats.ast_cabq_busy++; 742ba5c15d9SAdrian Chadd if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) { 743ba5c15d9SAdrian Chadd /* 744ba5c15d9SAdrian Chadd * CABQ traffic from a previous vap is still pending. 745ba5c15d9SAdrian Chadd * We must drain the q before this beacon frame goes 746ba5c15d9SAdrian Chadd * out as otherwise this vap's stations will get cab 747ba5c15d9SAdrian Chadd * frames from a different vap. 748ba5c15d9SAdrian Chadd * XXX could be slow causing us to miss DBA 749ba5c15d9SAdrian Chadd */ 7509be82a42SAdrian Chadd /* 7519be82a42SAdrian Chadd * XXX TODO: this doesn't stop CABQ DMA - it assumes 7529be82a42SAdrian Chadd * that since we're about to transmit a beacon, we've 7539be82a42SAdrian Chadd * already stopped transmitting on the CABQ. But this 7549be82a42SAdrian Chadd * doesn't at all mean that the CABQ DMA QCU will 7559be82a42SAdrian Chadd * accept a new TXDP! So what, should we do a DMA 7569be82a42SAdrian Chadd * stop? What if it fails? 7579be82a42SAdrian Chadd * 7589be82a42SAdrian Chadd * More thought is required here. 7599be82a42SAdrian Chadd */ 760062cf7d9SAdrian Chadd /* 761062cf7d9SAdrian Chadd * XXX can we even stop TX DMA here? Check what the 762062cf7d9SAdrian Chadd * reference driver does for cabq for beacons, given 763062cf7d9SAdrian Chadd * that stopping TX requires RX is paused. 764062cf7d9SAdrian Chadd */ 765ba5c15d9SAdrian Chadd ath_tx_draintxq(sc, cabq); 766ba5c15d9SAdrian Chadd } 767ba5c15d9SAdrian Chadd } 768ba5c15d9SAdrian Chadd ath_beacon_setup(sc, bf); 769ba5c15d9SAdrian Chadd bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 770ba5c15d9SAdrian Chadd 771ba5c15d9SAdrian Chadd /* 7721410ca56SAdrian Chadd * XXX TODO: tie into net80211 for quiet time IE update and program 7731410ca56SAdrian Chadd * local AP timer if we require it. The process of updating the 7741410ca56SAdrian Chadd * beacon will also update the IE with the relevant counters. 7751410ca56SAdrian Chadd */ 7761410ca56SAdrian Chadd 7771410ca56SAdrian Chadd /* 778ba5c15d9SAdrian Chadd * Enable the CAB queue before the beacon queue to 779ba5c15d9SAdrian Chadd * insure cab frames are triggered by this beacon. 780ba5c15d9SAdrian Chadd */ 7810cf00015SAdrian Chadd if (vap->iv_bcn_off.bo_tim[4] & 1) { 782ba5c15d9SAdrian Chadd /* NB: only at DTIM */ 783b837332dSAdrian Chadd ATH_TXQ_LOCK(&avp->av_mcastq); 784ba5c15d9SAdrian Chadd if (nmcastq) { 785b6ef0f8aSAdrian Chadd struct ath_buf *bfm, *bfc_last; 786ba5c15d9SAdrian Chadd 787ba5c15d9SAdrian Chadd /* 788ba5c15d9SAdrian Chadd * Move frames from the s/w mcast q to the h/w cab q. 78974ea88c3SAdrian Chadd * 790b837332dSAdrian Chadd * XXX TODO: if we chain together multiple VAPs 791b837332dSAdrian Chadd * worth of CABQ traffic, should we keep the 792b837332dSAdrian Chadd * MORE data bit set on the last frame of each 793b837332dSAdrian Chadd * intermediary VAP (ie, only clear the MORE 794b837332dSAdrian Chadd * bit of the last frame on the last vap?) 795ba5c15d9SAdrian Chadd */ 796ba5c15d9SAdrian Chadd bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q); 797b837332dSAdrian Chadd ATH_TXQ_LOCK(cabq); 798b6ef0f8aSAdrian Chadd 799b6ef0f8aSAdrian Chadd /* 800b6ef0f8aSAdrian Chadd * If there's already a frame on the CABQ, we 801b6ef0f8aSAdrian Chadd * need to link to the end of the last frame. 802b6ef0f8aSAdrian Chadd * We can't use axq_link here because 803b6ef0f8aSAdrian Chadd * EDMA descriptors require some recalculation 804b6ef0f8aSAdrian Chadd * (checksum) to occur. 805b6ef0f8aSAdrian Chadd */ 806b6ef0f8aSAdrian Chadd bfc_last = ATH_TXQ_LAST(cabq, axq_q_s); 807b6ef0f8aSAdrian Chadd if (bfc_last != NULL) { 808b6ef0f8aSAdrian Chadd ath_hal_settxdesclink(sc->sc_ah, 809b6ef0f8aSAdrian Chadd bfc_last->bf_lastds, 810b6ef0f8aSAdrian Chadd bfm->bf_daddr); 811b6ef0f8aSAdrian Chadd } 812ba5c15d9SAdrian Chadd ath_txqmove(cabq, &avp->av_mcastq); 813b837332dSAdrian Chadd ATH_TXQ_UNLOCK(cabq); 814b837332dSAdrian Chadd /* 815b837332dSAdrian Chadd * XXX not entirely accurate, in case a mcast 816b837332dSAdrian Chadd * queue frame arrived before we grabbed the TX 817b837332dSAdrian Chadd * lock. 818b837332dSAdrian Chadd */ 819ba5c15d9SAdrian Chadd sc->sc_stats.ast_cabq_xmit += nmcastq; 820ba5c15d9SAdrian Chadd } 821b837332dSAdrian Chadd ATH_TXQ_UNLOCK(&avp->av_mcastq); 822ba5c15d9SAdrian Chadd } 823ba5c15d9SAdrian Chadd return bf; 824ba5c15d9SAdrian Chadd } 825ba5c15d9SAdrian Chadd 826ba5c15d9SAdrian Chadd void 827ba5c15d9SAdrian Chadd ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap) 828ba5c15d9SAdrian Chadd { 829ba5c15d9SAdrian Chadd struct ath_vap *avp = ATH_VAP(vap); 830ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 831ba5c15d9SAdrian Chadd struct ath_buf *bf; 832ba5c15d9SAdrian Chadd struct mbuf *m; 833ba5c15d9SAdrian Chadd int error; 834ba5c15d9SAdrian Chadd 835ba5c15d9SAdrian Chadd KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer")); 836ba5c15d9SAdrian Chadd 837ba5c15d9SAdrian Chadd /* 838ba5c15d9SAdrian Chadd * Update dynamic beacon contents. If this returns 839ba5c15d9SAdrian Chadd * non-zero then we need to remap the memory because 840ba5c15d9SAdrian Chadd * the beacon frame changed size (probably because 841ba5c15d9SAdrian Chadd * of the TIM bitmap). 842ba5c15d9SAdrian Chadd */ 843ba5c15d9SAdrian Chadd bf = avp->av_bcbuf; 844ba5c15d9SAdrian Chadd m = bf->bf_m; 845210ab3c2SAdrian Chadd if (ieee80211_beacon_update(bf->bf_node, m, 0)) { 846ba5c15d9SAdrian Chadd /* XXX too conservative? */ 847ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 848ba5c15d9SAdrian Chadd error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m, 849ba5c15d9SAdrian Chadd bf->bf_segs, &bf->bf_nseg, 850ba5c15d9SAdrian Chadd BUS_DMA_NOWAIT); 851ba5c15d9SAdrian Chadd if (error != 0) { 852ba5c15d9SAdrian Chadd if_printf(vap->iv_ifp, 853ba5c15d9SAdrian Chadd "%s: bus_dmamap_load_mbuf_sg failed, error %u\n", 854ba5c15d9SAdrian Chadd __func__, error); 855ba5c15d9SAdrian Chadd return; 856ba5c15d9SAdrian Chadd } 857ba5c15d9SAdrian Chadd } 858ba5c15d9SAdrian Chadd ath_beacon_setup(sc, bf); 859ba5c15d9SAdrian Chadd bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); 860ba5c15d9SAdrian Chadd 861ba5c15d9SAdrian Chadd /* NB: caller is known to have already stopped tx dma */ 862ba5c15d9SAdrian Chadd ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); 863ba5c15d9SAdrian Chadd ath_hal_txstart(ah, sc->sc_bhalq); 864ba5c15d9SAdrian Chadd } 865ba5c15d9SAdrian Chadd 866ba5c15d9SAdrian Chadd /* 867ba5c15d9SAdrian Chadd * Reclaim beacon resources and return buffer to the pool. 868ba5c15d9SAdrian Chadd */ 869ba5c15d9SAdrian Chadd void 870ba5c15d9SAdrian Chadd ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf) 871ba5c15d9SAdrian Chadd { 872ba5c15d9SAdrian Chadd 873ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_NODE, "%s: free bf=%p, bf_m=%p, bf_node=%p\n", 874ba5c15d9SAdrian Chadd __func__, bf, bf->bf_m, bf->bf_node); 875ba5c15d9SAdrian Chadd if (bf->bf_m != NULL) { 876ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 877ba5c15d9SAdrian Chadd m_freem(bf->bf_m); 878ba5c15d9SAdrian Chadd bf->bf_m = NULL; 879ba5c15d9SAdrian Chadd } 880ba5c15d9SAdrian Chadd if (bf->bf_node != NULL) { 881ba5c15d9SAdrian Chadd ieee80211_free_node(bf->bf_node); 882ba5c15d9SAdrian Chadd bf->bf_node = NULL; 883ba5c15d9SAdrian Chadd } 884ba5c15d9SAdrian Chadd TAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list); 885ba5c15d9SAdrian Chadd } 886ba5c15d9SAdrian Chadd 887ba5c15d9SAdrian Chadd /* 888ba5c15d9SAdrian Chadd * Reclaim beacon resources. 889ba5c15d9SAdrian Chadd */ 890ba5c15d9SAdrian Chadd void 891ba5c15d9SAdrian Chadd ath_beacon_free(struct ath_softc *sc) 892ba5c15d9SAdrian Chadd { 893ba5c15d9SAdrian Chadd struct ath_buf *bf; 894ba5c15d9SAdrian Chadd 895ba5c15d9SAdrian Chadd TAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) { 896ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_NODE, 897ba5c15d9SAdrian Chadd "%s: free bf=%p, bf_m=%p, bf_node=%p\n", 898ba5c15d9SAdrian Chadd __func__, bf, bf->bf_m, bf->bf_node); 899ba5c15d9SAdrian Chadd if (bf->bf_m != NULL) { 900ba5c15d9SAdrian Chadd bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap); 901ba5c15d9SAdrian Chadd m_freem(bf->bf_m); 902ba5c15d9SAdrian Chadd bf->bf_m = NULL; 903ba5c15d9SAdrian Chadd } 904ba5c15d9SAdrian Chadd if (bf->bf_node != NULL) { 905ba5c15d9SAdrian Chadd ieee80211_free_node(bf->bf_node); 906ba5c15d9SAdrian Chadd bf->bf_node = NULL; 907ba5c15d9SAdrian Chadd } 908ba5c15d9SAdrian Chadd } 909ba5c15d9SAdrian Chadd } 910ba5c15d9SAdrian Chadd 911ba5c15d9SAdrian Chadd /* 912ba5c15d9SAdrian Chadd * Configure the beacon and sleep timers. 913ba5c15d9SAdrian Chadd * 914ba5c15d9SAdrian Chadd * When operating as an AP this resets the TSF and sets 915ba5c15d9SAdrian Chadd * up the hardware to notify us when we need to issue beacons. 916ba5c15d9SAdrian Chadd * 917ba5c15d9SAdrian Chadd * When operating in station mode this sets up the beacon 918ba5c15d9SAdrian Chadd * timers according to the timestamp of the last received 919ba5c15d9SAdrian Chadd * beacon and the current TSF, configures PCF and DTIM 920ba5c15d9SAdrian Chadd * handling, programs the sleep registers so the hardware 921ba5c15d9SAdrian Chadd * will wakeup in time to receive beacons, and configures 922ba5c15d9SAdrian Chadd * the beacon miss handling so we'll receive a BMISS 923ba5c15d9SAdrian Chadd * interrupt when we stop seeing beacons from the AP 924ba5c15d9SAdrian Chadd * we've associated with. 925ba5c15d9SAdrian Chadd */ 926ba5c15d9SAdrian Chadd void 927ba5c15d9SAdrian Chadd ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 928ba5c15d9SAdrian Chadd { 929ba5c15d9SAdrian Chadd #define TSF_TO_TU(_h,_l) \ 930ba5c15d9SAdrian Chadd ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10)) 931ba5c15d9SAdrian Chadd #define FUDGE 2 932ba5c15d9SAdrian Chadd struct ath_hal *ah = sc->sc_ah; 9331410ca56SAdrian Chadd struct ath_vap *avp; 9347a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic; 935ba5c15d9SAdrian Chadd struct ieee80211_node *ni; 936ba5c15d9SAdrian Chadd u_int32_t nexttbtt, intval, tsftu; 937e1252ce1SAdrian Chadd u_int32_t nexttbtt_u8, intval_u8; 938f5c30c4eSAdrian Chadd u_int64_t tsf, tsf_beacon; 939ba5c15d9SAdrian Chadd 94061cd9692SAdrian Chadd /* 941*f858e928SAdrian Chadd * Find the first VAP that we /can/ use a beacon configuration for. 942*f858e928SAdrian Chadd * If it's a STA VAP then if it has SWBMISS set we should ignore it. 943*f858e928SAdrian Chadd * 944*f858e928SAdrian Chadd * Yes, ideally we'd not have a STA without SWBMISS followed by an 945*f858e928SAdrian Chadd * AP STA, and yes this isn't ready for P2P/TSF2 logic on AR9300 and 946*f858e928SAdrian Chadd * later chips. 94761cd9692SAdrian Chadd */ 94861cd9692SAdrian Chadd if (vap == NULL) { 949*f858e928SAdrian Chadd IEEE80211_LOCK(ic); 950*f858e928SAdrian Chadd TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 951*f858e928SAdrian Chadd /* A STA VAP w/ SWBMISS set can't be used for beaconing */ 952*f858e928SAdrian Chadd if ((vap->iv_opmode == IEEE80211_M_STA) && 953*f858e928SAdrian Chadd ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) != 0)) 954*f858e928SAdrian Chadd continue; 955*f858e928SAdrian Chadd break; 956*f858e928SAdrian Chadd } 957*f858e928SAdrian Chadd IEEE80211_UNLOCK(ic); 958*f858e928SAdrian Chadd } 959*f858e928SAdrian Chadd 960*f858e928SAdrian Chadd if (vap == NULL) { 961*f858e928SAdrian Chadd device_printf(sc->sc_dev, "called with no valid vaps?\n"); 962*f858e928SAdrian Chadd return; 963*f858e928SAdrian Chadd } 964*f858e928SAdrian Chadd 965*f858e928SAdrian Chadd if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) != 0) { 966*f858e928SAdrian Chadd device_printf(sc->sc_dev, "called on VAP with SWBMISS set?\n"); 96761cd9692SAdrian Chadd return; 96861cd9692SAdrian Chadd } 96961cd9692SAdrian Chadd 9701410ca56SAdrian Chadd /* Now that we have a vap, we can do this bit */ 9711410ca56SAdrian Chadd avp = ATH_VAP(vap); 9721410ca56SAdrian Chadd 973ba5c15d9SAdrian Chadd ni = ieee80211_ref_node(vap->iv_bss); 974ba5c15d9SAdrian Chadd 975f5c30c4eSAdrian Chadd ATH_LOCK(sc); 976f5c30c4eSAdrian Chadd ath_power_set_power_state(sc, HAL_PM_AWAKE); 977f5c30c4eSAdrian Chadd ATH_UNLOCK(sc); 978f5c30c4eSAdrian Chadd 9791410ca56SAdrian Chadd /* Always clear the quiet IE timers; let the next update program them */ 9801410ca56SAdrian Chadd ath_hal_set_quiet(ah, 0, 0, 0, HAL_QUIET_DISABLE); 9811410ca56SAdrian Chadd memset(&avp->quiet_ie, 0, sizeof(avp->quiet_ie)); 9821410ca56SAdrian Chadd 983ba5c15d9SAdrian Chadd /* extract tstamp from last beacon and convert to TU */ 98431021a2bSAndriy Voskoboinyk nexttbtt = TSF_TO_TU(le32dec(ni->ni_tstamp.data + 4), 98531021a2bSAndriy Voskoboinyk le32dec(ni->ni_tstamp.data)); 986f5c30c4eSAdrian Chadd 98731021a2bSAndriy Voskoboinyk tsf_beacon = ((uint64_t) le32dec(ni->ni_tstamp.data + 4)) << 32; 98831021a2bSAndriy Voskoboinyk tsf_beacon |= le32dec(ni->ni_tstamp.data); 989f5c30c4eSAdrian Chadd 990ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_HOSTAP || 991ba5c15d9SAdrian Chadd ic->ic_opmode == IEEE80211_M_MBSS) { 992ba5c15d9SAdrian Chadd /* 993ba5c15d9SAdrian Chadd * For multi-bss ap/mesh support beacons are either staggered 994ba5c15d9SAdrian Chadd * evenly over N slots or burst together. For the former 995ba5c15d9SAdrian Chadd * arrange for the SWBA to be delivered for each slot. 996ba5c15d9SAdrian Chadd * Slots that are not occupied will generate nothing. 997ba5c15d9SAdrian Chadd */ 998ba5c15d9SAdrian Chadd /* NB: the beacon interval is kept internally in TU's */ 999ba5c15d9SAdrian Chadd intval = ni->ni_intval & HAL_BEACON_PERIOD; 1000ba5c15d9SAdrian Chadd if (sc->sc_stagbeacons) 1001ba5c15d9SAdrian Chadd intval /= ATH_BCBUF; 1002ba5c15d9SAdrian Chadd } else { 1003ba5c15d9SAdrian Chadd /* NB: the beacon interval is kept internally in TU's */ 1004ba5c15d9SAdrian Chadd intval = ni->ni_intval & HAL_BEACON_PERIOD; 1005ba5c15d9SAdrian Chadd } 10060ffc652eSAdrian Chadd 10070ffc652eSAdrian Chadd /* 1008872f3a66SAdrian Chadd * Note: rounding up to the next intval can cause problems with 1009872f3a66SAdrian Chadd * bad APs when we're in powersave mode. 10100ffc652eSAdrian Chadd * 10110ffc652eSAdrian Chadd * In STA mode with powersave enabled, beacons are only received 10120ffc652eSAdrian Chadd * whenever the beacon timer fires to wake up the hardware. 10130ffc652eSAdrian Chadd * Now, if this is rounded up to the next intval, it assumes 10140ffc652eSAdrian Chadd * that the AP has started transmitting beacons at TSF values that 10150ffc652eSAdrian Chadd * are multiples of intval, versus say being 25 TU off. 10160ffc652eSAdrian Chadd * 1017872f3a66SAdrian Chadd * The specification (802.11-2012 10.1.3.2 - Beacon Generation in 1018872f3a66SAdrian Chadd * Infrastructure Networks) requires APs be beaconing at a 1019872f3a66SAdrian Chadd * mutiple of intval. So, if bintval=100, then we shouldn't 1020872f3a66SAdrian Chadd * get beacons at intervals other than around multiples of 100. 10210ffc652eSAdrian Chadd */ 1022ba5c15d9SAdrian Chadd if (nexttbtt == 0) /* e.g. for ap mode */ 1023ba5c15d9SAdrian Chadd nexttbtt = intval; 1024872f3a66SAdrian Chadd else 1025ba5c15d9SAdrian Chadd nexttbtt = roundup(nexttbtt, intval); 10260ffc652eSAdrian Chadd 1027*f858e928SAdrian Chadd 1028ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) { 1029ba5c15d9SAdrian Chadd HAL_BEACON_STATE bs; 1030ba5c15d9SAdrian Chadd int dtimperiod, dtimcount; 1031ba5c15d9SAdrian Chadd int cfpperiod, cfpcount; 1032ba5c15d9SAdrian Chadd 1033ba5c15d9SAdrian Chadd /* 1034ba5c15d9SAdrian Chadd * Setup dtim and cfp parameters according to 1035ba5c15d9SAdrian Chadd * last beacon we received (which may be none). 1036ba5c15d9SAdrian Chadd */ 1037ba5c15d9SAdrian Chadd dtimperiod = ni->ni_dtim_period; 1038ba5c15d9SAdrian Chadd if (dtimperiod <= 0) /* NB: 0 if not known */ 1039ba5c15d9SAdrian Chadd dtimperiod = 1; 1040ba5c15d9SAdrian Chadd dtimcount = ni->ni_dtim_count; 1041ba5c15d9SAdrian Chadd if (dtimcount >= dtimperiod) /* NB: sanity check */ 1042ba5c15d9SAdrian Chadd dtimcount = 0; /* XXX? */ 1043ba5c15d9SAdrian Chadd cfpperiod = 1; /* NB: no PCF support yet */ 1044ba5c15d9SAdrian Chadd cfpcount = 0; 1045ba5c15d9SAdrian Chadd /* 1046ba5c15d9SAdrian Chadd * Pull nexttbtt forward to reflect the current 1047ba5c15d9SAdrian Chadd * TSF and calculate dtim+cfp state for the result. 1048ba5c15d9SAdrian Chadd */ 1049ba5c15d9SAdrian Chadd tsf = ath_hal_gettsf64(ah); 1050ba5c15d9SAdrian Chadd tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 1051f5c30c4eSAdrian Chadd 1052f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1053f5c30c4eSAdrian Chadd "%s: beacon tsf=%llu, hw tsf=%llu, nexttbtt=%u, tsftu=%u\n", 1054f5c30c4eSAdrian Chadd __func__, 1055f5c30c4eSAdrian Chadd (unsigned long long) tsf_beacon, 1056f5c30c4eSAdrian Chadd (unsigned long long) tsf, 1057f5c30c4eSAdrian Chadd nexttbtt, 1058f5c30c4eSAdrian Chadd tsftu); 1059f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1060f5c30c4eSAdrian Chadd "%s: beacon tsf=%llu, hw tsf=%llu, tsf delta=%lld\n", 1061f5c30c4eSAdrian Chadd __func__, 1062f5c30c4eSAdrian Chadd (unsigned long long) tsf_beacon, 1063f5c30c4eSAdrian Chadd (unsigned long long) tsf, 1064f5c30c4eSAdrian Chadd (long long) tsf - 1065f5c30c4eSAdrian Chadd (long long) tsf_beacon); 1066f5c30c4eSAdrian Chadd 1067f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1068f5c30c4eSAdrian Chadd "%s: nexttbtt=%llu, beacon tsf delta=%lld\n", 1069f5c30c4eSAdrian Chadd __func__, 1070f5c30c4eSAdrian Chadd (unsigned long long) nexttbtt, 1071f5c30c4eSAdrian Chadd (long long) ((long long) nexttbtt * 1024LL) - (long long) tsf_beacon); 1072f5c30c4eSAdrian Chadd 1073f5c30c4eSAdrian Chadd /* XXX cfpcount? */ 1074f5c30c4eSAdrian Chadd 1075f5c30c4eSAdrian Chadd if (nexttbtt > tsftu) { 1076f5c30c4eSAdrian Chadd uint32_t countdiff, oldtbtt, remainder; 1077f5c30c4eSAdrian Chadd 1078f5c30c4eSAdrian Chadd oldtbtt = nexttbtt; 1079f5c30c4eSAdrian Chadd remainder = (nexttbtt - tsftu) % intval; 1080f5c30c4eSAdrian Chadd nexttbtt = tsftu + remainder; 1081f5c30c4eSAdrian Chadd 1082f5c30c4eSAdrian Chadd countdiff = (oldtbtt - nexttbtt) / intval % dtimperiod; 1083f5c30c4eSAdrian Chadd if (dtimcount > countdiff) { 1084f5c30c4eSAdrian Chadd dtimcount -= countdiff; 1085f5c30c4eSAdrian Chadd } else { 1086f5c30c4eSAdrian Chadd dtimcount += dtimperiod - countdiff; 1087ba5c15d9SAdrian Chadd } 1088f5c30c4eSAdrian Chadd } else { //nexttbtt <= tsftu 1089f5c30c4eSAdrian Chadd uint32_t countdiff, oldtbtt, remainder; 1090f5c30c4eSAdrian Chadd 1091f5c30c4eSAdrian Chadd oldtbtt = nexttbtt; 1092f5c30c4eSAdrian Chadd remainder = (tsftu - nexttbtt) % intval; 1093f5c30c4eSAdrian Chadd nexttbtt = tsftu - remainder + intval; 1094f5c30c4eSAdrian Chadd countdiff = (nexttbtt - oldtbtt) / intval % dtimperiod; 1095f5c30c4eSAdrian Chadd if (dtimcount > countdiff) { 1096f5c30c4eSAdrian Chadd dtimcount -= countdiff; 1097f5c30c4eSAdrian Chadd } else { 1098f5c30c4eSAdrian Chadd dtimcount += dtimperiod - countdiff; 1099f5c30c4eSAdrian Chadd } 1100f5c30c4eSAdrian Chadd } 1101f5c30c4eSAdrian Chadd 1102f5c30c4eSAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1103f5c30c4eSAdrian Chadd "%s: adj nexttbtt=%llu, rx tsf delta=%lld\n", 1104f5c30c4eSAdrian Chadd __func__, 1105f5c30c4eSAdrian Chadd (unsigned long long) nexttbtt, 1106f5c30c4eSAdrian Chadd (long long) ((long long)nexttbtt * 1024LL) - (long long)tsf); 1107f5c30c4eSAdrian Chadd 1108ba5c15d9SAdrian Chadd memset(&bs, 0, sizeof(bs)); 1109ba5c15d9SAdrian Chadd bs.bs_intval = intval; 1110ba5c15d9SAdrian Chadd bs.bs_nexttbtt = nexttbtt; 1111ba5c15d9SAdrian Chadd bs.bs_dtimperiod = dtimperiod*intval; 1112ba5c15d9SAdrian Chadd bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; 1113ba5c15d9SAdrian Chadd bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; 1114ba5c15d9SAdrian Chadd bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; 1115ba5c15d9SAdrian Chadd bs.bs_cfpmaxduration = 0; 1116ba5c15d9SAdrian Chadd #if 0 1117ba5c15d9SAdrian Chadd /* 1118ba5c15d9SAdrian Chadd * The 802.11 layer records the offset to the DTIM 1119ba5c15d9SAdrian Chadd * bitmap while receiving beacons; use it here to 1120ba5c15d9SAdrian Chadd * enable h/w detection of our AID being marked in 1121ba5c15d9SAdrian Chadd * the bitmap vector (to indicate frames for us are 1122ba5c15d9SAdrian Chadd * pending at the AP). 1123ba5c15d9SAdrian Chadd * XXX do DTIM handling in s/w to WAR old h/w bugs 1124ba5c15d9SAdrian Chadd * XXX enable based on h/w rev for newer chips 1125ba5c15d9SAdrian Chadd */ 1126ba5c15d9SAdrian Chadd bs.bs_timoffset = ni->ni_timoff; 1127ba5c15d9SAdrian Chadd #endif 1128ba5c15d9SAdrian Chadd /* 1129ba5c15d9SAdrian Chadd * Calculate the number of consecutive beacons to miss 1130ba5c15d9SAdrian Chadd * before taking a BMISS interrupt. 1131ba5c15d9SAdrian Chadd * Note that we clamp the result to at most 10 beacons. 1132ba5c15d9SAdrian Chadd */ 1133ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = vap->iv_bmissthreshold; 1134ba5c15d9SAdrian Chadd if (bs.bs_bmissthreshold > 10) 1135ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = 10; 1136ba5c15d9SAdrian Chadd else if (bs.bs_bmissthreshold <= 0) 1137ba5c15d9SAdrian Chadd bs.bs_bmissthreshold = 1; 1138ba5c15d9SAdrian Chadd 1139ba5c15d9SAdrian Chadd /* 1140ba5c15d9SAdrian Chadd * Calculate sleep duration. The configuration is 1141ba5c15d9SAdrian Chadd * given in ms. We insure a multiple of the beacon 1142ba5c15d9SAdrian Chadd * period is used. Also, if the sleep duration is 1143ba5c15d9SAdrian Chadd * greater than the DTIM period then it makes senses 1144ba5c15d9SAdrian Chadd * to make it a multiple of that. 1145ba5c15d9SAdrian Chadd * 1146ba5c15d9SAdrian Chadd * XXX fixed at 100ms 1147ba5c15d9SAdrian Chadd */ 1148ba5c15d9SAdrian Chadd bs.bs_sleepduration = 1149ba5c15d9SAdrian Chadd roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval); 1150ba5c15d9SAdrian Chadd if (bs.bs_sleepduration > bs.bs_dtimperiod) 1151ba5c15d9SAdrian Chadd bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); 1152ba5c15d9SAdrian Chadd 1153ba5c15d9SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1154f5c30c4eSAdrian Chadd "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u " 1155f5c30c4eSAdrian Chadd "nextdtim %u bmiss %u sleep %u cfp:period %u " 1156f5c30c4eSAdrian Chadd "maxdur %u next %u timoffset %u\n" 1157ba5c15d9SAdrian Chadd , __func__ 1158f5c30c4eSAdrian Chadd , tsf 1159f5c30c4eSAdrian Chadd , tsftu 1160ba5c15d9SAdrian Chadd , bs.bs_intval 1161ba5c15d9SAdrian Chadd , bs.bs_nexttbtt 1162ba5c15d9SAdrian Chadd , bs.bs_dtimperiod 1163ba5c15d9SAdrian Chadd , bs.bs_nextdtim 1164ba5c15d9SAdrian Chadd , bs.bs_bmissthreshold 1165ba5c15d9SAdrian Chadd , bs.bs_sleepduration 1166ba5c15d9SAdrian Chadd , bs.bs_cfpperiod 1167ba5c15d9SAdrian Chadd , bs.bs_cfpmaxduration 1168ba5c15d9SAdrian Chadd , bs.bs_cfpnext 1169ba5c15d9SAdrian Chadd , bs.bs_timoffset 1170ba5c15d9SAdrian Chadd ); 1171ba5c15d9SAdrian Chadd ath_hal_intrset(ah, 0); 1172ba5c15d9SAdrian Chadd ath_hal_beacontimers(ah, &bs); 1173ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_BMISS; 1174ba5c15d9SAdrian Chadd ath_hal_intrset(ah, sc->sc_imask); 1175ba5c15d9SAdrian Chadd } else { 1176ba5c15d9SAdrian Chadd ath_hal_intrset(ah, 0); 1177ba5c15d9SAdrian Chadd if (nexttbtt == intval) 1178ba5c15d9SAdrian Chadd intval |= HAL_BEACON_RESET_TSF; 1179ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_IBSS) { 1180ba5c15d9SAdrian Chadd /* 1181ba5c15d9SAdrian Chadd * In IBSS mode enable the beacon timers but only 1182ba5c15d9SAdrian Chadd * enable SWBA interrupts if we need to manually 1183ba5c15d9SAdrian Chadd * prepare beacon frames. Otherwise we use a 1184ba5c15d9SAdrian Chadd * self-linked tx descriptor and let the hardware 1185ba5c15d9SAdrian Chadd * deal with things. 1186ba5c15d9SAdrian Chadd */ 1187ba5c15d9SAdrian Chadd intval |= HAL_BEACON_ENA; 1188ba5c15d9SAdrian Chadd if (!sc->sc_hasveol) 1189ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_SWBA; 1190ba5c15d9SAdrian Chadd if ((intval & HAL_BEACON_RESET_TSF) == 0) { 1191ba5c15d9SAdrian Chadd /* 1192ba5c15d9SAdrian Chadd * Pull nexttbtt forward to reflect 1193ba5c15d9SAdrian Chadd * the current TSF. 1194ba5c15d9SAdrian Chadd */ 1195ba5c15d9SAdrian Chadd tsf = ath_hal_gettsf64(ah); 1196ba5c15d9SAdrian Chadd tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; 1197ba5c15d9SAdrian Chadd do { 1198ba5c15d9SAdrian Chadd nexttbtt += intval; 1199ba5c15d9SAdrian Chadd } while (nexttbtt < tsftu); 1200ba5c15d9SAdrian Chadd } 1201ba5c15d9SAdrian Chadd ath_beaconq_config(sc); 1202ba5c15d9SAdrian Chadd } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || 1203ba5c15d9SAdrian Chadd ic->ic_opmode == IEEE80211_M_MBSS) { 1204ba5c15d9SAdrian Chadd /* 1205ba5c15d9SAdrian Chadd * In AP/mesh mode we enable the beacon timers 1206ba5c15d9SAdrian Chadd * and SWBA interrupts to prepare beacon frames. 1207ba5c15d9SAdrian Chadd */ 1208ba5c15d9SAdrian Chadd intval |= HAL_BEACON_ENA; 1209ba5c15d9SAdrian Chadd sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ 1210ba5c15d9SAdrian Chadd ath_beaconq_config(sc); 1211ba5c15d9SAdrian Chadd } 1212e1252ce1SAdrian Chadd 1213e1252ce1SAdrian Chadd /* 1214e1252ce1SAdrian Chadd * Now dirty things because for now, the EDMA HAL has 1215e1252ce1SAdrian Chadd * nexttbtt and intval is TU/8. 1216e1252ce1SAdrian Chadd */ 1217e1252ce1SAdrian Chadd if (sc->sc_isedma) { 1218*f858e928SAdrian Chadd nexttbtt_u8 = (nexttbtt << 3) & HAL_BEACON_PERIOD_TU8; 1219*f858e928SAdrian Chadd intval_u8 = (intval << 3) & HAL_BEACON_PERIOD_TU8; 1220e1252ce1SAdrian Chadd if (intval & HAL_BEACON_ENA) 1221e1252ce1SAdrian Chadd intval_u8 |= HAL_BEACON_ENA; 1222e1252ce1SAdrian Chadd if (intval & HAL_BEACON_RESET_TSF) 1223e1252ce1SAdrian Chadd intval_u8 |= HAL_BEACON_RESET_TSF; 1224e1252ce1SAdrian Chadd ath_hal_beaconinit(ah, nexttbtt_u8, intval_u8); 1225e1252ce1SAdrian Chadd } else 1226ba5c15d9SAdrian Chadd ath_hal_beaconinit(ah, nexttbtt, intval); 1227ba5c15d9SAdrian Chadd sc->sc_bmisscount = 0; 1228ba5c15d9SAdrian Chadd ath_hal_intrset(ah, sc->sc_imask); 1229ba5c15d9SAdrian Chadd /* 1230ba5c15d9SAdrian Chadd * When using a self-linked beacon descriptor in 1231ba5c15d9SAdrian Chadd * ibss mode load it once here. 1232ba5c15d9SAdrian Chadd */ 1233ba5c15d9SAdrian Chadd if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) 1234ba5c15d9SAdrian Chadd ath_beacon_start_adhoc(sc, vap); 1235ba5c15d9SAdrian Chadd } 1236ba5c15d9SAdrian Chadd ieee80211_free_node(ni); 1237f5c30c4eSAdrian Chadd 1238*f858e928SAdrian Chadd tsf = ath_hal_gettsf64(ah); 1239*f858e928SAdrian Chadd DPRINTF(sc, ATH_DEBUG_BEACON, 1240*f858e928SAdrian Chadd "%s: nexttbtt %u intval %u (%u), tsf64=%llu tsfbeacon=%llu delta=%lld\n", 1241*f858e928SAdrian Chadd __func__, nexttbtt, intval, ni->ni_intval, 1242*f858e928SAdrian Chadd (unsigned long long) tsf, 1243*f858e928SAdrian Chadd (unsigned long long) tsf_beacon, 1244*f858e928SAdrian Chadd (long long) tsf - 1245*f858e928SAdrian Chadd (long long) tsf_beacon); 1246*f858e928SAdrian Chadd 1247f5c30c4eSAdrian Chadd ATH_LOCK(sc); 1248f5c30c4eSAdrian Chadd ath_power_restore_power_state(sc); 1249f5c30c4eSAdrian Chadd ATH_UNLOCK(sc); 1250ba5c15d9SAdrian Chadd #undef FUDGE 1251ba5c15d9SAdrian Chadd #undef TSF_TO_TU 1252ba5c15d9SAdrian Chadd } 1253