#include <sys/cdefs.h>
#include "opt_inet.h"
#include "opt_ath.h"
#include "opt_ah.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/errno.h>
#include <sys/callout.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kthread.h>
#include <sys/taskqueue.h>
#include <sys/priv.h>
#include <sys/module.h>
#include <sys/ktr.h>
#include <sys/smp.h>
#include <machine/bus.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_llc.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
#ifdef IEEE80211_SUPPORT_SUPERG
#include <net80211/ieee80211_superg.h>
#endif
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
#include <net/bpf.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <dev/ath/if_athvar.h>
#include <dev/ath/ath_hal/ah_devid.h>
#include <dev/ath/ath_hal/ah_diagcodes.h>
#include <dev/ath/if_ath_debug.h>
#include <dev/ath/if_ath_misc.h>
#include <dev/ath/if_ath_tsf.h>
#include <dev/ath/if_ath_tx.h>
#include <dev/ath/if_ath_sysctl.h>
#include <dev/ath/if_ath_led.h>
#include <dev/ath/if_ath_keycache.h>
#include <dev/ath/if_ath_rx.h>
#include <dev/ath/if_ath_beacon.h>
#include <dev/ath/if_athdfs.h>
#ifdef ATH_TX99_DIAG
#include <dev/ath/ath_tx99/ath_tx99.h>
#endif
#ifdef ATH_DEBUG_ALQ
#include <dev/ath/if_ath_alq.h>
#endif
#ifdef IEEE80211_SUPPORT_TDMA
#include <dev/ath/if_ath_tdma.h>
static void ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt,
u_int32_t bintval);
static void ath_tdma_bintvalsetup(struct ath_softc *sc,
const struct ieee80211_tdma_state *tdma);
#endif
#ifdef IEEE80211_SUPPORT_TDMA
static void
ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, u_int32_t bintval)
{
struct ath_hal *ah = sc->sc_ah;
HAL_BEACON_TIMERS bt;
bt.bt_intval = bintval | HAL_BEACON_ENA;
bt.bt_nexttbtt = nexttbtt;
bt.bt_nextdba = (nexttbtt<<3) - sc->sc_tdmadbaprep;
bt.bt_nextswba = (nexttbtt<<3) - sc->sc_tdmaswbaprep;
bt.bt_nextatim = nexttbtt+1;
bt.bt_flags = 0;
#if 0
DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
"%s: intval=%d (0x%08x) nexttbtt=%u (0x%08x), nextdba=%u (0x%08x), nextswba=%u (0x%08x),nextatim=%u (0x%08x)\n",
__func__,
bt.bt_intval,
bt.bt_intval,
bt.bt_nexttbtt,
bt.bt_nexttbtt,
bt.bt_nextdba,
bt.bt_nextdba,
bt.bt_nextswba,
bt.bt_nextswba,
bt.bt_nextatim,
bt.bt_nextatim);
#endif
#ifdef ATH_DEBUG_ALQ
if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_SET)) {
struct if_ath_alq_tdma_timer_set t;
t.bt_intval = htobe32(bt.bt_intval);
t.bt_nexttbtt = htobe32(bt.bt_nexttbtt);
t.bt_nextdba = htobe32(bt.bt_nextdba);
t.bt_nextswba = htobe32(bt.bt_nextswba);
t.bt_nextatim = htobe32(bt.bt_nextatim);
t.bt_flags = htobe32(bt.bt_flags);
t.sc_tdmadbaprep = htobe32(sc->sc_tdmadbaprep);
t.sc_tdmaswbaprep = htobe32(sc->sc_tdmaswbaprep);
if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_SET,
sizeof(t), (char *) &t);
}
#endif
DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
"%s: nexttbtt=%u (0x%08x), nexttbtt tsf=%lld (0x%08llx)\n",
__func__,
bt.bt_nexttbtt,
bt.bt_nexttbtt,
(long long) ( ((u_int64_t) (bt.bt_nexttbtt)) << 10),
(long long) ( ((u_int64_t) (bt.bt_nexttbtt)) << 10));
ath_hal_beaconsettimers(ah, &bt);
}
static void
ath_tdma_bintvalsetup(struct ath_softc *sc,
const struct ieee80211_tdma_state *tdma)
{
sc->sc_tdmaslotlen = tdma->tdma_slotlen;
sc->sc_tdmabintval = roundup((sc->sc_tdmaslotlen+sc->sc_tdmaguard) *
tdma->tdma_slotcnt, 1024);
sc->sc_tdmabintval >>= 10;
if (sc->sc_tdmabintval & 1)
sc->sc_tdmabintval++;
if (tdma->tdma_slot == 0) {
sc->sc_imask |= HAL_INT_SWBA;
sc->sc_tdmaswba = 0;
} else {
sc->sc_imask &= ~HAL_INT_SWBA;
}
}
#define IEEE80211_MAXOVERHEAD \
(sizeof(struct ieee80211_qosframe) \
+ sizeof(struct llc) \
+ IEEE80211_ADDR_LEN \
+ IEEE80211_WEP_IVLEN \
+ IEEE80211_WEP_KIDLEN \
+ IEEE80211_WEP_CRCLEN \
+ IEEE80211_WEP_MICLEN \
+ IEEE80211_CRC_LEN)
void
ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap)
{
struct ath_hal *ah = sc->sc_ah;
struct ieee80211com *ic = &sc->sc_ic;
const struct ieee80211_txparam *tp;
const struct ieee80211_tdma_state *tdma = NULL;
int rix;
if (vap == NULL) {
vap = TAILQ_FIRST(&ic->ic_vaps);
if (vap == NULL) {
device_printf(sc->sc_dev, "%s: no vaps?\n", __func__);
return;
}
}
tp = vap->iv_bss->ni_txparms;
tdma = vap->iv_tdma;
if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
rix = ath_tx_findrix(sc, tp->ucastrate);
else
rix = ath_tx_findrix(sc, tp->mcastrate);
if (sc->sc_hasenforcetxop) {
sc->sc_tdmaguard = 0;
} else {
sc->sc_tdmaguard = ath_hal_computetxtime(ah, sc->sc_currates,
if_getmtu(vap->iv_ifp) + IEEE80211_MAXOVERHEAD, rix, AH_TRUE,
AH_TRUE);
}
ath_hal_intrset(ah, 0);
ath_beaconq_config(sc);
if (sc->sc_setcca)
ath_hal_setcca(ah, AH_FALSE);
ath_tdma_bintvalsetup(sc, tdma);
ath_tdma_settimers(sc, sc->sc_tdmabintval,
sc->sc_tdmabintval | HAL_BEACON_RESET_TSF);
sc->sc_syncbeacon = 0;
sc->sc_avgtsfdeltap = TDMA_DUMMY_MARKER;
sc->sc_avgtsfdeltam = TDMA_DUMMY_MARKER;
ath_hal_intrset(ah, sc->sc_imask);
DPRINTF(sc, ATH_DEBUG_TDMA, "%s: slot %u len %uus cnt %u "
"bsched %u guard %uus bintval %u TU dba prep %u\n", __func__,
tdma->tdma_slot, tdma->tdma_slotlen, tdma->tdma_slotcnt,
tdma->tdma_bintval, sc->sc_tdmaguard, sc->sc_tdmabintval,
sc->sc_tdmadbaprep);
#ifdef ATH_DEBUG_ALQ
if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_CONFIG)) {
struct if_ath_alq_tdma_timer_config t;
t.tdma_slot = htobe32(tdma->tdma_slot);
t.tdma_slotlen = htobe32(tdma->tdma_slotlen);
t.tdma_slotcnt = htobe32(tdma->tdma_slotcnt);
t.tdma_bintval = htobe32(tdma->tdma_bintval);
t.tdma_guard = htobe32(sc->sc_tdmaguard);
t.tdma_scbintval = htobe32(sc->sc_tdmabintval);
t.tdma_dbaprep = htobe32(sc->sc_tdmadbaprep);
if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_CONFIG,
sizeof(t), (char *) &t);
}
#endif
}
void
ath_tdma_update(struct ieee80211_node *ni,
const struct ieee80211_tdma_param *tdma, int changed)
{
#define TSF_TO_TU(_h,_l) \
((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
#define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10)
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ath_softc *sc = ic->ic_softc;
struct ath_hal *ah = sc->sc_ah;
const HAL_RATE_TABLE *rt = sc->sc_currates;
u_int64_t tsf, rstamp, nextslot, nexttbtt, nexttbtt_full;
u_int32_t txtime, nextslottu;
int32_t tudelta, tsfdelta;
const struct ath_rx_status *rs;
int rix;
sc->sc_stats.ast_tdma_update++;
if (changed != 0) {
const struct ieee80211_tdma_state *ts = vap->iv_tdma;
ath_tdma_bintvalsetup(sc, ts);
if (changed & TDMA_UPDATE_SLOTLEN)
ath_wme_update(ic);
DPRINTF(sc, ATH_DEBUG_TDMA,
"%s: adopt slot %u slotcnt %u slotlen %u us "
"bintval %u TU\n", __func__,
ts->tdma_slot, ts->tdma_slotcnt, ts->tdma_slotlen,
sc->sc_tdmabintval);
ath_hal_intrset(ah, sc->sc_imask);
}
rs = sc->sc_lastrs;
tsf = ath_hal_gettsf64(ah);
rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf);
rix = rt->rateCodeToIndex[rs->rs_rate];
txtime = ath_hal_pkt_txtime(ah, rt, rs->rs_datalen,
rix,
!! (rs->rs_flags & HAL_RX_2040),
(rix & 0x80) ?
(! (rs->rs_flags & HAL_RX_GI)) : rt->info[rix].shortPreamble,
AH_TRUE);
nextslot = (rstamp - txtime) + (sc->sc_tdmabintval << 9);
nextslottu = TSF_TO_TU(nextslot>>32, nextslot);
nexttbtt_full = ath_hal_getnexttbtt(ah);
nexttbtt = nexttbtt_full % (TU_TO_TSF(HAL_BEACON_PERIOD + 1));
tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD + 1)) - nexttbtt);
DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
"rs->rstamp %llu rstamp %llu tsf %llu txtime %d, nextslot %llu, "
"nextslottu %d, nextslottume %d\n",
(unsigned long long) rs->rs_tstamp,
(unsigned long long) rstamp,
(unsigned long long) tsf, txtime,
(unsigned long long) nextslot,
nextslottu, TSF_TO_TU(nextslot >> 32, nextslot));
DPRINTF(sc, ATH_DEBUG_TDMA,
" beacon tstamp: %llu (0x%016llx)\n",
(unsigned long long) le64toh(ni->ni_tstamp.tsf),
(unsigned long long) le64toh(ni->ni_tstamp.tsf));
DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
"nexttbtt %llu (0x%08llx) tsfdelta %d avg +%d/-%d\n",
(unsigned long long) nexttbtt,
(long long) nexttbtt,
tsfdelta,
TDMA_AVG(sc->sc_avgtsfdeltap), TDMA_AVG(sc->sc_avgtsfdeltam));
if (tsfdelta < 0) {
TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0);
TDMA_SAMPLE(sc->sc_avgtsfdeltam, -tsfdelta);
tsfdelta = -tsfdelta % 1024;
nextslottu++;
} else if (tsfdelta > 0) {
TDMA_SAMPLE(sc->sc_avgtsfdeltap, tsfdelta);
TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0);
tsfdelta = 1024 - (tsfdelta % 1024);
nextslottu++;
} else {
TDMA_SAMPLE(sc->sc_avgtsfdeltap, 0);
TDMA_SAMPLE(sc->sc_avgtsfdeltam, 0);
}
tudelta = nextslottu - TSF_TO_TU(nexttbtt_full >> 32, nexttbtt_full);
#ifdef ATH_DEBUG_ALQ
if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_BEACON_STATE)) {
struct if_ath_alq_tdma_beacon_state t;
t.rx_tsf = htobe64(rstamp);
t.beacon_tsf = htobe64(le64toh(ni->ni_tstamp.tsf));
t.tsf64 = htobe64(tsf);
t.nextslot_tsf = htobe64(nextslot);
t.nextslot_tu = htobe32(nextslottu);
t.txtime = htobe32(txtime);
if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_BEACON_STATE,
sizeof(t), (char *) &t);
}
if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_SLOT_CALC)) {
struct if_ath_alq_tdma_slot_calc t;
t.nexttbtt = htobe64(nexttbtt_full);
t.next_slot = htobe64(nextslot);
t.tsfdelta = htobe32(tsfdelta);
t.avg_plus = htobe32(TDMA_AVG(sc->sc_avgtsfdeltap));
t.avg_minus = htobe32(TDMA_AVG(sc->sc_avgtsfdeltam));
if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_SLOT_CALC,
sizeof(t), (char *) &t);
}
#endif
memcpy(vap->iv_bcn_off.bo_tdma +
__offsetof(struct ieee80211_tdma_param, tdma_tstamp),
&ni->ni_tstamp.data, 8);
#if 0
DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
"tsf %llu nextslot %llu (%d, %d) nextslottu %u nexttbtt %llu (%d)\n",
(unsigned long long) tsf, (unsigned long long) nextslot,
(int)(nextslot - tsf), tsfdelta, nextslottu, nexttbtt, tudelta);
#endif
if (tudelta != 0 && (tudelta > 0 || -tudelta < sc->sc_tdmabintval)) {
DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
"%s: calling ath_tdma_settimers; nextslottu=%d, bintval=%d\n",
__func__,
nextslottu,
sc->sc_tdmabintval);
ath_tdma_settimers(sc, nextslottu, sc->sc_tdmabintval);
sc->sc_stats.ast_tdma_timers++;
}
if (tsfdelta > 0) {
uint64_t tsf;
tsf = ath_hal_gettsf64(ah);
ath_hal_settsf64(ah, tsf + tsfdelta);
DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
"%s: calling ath_hal_adjusttsf: TSF=%llu, tsfdelta=%d\n",
__func__,
(unsigned long long) tsf,
tsfdelta);
#ifdef ATH_DEBUG_ALQ
if (if_ath_alq_checkdebug(&sc->sc_alq,
ATH_ALQ_TDMA_TSF_ADJUST)) {
struct if_ath_alq_tdma_tsf_adjust t;
t.tsfdelta = htobe32(tsfdelta);
t.tsf64_old = htobe64(tsf);
t.tsf64_new = htobe64(tsf + tsfdelta);
if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TSF_ADJUST,
sizeof(t), (char *) &t);
}
#endif
sc->sc_stats.ast_tdma_tsf++;
}
ath_tdma_beacon_send(sc, vap);
#undef TU_TO_TSF
#undef TSF_TO_TU
}
void
ath_tdma_beacon_send(struct ath_softc *sc, struct ieee80211vap *vap)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
int otherant;
if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) {
sc->sc_bmisscount++;
DPRINTF(sc, ATH_DEBUG_BEACON,
"%s: missed %u consecutive beacons\n",
__func__, sc->sc_bmisscount);
if (sc->sc_bmisscount >= ath_bstuck_threshold)
taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask);
return;
}
if (sc->sc_bmisscount != 0) {
DPRINTF(sc, ATH_DEBUG_BEACON,
"%s: resume beacon xmit after %u misses\n",
__func__, sc->sc_bmisscount);
sc->sc_bmisscount = 0;
}
if (!sc->sc_diversity) {
otherant = sc->sc_defant & 1 ? 2 : 1;
if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2)
ath_setdefantenna(sc, otherant);
sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
}
bf = ath_beacon_generate(sc, vap);
ATH_TXQ_LOCK(sc->sc_cabq);
ath_beacon_cabq_start(sc);
ATH_TXQ_UNLOCK(sc->sc_cabq);
if (bf != NULL) {
if ((! sc->sc_isedma) &&
(! ath_hal_stoptxdma(ah, sc->sc_bhalq))) {
DPRINTF(sc, ATH_DEBUG_ANY,
"%s: beacon queue %u did not stop?\n",
__func__, sc->sc_bhalq);
}
ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
ath_hal_txstart(ah, sc->sc_bhalq);
sc->sc_stats.ast_be_xmit++;
vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah);
}
}
#endif