svn commit: r186904 - in head: sbin/ifconfig sys/conf sys/dev/ath
sys/net80211 tools/tools/ath/athdebug
tools/tools/ath/athstats usr.sbin/wlandebug
Sam Leffler
sam at FreeBSD.org
Thu Jan 8 09:12:51 PST 2009
Author: sam
Date: Thu Jan 8 17:12:47 2009
New Revision: 186904
URL: http://svn.freebsd.org/changeset/base/186904
Log:
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
Added:
head/sys/net80211/ieee80211_tdma.c (contents, props changed)
head/sys/net80211/ieee80211_tdma.h (contents, props changed)
Modified:
head/sbin/ifconfig/ifconfig.8
head/sbin/ifconfig/ifieee80211.c
head/sys/conf/files
head/sys/conf/options
head/sys/dev/ath/if_ath.c
head/sys/dev/ath/if_athioctl.h
head/sys/dev/ath/if_athvar.h
head/sys/net80211/ieee80211.c
head/sys/net80211/ieee80211.h
head/sys/net80211/ieee80211_adhoc.c
head/sys/net80211/ieee80211_ddb.c
head/sys/net80211/ieee80211_freebsd.c
head/sys/net80211/ieee80211_input.c
head/sys/net80211/ieee80211_input.h
head/sys/net80211/ieee80211_ioctl.c
head/sys/net80211/ieee80211_ioctl.h
head/sys/net80211/ieee80211_node.c
head/sys/net80211/ieee80211_node.h
head/sys/net80211/ieee80211_output.c
head/sys/net80211/ieee80211_proto.h
head/sys/net80211/ieee80211_scan.h
head/sys/net80211/ieee80211_scan_sta.c
head/sys/net80211/ieee80211_var.h
head/tools/tools/ath/athdebug/athdebug.c
head/tools/tools/ath/athstats/Makefile
head/tools/tools/ath/athstats/athstats.c
head/tools/tools/ath/athstats/main.c
head/usr.sbin/wlandebug/wlandebug.c
Modified: head/sbin/ifconfig/ifconfig.8
==============================================================================
--- head/sbin/ifconfig/ifconfig.8 Thu Jan 8 15:20:32 2009 (r186903)
+++ head/sbin/ifconfig/ifconfig.8 Thu Jan 8 17:12:47 2009 (r186904)
@@ -28,7 +28,7 @@
.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
.\" $FreeBSD$
.\"
-.Dd October 19, 2008
+.Dd January 7, 2009
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -610,9 +610,15 @@ is one of
(or
.Cm hostap ),
.Cm wds ,
+.Cm tdma ,
and
.Cm monitor .
The operating mode of a cloned interface cannot be changed.
+The
+.Cm tdma
+mode is actually implemented as an
+.Cm adhoc-demo
+interface with special properties.
.It Cm wlanbssid Ar bssid
The 802.11 mac address to use for the bssid.
This must be specified at create time for a legacy
@@ -1530,6 +1536,60 @@ hexadecimal when preceded by
.Ql 0x .
Additionally, the SSID may be cleared by setting it to
.Ql - .
+.It Cm tdmaslot Ar slot
+When operating with TDMA, use the specified
+.Ar slot
+configuration.
+The
+.Ar slot
+is a number between 0 and the maximum number of slots in the BSS.
+Note that a station configured as slot 0 is a master and
+will broadcast beacon frames advertising the BSS;
+stations configured to use other slots will always
+scan to locate a master before they ever transmit.
+By default
+.Cm tdmaslot
+is set to 1.
+.It Cm tdmaslotcnt Ar cnt
+When operating with TDMA, setup a BSS with
+.Ar cnt
+slots.
+The slot count may be at most 8.
+The current implementation is only tested with two stations
+(i.e. point to point applications).
+This setting is only meaningful when a station is configured as slot 0;
+other stations adopt this setting from the BSS they join.
+By default
+.Cm tdmaslotcnt
+is set to 2.
+.It Cm tdmaslotlen Ar len
+When operating with TDMA, setup a BSS such that each station has a slot
+.Ar len
+microseconds long.
+The slot length must be at least 150 microseconds (1/8 TU)
+and no more than 65 milliseconds.
+Note that setting too small a slot length may result in poor channel
+bandwidth utilization due to factors such as timer granularity and
+guard time.
+This setting is only meaningful when a station is configured as slot 0;
+other stations adopt this setting from the BSS they join.
+By default
+.Cm tdmaslotlen
+is set to 10 milliseconds.
+.It Cm tdmabintval Ar intval
+When operating with TDMA, setup a BSS such that beacons are transmitted every
+.Ar intval
+superframes to synchronize the TDMA slot timing.
+A superframe is defined as the number of slots times the slot length; e.g.
+a BSS with two slots of 10 milliseconds has a 20 millisecond superframe.
+The beacon interval may not be zero.
+A lower setting of
+.Cm tdmabintval
+causes the timers to be resynchronized more often; this can be help if
+significant timer drift is observed.
+By default
+.Cm tdmabintval
+is set to 5.
.It Cm tsn
When operating as an access point with WPA/802.11i allow legacy
stations to associate using static key WEP and open authentication.
Modified: head/sbin/ifconfig/ifieee80211.c
==============================================================================
--- head/sbin/ifconfig/ifieee80211.c Thu Jan 8 15:20:32 2009 (r186903)
+++ head/sbin/ifconfig/ifieee80211.c Thu Jan 8 17:12:47 2009 (r186904)
@@ -1711,6 +1711,30 @@ set80211rifs(const char *val, int d, int
set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
}
+static
+DECL_CMD_FUNC(set80211tdmaslot, val, d)
+{
+ set80211(s, IEEE80211_IOC_TDMA_SLOT, atoi(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211tdmaslotcnt, val, d)
+{
+ set80211(s, IEEE80211_IOC_TDMA_SLOTCNT, atoi(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211tdmaslotlen, val, d)
+{
+ set80211(s, IEEE80211_IOC_TDMA_SLOTLEN, atoi(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211tdmabintval, val, d)
+{
+ set80211(s, IEEE80211_IOC_TDMA_BINTERVAL, atoi(val), 0, NULL);
+}
+
static int
regdomain_sort(const void *a, const void *b)
{
@@ -2558,6 +2582,22 @@ printwpsie(const char *tag, const u_int8
#undef N
}
+static void
+printtdmaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
+{
+ printf("%s", tag);
+ if (verbose && ielen >= sizeof(struct ieee80211_tdma_param)) {
+ const struct ieee80211_tdma_param *tdma =
+ (const struct ieee80211_tdma_param *) ie;
+
+ /* XXX tstamp */
+ printf("<v%u slot:%u slotcnt:%u slotlen:%u bintval:%u inuse:0x%x>",
+ tdma->tdma_version, tdma->tdma_slot, tdma->tdma_slotcnt,
+ LE_READ_2(&tdma->tdma_slotlen), tdma->tdma_bintval,
+ tdma->tdma_inuse[0]);
+ }
+}
+
/*
* Copy the ssid string contents into buf, truncating to fit. If the
* ssid is entirely printable then just copy intact. Otherwise convert
@@ -2681,6 +2721,12 @@ isatherosoui(const u_int8_t *frm)
}
static __inline int
+istdmaoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((TDMA_OUI_TYPE<<24)|TDMA_OUI);
+}
+
+static __inline int
iswpsoui(const uint8_t *frm)
{
return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
@@ -2749,6 +2795,8 @@ printies(const u_int8_t *vp, int ielen,
printathie(" ATH", vp, 2+vp[1], maxcols);
else if (iswpsoui(vp))
printwpsie(" WPS", vp, 2+vp[1], maxcols);
+ else if (istdmaoui(vp))
+ printtdmaie(" TDMA", vp, 2+vp[1], maxcols);
else if (verbose)
printie(" VEN", vp, 2+vp[1], maxcols);
break;
@@ -3192,7 +3240,7 @@ list_keys(int s)
"\20\1STA\7FF\10TURBOP\11IBSS\12PMGT" \
"\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
"\21MONITOR\22DFS\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
- "\37TXFRAG"
+ "\37TXFRAG\40TDMA"
#define IEEE80211_CRYPTO_BITS \
"\20\1WEP\2TKIP\3AES\4AES_CCM\5TKIPMIC\6CKIP\12PMGT"
@@ -4263,7 +4311,16 @@ end:
}
}
- if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
+ if (opmode == IEEE80211_M_AHDEMO) {
+ if (get80211val(s, IEEE80211_IOC_TDMA_SLOT, &val) != -1)
+ LINE_CHECK("tdmaslot %u", val);
+ if (get80211val(s, IEEE80211_IOC_TDMA_SLOTCNT, &val) != -1)
+ LINE_CHECK("tdmaslotcnt %u", val);
+ if (get80211val(s, IEEE80211_IOC_TDMA_SLOTLEN, &val) != -1)
+ LINE_CHECK("tdmaslotlen %u", val);
+ if (get80211val(s, IEEE80211_IOC_TDMA_BINTERVAL, &val) != -1)
+ LINE_CHECK("tdmabintval %u", val);
+ } else if (get80211val(s, IEEE80211_IOC_BEACON_INTERVAL, &val) != -1) {
/* XXX default define not visible */
if (val != 100 || verbose)
LINE_CHECK("bintval %u", val);
@@ -4486,7 +4543,10 @@ DECL_CMD_FUNC(set80211clone_wlanmode, ar
params.icp_opmode = IEEE80211_M_WDS;
else if (iseq(arg, "monitor"))
params.icp_opmode = IEEE80211_M_MONITOR;
- else
+ else if (iseq(arg, "tdma")) {
+ params.icp_opmode = IEEE80211_M_AHDEMO;
+ params.icp_flags |= IEEE80211_CLONE_TDMA;
+ } else
errx(1, "Don't know to create %s for %s", arg, name);
clone_setcallback(wlan_create);
#undef iseq
@@ -4662,6 +4722,11 @@ static struct cmd ieee80211_cmds[] = {
/* XXX for testing */
DEF_CMD_ARG("chanswitch", set80211chanswitch),
+ DEF_CMD_ARG("tdmaslot", set80211tdmaslot),
+ DEF_CMD_ARG("tdmaslotcnt", set80211tdmaslotcnt),
+ DEF_CMD_ARG("tdmaslotlen", set80211tdmaslotlen),
+ DEF_CMD_ARG("tdmabintval", set80211tdmabintval),
+
/* vap cloning support */
DEF_CLONE_CMD_ARG("wlanaddr", set80211clone_wlanaddr),
DEF_CLONE_CMD_ARG("wlanbssid", set80211clone_wlanbssid),
Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Thu Jan 8 15:20:32 2009 (r186903)
+++ head/sys/conf/files Thu Jan 8 17:12:47 2009 (r186904)
@@ -2232,6 +2232,7 @@ net80211/ieee80211_rssadapt.c optional w
net80211/ieee80211_scan.c optional wlan
net80211/ieee80211_scan_sta.c optional wlan
net80211/ieee80211_sta.c optional wlan
+net80211/ieee80211_tdma.c optional wlan
net80211/ieee80211_wds.c optional wlan
net80211/ieee80211_xauth.c optional wlan_xauth
netatalk/aarp.c optional netatalk
Modified: head/sys/conf/options
==============================================================================
--- head/sys/conf/options Thu Jan 8 15:20:32 2009 (r186903)
+++ head/sys/conf/options Thu Jan 8 17:12:47 2009 (r186904)
@@ -741,6 +741,7 @@ ATH_TXBUF opt_ath.h
ATH_RXBUF opt_ath.h
ATH_DIAGAPI opt_ath.h
ATH_TX99_DIAG opt_ath.h
+ATH_SUPPORT_TDMA opt_ath.h
# options for the Atheros hal
AH_SUPPORT_AR5416 opt_ah.h
@@ -784,6 +785,15 @@ INTR_FILTER
IEEE80211_DEBUG opt_wlan.h
IEEE80211_DEBUG_REFCNT opt_wlan.h
IEEE80211_AMPDU_AGE opt_wlan.h
+IEEE80211_SUPPORT_TDMA opt_wlan.h
+
+# 802.11 TDMA support
+TDMA_SLOTLEN_DEFAULT opt_tdma.h
+TDMA_SLOTCNT_DEFAULT opt_tdma.h
+TDMA_BINTVAL_DEFAULT opt_tdma.h
+TDMA_TXRATE_11B_DEFAULT opt_tdma.h
+TDMA_TXRATE_11G_DEFAULT opt_tdma.h
+TDMA_TXRATE_11A_DEFAULT opt_tdma.h
# Virtualize the network stack
VIMAGE opt_global.h
Modified: head/sys/dev/ath/if_ath.c
==============================================================================
--- head/sys/dev/ath/if_ath.c Thu Jan 8 15:20:32 2009 (r186903)
+++ head/sys/dev/ath/if_ath.c Thu Jan 8 17:12:47 2009 (r186904)
@@ -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
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_s
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
}
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, struc
* 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, struc
}
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, struc
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, a
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
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, str
* 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, str
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, struc
}
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, st
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, st
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, st
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, st
* 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, st
}
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, e
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, e
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, e
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,
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,
/*
* 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,
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)
+
+/*
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-all
mailing list