From owner-svn-src-head@FreeBSD.ORG Thu Jan 8 17:12:48 2009 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 28BB01065883; Thu, 8 Jan 2009 17:12:48 +0000 (UTC) (envelope-from sam@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 10DD78FC30; Thu, 8 Jan 2009 17:12:48 +0000 (UTC) (envelope-from sam@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n08HCmYd028063; Thu, 8 Jan 2009 17:12:48 GMT (envelope-from sam@svn.freebsd.org) Received: (from sam@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n08HClhX028057; Thu, 8 Jan 2009 17:12:47 GMT (envelope-from sam@svn.freebsd.org) Message-Id: <200901081712.n08HClhX028057@svn.freebsd.org> From: Sam Leffler Date: Thu, 8 Jan 2009 17:12:47 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: 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 X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 08 Jan 2009 17:12:51 -0000 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("", + 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 #include +#ifdef ATH_SUPPORT_TDMA +#include +#endif #include @@ -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 ***