summaryrefslogtreecommitdiffstats
path: root/sys/dev/ath
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2009-01-08 17:12:47 +0000
committersam <sam@FreeBSD.org>2009-01-08 17:12:47 +0000
commit98ad45c3d3f257c3649d4e198723267231203184 (patch)
tree6038542738398db4d8d4a8e36ed50ac642a506ac /sys/dev/ath
parent6f4df3b74deeeb200dfc7f23a8344f50eb34942c (diff)
downloadFreeBSD-src-98ad45c3d3f257c3649d4e198723267231203184.zip
FreeBSD-src-98ad45c3d3f257c3649d4e198723267231203184.tar.gz
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
Diffstat (limited to 'sys/dev/ath')
-rw-r--r--sys/dev/ath/if_ath.c740
-rw-r--r--sys/dev/ath/if_athioctl.h10
-rw-r--r--sys/dev/ath/if_athvar.h34
3 files changed, 734 insertions, 50 deletions
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 <net/if_llc.h>
#include <net80211/ieee80211_var.h>
+#ifdef ATH_SUPPORT_TDMA
+#include <net80211/ieee80211_tdma.h>
+#endif
#include <net/bpf.h>
@@ -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) \
OpenPOWER on IntegriCloud