From 98ad45c3d3f257c3649d4e198723267231203184 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 8 Jan 2009 17:12:47 +0000 Subject: TDMA support for long distance point-to-point links using ath devices: o add net80211 support for a tdma vap that is built on top of the existing adhoc-demo support o add tdma scheduling of frame transmission to the ath driver; it's conceivable other devices might be capable of this too in which case they can make use of the 802.11 protocol additions etc. o add minor bits to user tools that need to know: ifconfig to setup and configure, new statistics in athstats, and new debug mask bits While the architecture can support >2 slots in a TDMA BSS the current design is intended (and tested) for only 2 slots. Sponsored by: Intel --- sys/dev/ath/if_ath.c | 740 +++++++++++++++++++++++++++++++++++++++++++--- sys/dev/ath/if_athioctl.h | 10 +- sys/dev/ath/if_athvar.h | 34 ++- 3 files changed, 734 insertions(+), 50 deletions(-) (limited to 'sys/dev/ath') diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 006e712..2876e0e 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,6 +68,9 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef ATH_SUPPORT_TDMA +#include +#endif #include @@ -216,6 +219,50 @@ static int ath_raw_xmit(struct ieee80211_node *, static void ath_bpfattach(struct ath_softc *); static void ath_announce(struct ath_softc *); +#ifdef ATH_SUPPORT_TDMA +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); +static void ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap); +static void ath_tdma_update(struct ieee80211_node *ni, + const struct ieee80211_tdma_param *tdma); +static void ath_tdma_beacon_send(struct ath_softc *sc, + struct ieee80211vap *vap); + +static __inline void +ath_hal_setcca(struct ath_hal *ah, int ena) +{ + /* + * NB: fill me in; this is not provided by default because disabling + * CCA in most locales violates regulatory. + */ +} + +static __inline int +ath_hal_getcca(struct ath_hal *ah) +{ + u_int32_t diag; + if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK) + return 1; + return ((diag & 0x500000) == 0); +} + +#define TDMA_EP_MULTIPLIER (1<<10) /* pow2 to optimize out * and / */ +#define TDMA_LPF_LEN 6 +#define TDMA_DUMMY_MARKER 0x127 +#define TDMA_EP_MUL(x, mul) ((x) * (mul)) +#define TDMA_IN(x) (TDMA_EP_MUL((x), TDMA_EP_MULTIPLIER)) +#define TDMA_LPF(x, y, len) \ + ((x != TDMA_DUMMY_MARKER) ? (((x) * ((len)-1) + (y)) / (len)) : (y)) +#define TDMA_SAMPLE(x, y) do { \ + x = TDMA_LPF((x), TDMA_IN(y), TDMA_LPF_LEN); \ +} while (0) +#define TDMA_EP_RND(x,mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +#define TDMA_AVG(x) TDMA_EP_RND(x, TDMA_EP_MULTIPLIER) +#endif /* ATH_SUPPORT_TDMA */ + SYSCTL_DECL(_hw_ath); /* XXX validate sysctl values */ @@ -260,6 +307,8 @@ enum { ATH_DEBUG_LED = 0x00100000, /* led management */ ATH_DEBUG_FF = 0x00200000, /* fast frames */ ATH_DEBUG_DFS = 0x00400000, /* DFS processing */ + ATH_DEBUG_TDMA = 0x00800000, /* TDMA processing */ + ATH_DEBUG_TDMA_TIMER = 0x01000000, /* TDMA timer processing */ ATH_DEBUG_REGDOMAIN = 0x02000000, /* regulatory processing */ ATH_DEBUG_FATAL = 0x80000000, /* fatal errors */ ATH_DEBUG_ANY = 0xffffffff @@ -615,7 +664,12 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) wmodes = ath_hal_getwirelessmodes(ah, ic->ic_regdomain.country); if (wmodes & (HAL_MODE_108G|HAL_MODE_TURBO)) ic->ic_caps |= IEEE80211_C_TURBOP; - +#ifdef ATH_SUPPORT_TDMA + if (ath_hal_macversion(ah) > 0x78) { + ic->ic_caps |= IEEE80211_C_TDMA; /* capable of TDMA */ + ic->ic_tdma_update = ath_tdma_update; + } +#endif /* * Indicate we need the 802.11 header padded to a * 32-bit boundary for 4-address and QoS frames. @@ -824,10 +878,9 @@ ath_vap_create(struct ieee80211com *ic, */ flags |= IEEE80211_CLONE_NOBEACONS; } - if (flags & IEEE80211_CLONE_NOBEACONS) { - sc->sc_swbmiss = 1; + if (flags & IEEE80211_CLONE_NOBEACONS) ic_opmode = IEEE80211_M_HOSTAP; - } else + else ic_opmode = opmode; break; case IEEE80211_M_IBSS: @@ -840,7 +893,13 @@ ath_vap_create(struct ieee80211com *ic, needbeacon = 1; break; case IEEE80211_M_AHDEMO: +#ifdef ATH_SUPPORT_TDMA + if (flags & IEEE80211_CLONE_TDMA) { + needbeacon = 1; + flags |= IEEE80211_CLONE_NOBEACONS; + } /* fall thru... */ +#endif case IEEE80211_M_MONITOR: if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) { /* XXX not right for monitor mode */ @@ -959,6 +1018,18 @@ ath_vap_create(struct ieee80211com *ic, sc->sc_opmode = HAL_M_STA; break; case IEEE80211_M_AHDEMO: +#ifdef ATH_SUPPORT_TDMA + if (vap->iv_caps & IEEE80211_C_TDMA) { + sc->sc_tdma = 1; + /* NB: disable tsf adjust */ + sc->sc_stagbeacons = 0; + } + /* + * NB: adhoc demo mode is a pseudo mode; to the hal it's + * just ap mode. + */ + /* fall thru... */ +#endif case IEEE80211_M_HOSTAP: sc->sc_opmode = HAL_M_HOSTAP; break; @@ -975,6 +1046,12 @@ ath_vap_create(struct ieee80211com *ic, */ ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons); } + if (flags & IEEE80211_CLONE_NOBEACONS) { + /* + * Enable s/w beacon miss handling. + */ + sc->sc_swbmiss = 1; + } ATH_UNLOCK(sc); /* complete setup */ @@ -1047,6 +1124,13 @@ ath_vap_delete(struct ieee80211vap *vap) } if (vap->iv_opmode != IEEE80211_M_WDS) sc->sc_nvaps--; +#ifdef ATH_SUPPORT_TDMA + /* TDMA operation ceases when the last vap is destroyed */ + if (sc->sc_tdma && sc->sc_nvaps == 0) { + sc->sc_tdma = 0; + sc->sc_swbmiss = 0; + } +#endif ATH_UNLOCK(sc); free(avp, M_80211_VAP); @@ -1198,7 +1282,20 @@ ath_intr(void *arg) * this is too slow to meet timing constraints * under load. */ - ath_beacon_proc(sc, 0); +#ifdef ATH_SUPPORT_TDMA + if (sc->sc_tdma) { + if (sc->sc_tdmaswba == 0) { + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = + TAILQ_FIRST(&ic->ic_vaps); + ath_tdma_beacon_send(sc, vap); + sc->sc_tdmaswba = + vap->iv_tdma->tdma_bintval; + } else + sc->sc_tdmaswba--; + } else +#endif + ath_beacon_proc(sc, 0); } if (status & HAL_INT_RXEOL) { /* @@ -1581,8 +1678,14 @@ ath_reset(struct ifnet *ifp) * might change as a result. */ ath_chan_change(sc, ic->ic_curchan); - if (sc->sc_beacons) - ath_beacon_config(sc, NULL); /* restart beacons */ + if (sc->sc_beacons) { +#ifdef ATH_SUPPORT_TDMA + if (sc->sc_tdma) + ath_tdma_config(sc, NULL); + else +#endif + ath_beacon_config(sc, NULL); /* restart beacons */ + } ath_hal_intrset(ah, sc->sc_imask); ath_start(ifp); /* restart xmit */ @@ -1688,7 +1791,7 @@ ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq, } ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); ATH_TXBUF_UNLOCK(sc); } } @@ -1829,7 +1932,7 @@ ath_ff_check(struct ath_softc *sc, struct ath_txq *txq, * Return bfstaged to the free list. */ ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bfstaged, bf_list); + STAILQ_INSERT_HEAD(&sc->sc_txbuf, bfstaged, bf_list); ATH_TXBUF_UNLOCK(sc); return m; /* ready to go */ @@ -1902,7 +2005,7 @@ ath_ff_check(struct ath_softc *sc, struct ath_txq *txq, } ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bfstaged, bf_list); + STAILQ_INSERT_HEAD(&sc->sc_txbuf, bfstaged, bf_list); ATH_TXBUF_UNLOCK(sc); } else { #if 0 @@ -1925,6 +2028,45 @@ ath_ff_check(struct ath_softc *sc, struct ath_txq *txq, return m; } +static struct ath_buf * +_ath_getbuf_locked(struct ath_softc *sc) +{ + struct ath_buf *bf; + + ATH_TXBUF_LOCK_ASSERT(sc); + + bf = STAILQ_FIRST(&sc->sc_txbuf); + if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) + STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); + else + bf = NULL; + if (bf == NULL) { + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__, + STAILQ_FIRST(&sc->sc_txbuf) == NULL ? + "out of xmit buffers" : "xmit buffer busy"); + sc->sc_stats.ast_tx_nobuf++; + } + return bf; +} + +static struct ath_buf * +ath_getbuf(struct ath_softc *sc) +{ + struct ath_buf *bf; + + ATH_TXBUF_LOCK(sc); + bf = _ath_getbuf_locked(sc); + if (bf == NULL) { + struct ifnet *ifp = sc->sc_ifp; + + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); + sc->sc_stats.ast_tx_qstop++; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + ATH_TXBUF_UNLOCK(sc); + return bf; +} + /* * Cleanup driver resources when we run out of buffers * while processing fragments; return the tx buffers @@ -1941,7 +2083,7 @@ ath_txfrag_cleanup(struct ath_softc *sc, STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) { /* NB: bf assumed clean */ STAILQ_REMOVE_HEAD(frags, bf_list); - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); ieee80211_node_decref(ni); } } @@ -1960,12 +2102,11 @@ ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags, ATH_TXBUF_LOCK(sc); for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) { - bf = STAILQ_FIRST(&sc->sc_txbuf); + bf = _ath_getbuf_locked(sc); if (bf == NULL) { /* out of buffers, cleanup */ ath_txfrag_cleanup(sc, frags, ni); break; } - STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); ieee80211_node_incref(ni); STAILQ_INSERT_TAIL(frags, bf, bf_list); } @@ -1992,23 +2133,14 @@ ath_start(struct ifnet *ifp) /* * Grab a TX buffer and associated resources. */ - ATH_TXBUF_LOCK(sc); - bf = STAILQ_FIRST(&sc->sc_txbuf); - if (bf != NULL) - STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); - ATH_TXBUF_UNLOCK(sc); - if (bf == NULL) { - DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of xmit buffers\n", - __func__); - sc->sc_stats.ast_tx_qstop++; - ifp->if_drv_flags |= IFF_DRV_OACTIVE; + bf = ath_getbuf(sc); + if (bf == NULL) break; - } IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); ATH_TXBUF_UNLOCK(sc); break; } @@ -2082,7 +2214,7 @@ ath_start(struct ifnet *ifp) bf->bf_m = NULL; bf->bf_node = NULL; ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); ath_txfrag_cleanup(sc, &frags, ni); ATH_TXBUF_UNLOCK(sc); if (ni != NULL) @@ -4215,6 +4347,9 @@ rx_accept: /* * Sending station is known, dispatch directly. */ +#ifdef ATH_SUPPORT_TDMA + sc->sc_tdmars = rs; +#endif type = ieee80211_input(ni, m, rs->rs_rssi, nf, rs->rs_tstamp); ieee80211_free_node(ni); @@ -4387,10 +4522,48 @@ ath_txq_update(struct ath_softc *sc, int ac) HAL_TXQ_INFO qi; ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi); - qi.tqi_aifs = wmep->wmep_aifsn; - qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); - qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); - qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit); +#ifdef ATH_SUPPORT_TDMA + if (sc->sc_tdma) { + /* + * AIFS is zero so there's no pre-transmit wait. The + * burst time defines the slot duration and is configured + * via sysctl. The QCU is setup to not do post-xmit + * back off, lockout all lower-priority QCU's, and fire + * off the DMA beacon alert timer which is setup based + * on the slot configuration. + */ + qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE + | HAL_TXQ_TXERRINT_ENABLE + | HAL_TXQ_TXURNINT_ENABLE + | HAL_TXQ_TXEOLINT_ENABLE + | HAL_TXQ_DBA_GATED + | HAL_TXQ_BACKOFF_DISABLE + | HAL_TXQ_ARB_LOCKOUT_GLOBAL + ; + qi.tqi_aifs = 0; + /* XXX +dbaprep? */ + qi.tqi_readyTime = sc->sc_tdmaslotlen; + qi.tqi_burstTime = qi.tqi_readyTime; + } else { +#endif + qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE + | HAL_TXQ_TXERRINT_ENABLE + | HAL_TXQ_TXDESCINT_ENABLE + | HAL_TXQ_TXURNINT_ENABLE + ; + qi.tqi_aifs = wmep->wmep_aifsn; + qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin); + qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax); + qi.tqi_readyTime = 0; + qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit); +#ifdef ATH_SUPPORT_TDMA + } +#endif + + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: Q%u qflags 0x%x aifs %u cwmin %u cwmax %u burstTime %u\n", + __func__, txq->axq_qnum, qi.tqi_qflags, + qi.tqi_aifs, qi.tqi_cwmin, qi.tqi_cwmax, qi.tqi_burstTime); if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) { if_printf(ifp, "unable to update hardware queue " @@ -4570,13 +4743,71 @@ ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) * to avoid possible races. */ ATH_TXQ_LOCK(txq); + KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0, + ("busy status 0x%x", bf->bf_flags)); if (txq->axq_qnum != ATH_TXQ_SWQ) { +#ifdef ATH_SUPPORT_TDMA + int qbusy; + + ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); + qbusy = ath_hal_txqenabled(ah, txq->axq_qnum); + if (txq->axq_link == NULL) { + /* + * Be careful writing the address to TXDP. If + * the tx q is enabled then this write will be + * ignored. Normally this is not an issue but + * when tdma is in use and the q is beacon gated + * this race can occur. If the q is busy then + * defer the work to later--either when another + * packet comes along or when we prepare a beacon + * frame at SWBA. + */ + if (!qbusy) { + ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); + txq->axq_flags &= ~ATH_TXQ_PUTPENDING; + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: TXDP[%u] = %p (%p) depth %d\n", + __func__, txq->axq_qnum, + (caddr_t)bf->bf_daddr, bf->bf_desc, + txq->axq_depth); + } else { + txq->axq_flags |= ATH_TXQ_PUTPENDING; + DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT, + "%s: Q%u busy, defer enable\n", __func__, + txq->axq_qnum); + } + } else { + *txq->axq_link = bf->bf_daddr; + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: link[%u](%p)=%p (%p) depth %d\n", __func__, + txq->axq_qnum, txq->axq_link, + (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); + if ((txq->axq_flags & ATH_TXQ_PUTPENDING) && !qbusy) { + /* + * The q was busy when we previously tried + * to write the address of the first buffer + * in the chain. Since it's not busy now + * handle this chore. We are certain the + * buffer at the front is the right one since + * axq_link is NULL only when the buffer list + * is/was empty. + */ + ath_hal_puttxbuf(ah, txq->axq_qnum, + STAILQ_FIRST(&txq->axq_q)->bf_daddr); + txq->axq_flags &= ~ATH_TXQ_PUTPENDING; + DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT, + "%s: Q%u restarted\n", __func__, + txq->axq_qnum); + } + } +#else ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); if (txq->axq_link == NULL) { ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: TXDP[%u] = %p (%p) depth %d\n", __func__, - txq->axq_qnum, (caddr_t)bf->bf_daddr, bf->bf_desc, + "%s: TXDP[%u] = %p (%p) depth %d\n", + __func__, txq->axq_qnum, + (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); } else { *txq->axq_link = bf->bf_daddr; @@ -4585,6 +4816,7 @@ ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf) txq->axq_qnum, txq->axq_link, (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth); } +#endif /* ATH_SUPPORT_TDMA */ txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link; ath_hal_txstart(ah, txq->axq_qnum); } else { @@ -4819,6 +5051,15 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf } if (flags & HAL_TXDESC_NOACK) /* NB: avoid double counting */ sc->sc_stats.ast_tx_noack++; +#ifdef ATH_SUPPORT_TDMA + if (sc->sc_tdma && (flags & HAL_TXDESC_NOACK) == 0) { + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: ACK required w/ TDMA\n", + __func__); + /* XXX statistic */ + ath_freetx(m0); + return EIO; + } +#endif /* * If 802.11g protection is enabled, determine whether @@ -5017,7 +5258,7 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) struct ath_hal *ah = sc->sc_ah; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct ath_buf *bf; + struct ath_buf *bf, *last; struct ath_desc *ds, *ds0; struct ath_tx_status *ts; struct ieee80211_node *ni; @@ -5052,7 +5293,18 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) break; } ATH_TXQ_REMOVE_HEAD(txq, bf_list); +#ifdef ATH_SUPPORT_TDMA + if (txq->axq_depth > 0) { + /* + * More frames follow. Mark the buffer busy + * so it's not re-used while the hardware may + * still re-read the link field in the descriptor. + */ + bf->bf_flags |= ATH_BUF_BUSY; + } else +#else if (txq->axq_depth == 0) +#endif txq->axq_link = NULL; ATH_TXQ_UNLOCK(txq); @@ -5128,6 +5380,9 @@ ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) bf->bf_node = NULL; ATH_TXBUF_LOCK(sc); + last = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list); + if (last != NULL) + last->bf_flags &= ~ATH_BUF_BUSY; STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); ATH_TXBUF_UNLOCK(sc); } @@ -5250,6 +5505,11 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) * NB: this assumes output has been stopped and * we do not need to block ath_tx_proc */ + ATH_TXBUF_LOCK(sc); + bf = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list); + if (bf != NULL) + bf->bf_flags &= ~ATH_BUF_BUSY; + ATH_TXBUF_UNLOCK(sc); for (ix = 0;; ix++) { ATH_TXQ_LOCK(txq); bf = STAILQ_FIRST(&txq->axq_q); @@ -5284,6 +5544,7 @@ ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) } m_freem(bf->bf_m); bf->bf_m = NULL; + bf->bf_flags &= ~ATH_BUF_BUSY; ATH_TXBUF_LOCK(sc); STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); @@ -5760,6 +6021,12 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) ni->ni_capinfo, ieee80211_chan2ieee(ic, ic->ic_curchan)); switch (vap->iv_opmode) { +#ifdef ATH_SUPPORT_TDMA + case IEEE80211_M_AHDEMO: + if ((vap->iv_caps & IEEE80211_C_TDMA) == 0) + break; + /* fall thru... */ +#endif case IEEE80211_M_HOSTAP: case IEEE80211_M_IBSS: /* @@ -5788,7 +6055,12 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) ni->ni_tstamp.tsf != 0) { sc->sc_syncbeacon = 1; } else if (!sc->sc_beacons) { - ath_beacon_config(sc, vap); +#ifdef ATH_SUPPORT_TDMA + if (vap->iv_caps & IEEE80211_C_TDMA) + ath_tdma_config(sc, vap); + else +#endif + ath_beacon_config(sc, vap); sc->sc_beacons = 1; } break; @@ -5851,6 +6123,9 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) taskqueue_block(sc->sc_tq); sc->sc_beacons = 0; } +#ifdef ATH_SUPPORT_TDMA + ath_hal_setcca(ah, AH_TRUE); +#endif } bad: return error; @@ -6500,6 +6775,10 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) sc->sc_stats.ast_rx_packets = ifp->if_ipackets; sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); +#ifdef ATH_SUPPORT_TDMA + sc->sc_stats.ast_tdma_tsfadjp = TDMA_AVG(sc->sc_avgtsfdeltap); + sc->sc_stats.ast_tdma_tsfadjm = TDMA_AVG(sc->sc_avgtsfdeltam); +#endif rt = sc->sc_currates; /* XXX HT rates */ sc->sc_stats.ast_tx_rate = @@ -6876,6 +7155,24 @@ ath_sysctlattach(struct ath_softc *sc) SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "monpass", CTLFLAG_RW, &sc->sc_monpass, 0, "mask of error frames to pass when monitoring"); +#ifdef ATH_SUPPORT_TDMA + if (ath_hal_macversion(ah) > 0x78) { + sc->sc_tdmadbaprep = 2; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "dbaprep", CTLFLAG_RW, &sc->sc_tdmadbaprep, 0, + "TDMA DBA preparation time"); + sc->sc_tdmaswbaprep = 10; + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "swbaprep", CTLFLAG_RW, &sc->sc_tdmaswbaprep, 0, + "TDMA SWBA preparation time"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "guardtime", CTLFLAG_RW, &sc->sc_tdmaguard, 0, + "TDMA slot guard time"); + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "superframe", CTLFLAG_RD, &sc->sc_tdmabintval, 0, + "TDMA calculated super frame"); + } +#endif } static void @@ -7123,16 +7420,8 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, /* * Grab a TX buffer and associated resources. */ - ATH_TXBUF_LOCK(sc); - bf = STAILQ_FIRST(&sc->sc_txbuf); - if (bf != NULL) - STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); - ATH_TXBUF_UNLOCK(sc); + bf = ath_getbuf(sc); if (bf == NULL) { - DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of xmit buffers\n", - __func__); - sc->sc_stats.ast_tx_qstop++; - ifp->if_drv_flags |= IFF_DRV_OACTIVE; ieee80211_free_node(ni); m_freem(m); return ENOBUFS; @@ -7162,7 +7451,7 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, bad: ifp->if_oerrors++; ATH_TXBUF_LOCK(sc); - STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list); ATH_TXBUF_UNLOCK(sc); ieee80211_free_node(ni); return EIO; /* XXX */ @@ -7220,3 +7509,364 @@ ath_announce(struct ath_softc *sc) if_printf(ifp, "using %u tx buffers\n", ath_txbuf); #undef HAL_MODE_DUALBAND } + +#ifdef ATH_SUPPORT_TDMA +static __inline uint32_t +ath_hal_getnexttbtt(struct ath_hal *ah) +{ +#define AR_TIMER0 0x8028 + return OS_REG_READ(ah, AR_TIMER0); +} + +static __inline void +ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta) +{ + /* XXX handle wrap/overflow */ + OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta); +} + +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; + ath_hal_beaconsettimers(ah, &bt); +} + +/* + * Calculate the beacon interval. This is periodic in the + * superframe for the bss. We assume each station is configured + * identically wrt transmit rate so the guard time we calculate + * above will be the same on all stations. Note we need to + * factor in the xmit time because the hardware will schedule + * a frame for transmit if the start of the frame is within + * the burst time. When we get hardware that properly kills + * frames in the PCU we can reduce/eliminate the guard time. + * + * Roundup to 1024 is so we have 1 TU buffer in the guard time + * to deal with the granularity of the nexttbtt timer. 11n MAC's + * with 1us timer granularity should allow us to reduce/eliminate + * this. + */ +static void +ath_tdma_bintvalsetup(struct ath_softc *sc, + const struct ieee80211_tdma_state *tdma) +{ + /* copy from vap state (XXX check all vaps have same value?) */ + sc->sc_tdmaslotlen = tdma->tdma_slotlen; + sc->sc_tdmabintcnt = tdma->tdma_bintval; + + sc->sc_tdmabintval = roundup((sc->sc_tdmaslotlen+sc->sc_tdmaguard) * + tdma->tdma_slotcnt, 1024); + sc->sc_tdmabintval >>= 10; /* TSF -> TU */ + if (sc->sc_tdmabintval & 1) + sc->sc_tdmabintval++; + + if (tdma->tdma_slot == 0) { + /* + * Only slot 0 beacons; other slots respond. + */ + sc->sc_imask |= HAL_INT_SWBA; + sc->sc_tdmaswba = 0; /* beacon immediately */ + } else { + /* XXX all vaps must be slot 0 or slot !0 */ + sc->sc_imask &= ~HAL_INT_SWBA; + } +} + +/* + * Max 802.11 overhead. This assumes no 4-address frames and + * the encapsulation done by ieee80211_encap (llc). We also + * include potential crypto overhead. + */ +#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) + +/* + * Setup initially for tdma operation. Start the beacon + * timers and enable SWBA if we are slot 0. Otherwise + * we wait for slot 0 to arrive so we can sync up before + * starting to transmit. + */ +static void +ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap) +{ + struct ath_hal *ah = sc->sc_ah; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + const struct ieee80211_txparam *tp; + const struct ieee80211_tdma_state *tdma = NULL; + int rix; + + if (vap == NULL) { + vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ + if (vap == NULL) { + if_printf(ifp, "%s: no vaps?\n", __func__); + return; + } + } + tp = vap->iv_bss->ni_txparms; + /* + * Calculate the guard time for each slot. This is the + * time to send a maximal-size frame according to the + * fixed/lowest transmit rate. Note that the interface + * mtu does not include the 802.11 overhead so we must + * tack that on (ath_hal_computetxtime includes the + * preamble and plcp in it's calculation). + */ + tdma = vap->iv_tdma; + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rix = ath_tx_findrix(sc->sc_currates, tp->ucastrate); + else + rix = ath_tx_findrix(sc->sc_currates, tp->mcastrate); + /* XXX short preamble assumed */ + sc->sc_tdmaguard = ath_hal_computetxtime(ah, sc->sc_currates, + ifp->if_mtu + IEEE80211_MAXOVERHEAD, rix, AH_TRUE); + + ath_hal_intrset(ah, 0); + + ath_beaconq_config(sc); /* setup h/w beacon q */ + ath_hal_setcca(ah, AH_FALSE); /* disable CCA */ + ath_tdma_bintvalsetup(sc, tdma); /* calculate beacon interval */ + 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); +} + +/* + * Update tdma operation. Called from the 802.11 layer + * when a beacon is received from the TDMA station operating + * in the slot immediately preceding us in the bss. Use + * the rx timestamp for the beacon frame to update our + * beacon timers so we follow their schedule. Note that + * by using the rx timestamp we implicitly include the + * propagation delay in our schedule. + */ +static void +ath_tdma_update(struct ieee80211_node *ni, + const struct ieee80211_tdma_param *tdma) +{ +#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_ifp->if_softc; + struct ath_hal *ah = sc->sc_ah; + const HAL_RATE_TABLE *rt = sc->sc_currates; + u_int64_t tsf, rstamp, nextslot; + u_int32_t txtime, nextslottu, timer0; + int32_t tudelta, tsfdelta; + const struct ath_rx_status *rs; + int rix; + + sc->sc_stats.ast_tdma_update++; + + /* + * Check for and adopt configuration changes. + */ + if (isset(ATH_VAP(vap)->av_boff.bo_flags, IEEE80211_BEACON_TDMA)) { + const struct ieee80211_tdma_state *ts = vap->iv_tdma; + + ath_tdma_bintvalsetup(sc, ts); + + 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_beaconq_config(sc); + /* XXX right? */ + ath_hal_intrset(ah, sc->sc_imask); + /* NB: beacon timers programmed below */ + } + + /* extend rx timestamp to 64 bits */ + tsf = ath_hal_gettsf64(ah); + rstamp = ath_extend_tsf(ni->ni_rstamp, tsf); + /* + * The rx timestamp is set by the hardware on completing + * reception (at the point where the rx descriptor is DMA'd + * to the host). To find the start of our next slot we + * must adjust this time by the time required to send + * the packet just received. + */ + rs = sc->sc_tdmars; + rix = rt->rateCodeToIndex[rs->rs_rate]; + txtime = ath_hal_computetxtime(ah, rt, rs->rs_datalen, rix, + rt->info[rix].shortPreamble); + /* NB: << 9 is to cvt to TU and /2 */ + nextslot = (rstamp - txtime) + (sc->sc_tdmabintval << 9); + nextslottu = TSF_TO_TU(nextslot>>32, nextslot) & HAL_BEACON_PERIOD; + + /* + * TIMER0 is the h/w's idea of NextTBTT (in TU's). Convert + * to usecs and calculate the difference between what the + * other station thinks and what we have programmed. This + * lets us figure how to adjust our timers to match. The + * adjustments are done by pulling the TSF forward and possibly + * rewriting the beacon timers. + */ + timer0 = ath_hal_getnexttbtt(ah); + tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD+1)) - TU_TO_TSF(timer0)); + + DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, + "tsfdelta %d avg +%d/-%d\n", 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 - timer0; + + /* + * Copy sender's timetstamp into tdma ie so they can + * calculate roundtrip time. We submit a beacon frame + * below after any timer adjustment. The frame goes out + * at the next TBTT so the sender can calculate the + * roundtrip by inspecting the tdma ie in our beacon frame. + * + * NB: This tstamp is subtlely preserved when + * IEEE80211_BEACON_TDMA is marked (e.g. when the + * slot position changes) because ieee80211_add_tdma + * skips over the data. + */ + memcpy(ATH_VAP(vap)->av_boff.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 timer0 %u (%d)\n", + (unsigned long long) tsf, (unsigned long long) nextslot, + (int)(nextslot - tsf), tsfdelta, + nextslottu, timer0, tudelta); +#endif + /* + * Adjust the beacon timers only when pulling them forward + * or when going back by less than the beacon interval. + * Negative jumps larger than the beacon interval seem to + * cause the timers to stop and generally cause instability. + * This basically filters out jumps due to missed beacons. + */ + if (tudelta != 0 && (tudelta > 0 || -tudelta < sc->sc_tdmabintval)) { + ath_tdma_settimers(sc, nextslottu, sc->sc_tdmabintval); + sc->sc_stats.ast_tdma_timers++; + } + if (tsfdelta > 0) { + ath_hal_adjusttsf(ah, tsfdelta); + sc->sc_stats.ast_tdma_tsf++; + } + ath_tdma_beacon_send(sc, vap); /* prepare response */ +#undef TU_TO_TSF +#undef TSF_TO_TU +} + +/* + * Transmit a beacon frame at SWBA. Dynamic updates + * to the frame contents are done as needed. + */ +static 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; + + /* + * Check if the previous beacon has gone out. If + * not don't try to post another, skip this period + * and wait for the next. Missed beacons indicate + * a problem and should not occur. If we miss too + * many consecutive beacons reset the device. + */ + 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 > 3) /* NB: 3 is a guess */ + 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; + } + + /* + * Check recent per-antenna transmit statistics and flip + * the default antenna if noticeably more frames went out + * on the non-default antenna. + * XXX assumes 2 anntenae + */ + 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); + if (bf != NULL) { + /* + * Stop any current dma and put the new frame on the queue. + * This should never fail since we check above that no frames + * are still pending on the queue. + */ + if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: beacon queue %u did not stop?\n", + __func__, sc->sc_bhalq); + /* NB: the HAL still stops DMA, so proceed */ + } + ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); + ath_hal_txstart(ah, sc->sc_bhalq); + + sc->sc_stats.ast_be_xmit++; /* XXX per-vap? */ + + /* + * Record local TSF for our last send for use + * in arbitrating slot collisions. + */ + vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah); + } +} +#endif /* ATH_SUPPORT_TDMA */ diff --git a/sys/dev/ath/if_athioctl.h b/sys/dev/ath/if_athioctl.h index 1c737a4..096f306 100644 --- a/sys/dev/ath/if_athioctl.h +++ b/sys/dev/ath/if_athioctl.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -109,7 +109,13 @@ struct ath_stats { u_int32_t ast_ff_flush; /* fast frames flushed from staging q */ u_int32_t ast_tx_qfull; /* tx dropped 'cuz of queue limit */ int8_t ast_rx_noise; /* rx noise floor */ - u_int32_t ast_pad[22]; + u_int32_t ast_tx_nobuf; /* tx dropped 'cuz no ath buffer */ + u_int32_t ast_tdma_update;/* TDMA slot timing updates */ + u_int32_t ast_tdma_timers;/* TDMA slot update set beacon timers */ + u_int32_t ast_tdma_tsf; /* TDMA slot update set TSF */ + u_int16_t ast_tdma_tsfadjp;/* TDMA slot adjust+ (usec, smoothed)*/ + u_int16_t ast_tdma_tsfadjm;/* TDMA slot adjust- (usec, smoothed)*/ + u_int32_t ast_pad[17]; }; #define SIOCGATHSTATS _IOWR('i', 137, struct ifreq) diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h index 0f08eec..7b74c47 100644 --- a/sys/dev/ath/if_athvar.h +++ b/sys/dev/ath/if_athvar.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -109,7 +109,8 @@ struct ath_buf { TAILQ_ENTRY(ath_buf) bf_stagelist; /* stage queue list */ u_int32_t bf_age; /* age when placed on stageq */ int bf_nseg; - int bf_txflags; /* tx descriptor flags */ + uint16_t bf_txflags; /* tx descriptor flags */ + uint16_t bf_flags; /* status flags (below) */ struct ath_desc *bf_desc; /* virtual addr of desc */ struct ath_desc_status bf_status; /* tx/rx status */ bus_addr_t bf_daddr; /* physical addr of desc */ @@ -122,6 +123,8 @@ struct ath_buf { }; typedef STAILQ_HEAD(, ath_buf) ath_bufhead; +#define ATH_BUF_BUSY 0x00000002 /* (tx) desc owned by h/w */ + /* * DMA state for tx/rx descriptors. */ @@ -148,6 +151,8 @@ struct ath_descdma { struct ath_txq { u_int axq_qnum; /* hardware q number */ #define ATH_TXQ_SWQ (HAL_NUM_TX_QUEUES+1) /* qnum for s/w only queue */ + u_int axq_flags; +#define ATH_TXQ_PUTPENDING 0x0001 /* ath_hal_puttxbuf pending */ u_int axq_depth; /* queue depth (stat only) */ u_int axq_intrcnt; /* interrupt count */ u_int32_t *axq_link; /* link ptr in last TX desc */ @@ -249,6 +254,7 @@ struct ath_softc { sc_stagbeacons:1,/* use staggered beacons */ sc_wmetkipmic:1,/* can do WME+TKIP MIC */ sc_resume_up: 1,/* on resume, start all vaps */ + sc_tdma : 1,/* TDMA in use */ sc_resetcal : 1;/* reset cal state next trip */ uint32_t sc_eerd; /* regdomain from EEPROM */ uint32_t sc_eecc; /* country code from EEPROM */ @@ -338,6 +344,18 @@ struct ath_softc { int sc_lastlongcal; /* last long cal completed */ int sc_lastcalreset;/* last cal reset done */ HAL_NODE_STATS sc_halstats; /* station-mode rssi stats */ +#ifdef ATH_SUPPORT_TDMA + u_int sc_tdmadbaprep; /* TDMA DBA prep time */ + u_int sc_tdmaswbaprep;/* TDMA SWBA prep time */ + u_int sc_tdmaswba; /* TDMA SWBA counter */ + u_int32_t sc_tdmabintval; /* TDMA beacon interval (TU) */ + u_int32_t sc_tdmaguard; /* TDMA guard time (usec) */ + u_int sc_tdmaslotlen; /* TDMA slot length (usec) */ + u_int sc_tdmabintcnt; /* TDMA beacon intvl (slots) */ + struct ath_rx_status *sc_tdmars; /* TDMA status of last rx */ + u_int32_t sc_avgtsfdeltap;/* TDMA slot adjust (+) */ + u_int32_t sc_avgtsfdeltam;/* TDMA slot adjust (-) */ +#endif }; #define ATH_LOCK_INIT(_sc) \ @@ -375,6 +393,8 @@ void ath_intr(void *); ((*(_ah)->ah_detach)((_ah))) #define ath_hal_reset(_ah, _opmode, _chan, _outdoor, _pstatus) \ ((*(_ah)->ah_reset)((_ah), (_opmode), (_chan), (_outdoor), (_pstatus))) +#define ath_hal_macversion(_ah) \ + (((_ah)->ah_macVersion << 4) | ((_ah)->ah_macRev)) #define ath_hal_getratetable(_ah, _mode) \ ((*(_ah)->ah_getRateTable)((_ah), (_mode))) #define ath_hal_getmac(_ah, _mac) \ @@ -417,8 +437,10 @@ void ath_intr(void *); ((*(_ah)->ah_waitForBeaconDone)((_ah), (_bf)->bf_daddr)) #define ath_hal_putrxbuf(_ah, _bufaddr) \ ((*(_ah)->ah_setRxDP)((_ah), (_bufaddr))) +/* NB: common across all chips */ +#define AR_TSF_L32 0x804c /* MAC local clock lower 32 bits */ #define ath_hal_gettsf32(_ah) \ - ((*(_ah)->ah_getTsf32)((_ah))) + OS_REG_READ(_ah, AR_TSF_L32) #define ath_hal_gettsf64(_ah) \ ((*(_ah)->ah_getTsf64)((_ah))) #define ath_hal_resettsf(_ah) \ @@ -455,6 +477,8 @@ void ath_intr(void *); ((*(_ah)->ah_beaconInit)((_ah), (_nextb), (_bperiod))) #define ath_hal_beaconreset(_ah) \ ((*(_ah)->ah_resetStationBeaconTimers)((_ah))) +#define ath_hal_beaconsettimers(_ah, _bt) \ + ((*(_ah)->ah_setBeaconTimers)((_ah), (_bt))) #define ath_hal_beacontimers(_ah, _bs) \ ((*(_ah)->ah_setStationBeaconTimers)((_ah), (_bs))) #define ath_hal_setassocid(_ah, _bss, _associd) \ @@ -486,6 +510,10 @@ void ath_intr(void *); ((*(_ah)->ah_getTxQueueProps)((_ah), (_q), (_qi))) #define ath_hal_settxqueueprops(_ah, _q, _qi) \ ((*(_ah)->ah_setTxQueueProps)((_ah), (_q), (_qi))) +/* NB: common across all chips */ +#define AR_Q_TXE 0x0840 /* MAC Transmit Queue enable */ +#define ath_hal_txqenabled(_ah, _qnum) \ + (OS_REG_READ(_ah, AR_Q_TXE) & (1<<(_qnum))) #define ath_hal_getrfgain(_ah) \ ((*(_ah)->ah_getRfGain)((_ah))) #define ath_hal_getdefantenna(_ah) \ -- cgit v1.1