[Zrouter-src-freebsd] ZRouter.org: push to FreeBSD HEAD tree

zrouter-src-freebsd at zrouter.org zrouter-src-freebsd at zrouter.org
Wed Jul 25 14:36:29 UTC 2012


details:   http://zrouter.org/hg/FreeBSD/head//rev/8f13631d24de
changeset: 504:8f13631d24de
user:      Aleksandr Rybalko <ray at ddteam.net>
date:      Wed Jul 25 17:07:47 2012 +0300
description:
Lazy update

diffstat:

 head/sys/dev/ath/ah_osdep.c                     |     5 +-
 head/sys/dev/ath/ah_osdep.h                     |    21 +-
 head/sys/dev/ath/ath_dfs/null/dfs_null.c        |    25 +-
 head/sys/dev/ath/ath_hal/ah.c                   |    31 +-
 head/sys/dev/ath/ath_hal/ah.h                   |   246 ++-
 head/sys/dev/ath/ath_hal/ah_debug.h             |     9 +-
 head/sys/dev/ath/ath_hal/ah_desc.h              |    29 +-
 head/sys/dev/ath/ath_hal/ah_internal.h          |    73 +-
 head/sys/dev/ath/ath_hal/ar5210/ar5210.h        |    17 +-
 head/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c |    20 +-
 head/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c   |    33 +-
 head/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c   |    10 +-
 head/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c   |    35 +-
 head/sys/dev/ath/ath_hal/ar5211/ar5211.h        |    17 +-
 head/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c |    20 +-
 head/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c   |    33 +-
 head/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c   |    10 +-
 head/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c   |    26 +-
 head/sys/dev/ath/ath_hal/ar5212/ar5212.h        |    15 +-
 head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c |    17 +-
 head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c   |    17 +-
 head/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c   |    10 +-
 head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c  |     5 +-
 head/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c   |    26 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416.h        |    45 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c    |    79 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c |    73 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c |   385 +++
 head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h |    31 +
 head/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c   |    38 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c   |    69 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c   |    75 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c  |     5 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416phy.h     |    38 +-
 head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h     |    55 +-
 head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c |    67 +-
 head/sys/dev/ath/ath_hal/ar9002/ar9285.h        |     7 +-
 head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c |    89 +-
 head/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c |   135 +
 head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c |    18 +-
 head/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c  |   150 +-
 head/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h |    59 +
 head/sys/dev/ath/ath_rate/amrr/amrr.c           |    11 +-
 head/sys/dev/ath/ath_rate/onoe/onoe.c           |    11 +-
 head/sys/dev/ath/ath_rate/sample/sample.c       |    98 +-
 head/sys/dev/ath/ath_rate/sample/sample.h       |    18 +-
 head/sys/dev/ath/if_ath.c                       |  2496 +++-------------------
 head/sys/dev/ath/if_ath_ahb.c                   |    10 +-
 head/sys/dev/ath/if_ath_beacon.c                |   844 +++++++
 head/sys/dev/ath/if_ath_beacon.h                |    51 +
 head/sys/dev/ath/if_ath_debug.c                 |    21 +-
 head/sys/dev/ath/if_ath_debug.h                 |    73 +-
 head/sys/dev/ath/if_ath_led.c                   |     8 +-
 head/sys/dev/ath/if_ath_misc.h                  |    48 +-
 head/sys/dev/ath/if_ath_pci.c                   |     8 +-
 head/sys/dev/ath/if_ath_rx.c                    |  1113 ++++++++++
 head/sys/dev/ath/if_ath_rx.h                    |    65 +
 head/sys/dev/ath/if_ath_rx_edma.c               |   849 +++++++
 head/sys/dev/ath/if_ath_rx_edma.h               |    36 +
 head/sys/dev/ath/if_ath_sysctl.c                |    26 +-
 head/sys/dev/ath/if_ath_tdma.c                  |   476 ++++
 head/sys/dev/ath/if_ath_tdma.h                  |    55 +
 head/sys/dev/ath/if_ath_tsf.h                   |    81 +
 head/sys/dev/ath/if_ath_tx.c                    |   581 ++--
 head/sys/dev/ath/if_ath_tx.h                    |    13 +-
 head/sys/dev/ath/if_ath_tx_edma.c               |   162 +
 head/sys/dev/ath/if_ath_tx_edma.h               |    36 +
 head/sys/dev/ath/if_ath_tx_ht.c                 |   118 +-
 head/sys/dev/ath/if_athdfs.h                    |     4 +-
 head/sys/dev/ath/if_athioctl.h                  |   147 +-
 head/sys/dev/ath/if_athrate.h                   |    14 +-
 head/sys/dev/ath/if_athvar.h                    |   220 +-
 72 files changed, 7005 insertions(+), 2856 deletions(-)

diffs (13118 lines):

diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ah_osdep.c
--- a/head/sys/dev/ath/ah_osdep.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ah_osdep.c	Wed Jul 25 17:07:47 2012 +0300
@@ -26,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/ah_osdep.c 234450 2012-04-19 03:26:21Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ah_osdep.c 237864 2012-07-01 02:34:32Z adrian $
  */
 #include "opt_ah.h"
 
@@ -47,6 +47,7 @@
 #include <net/ethernet.h>		/* XXX for ether_sprintf */
 
 #include <dev/ath/ath_hal/ah.h>
+#include <dev/ath/ath_hal/ah_debug.h>
 
 /*
  * WiSoC boards overload the bus tag with information about the
@@ -137,8 +138,6 @@
 
 #ifdef AH_DEBUG
 
-/* This must match the definition in ath_hal/ah_debug.h */
-#define	HAL_DEBUG_UNMASKABLE	0xf0000000
 void
 DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...)
 {
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ah_osdep.h
--- a/head/sys/dev/ath/ah_osdep.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ah_osdep.h	Wed Jul 25 17:07:47 2012 +0300
@@ -26,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/ah_osdep.h 234450 2012-04-19 03:26:21Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ah_osdep.h 237865 2012-07-01 02:37:04Z adrian $
  */
 #ifndef _ATH_AH_OSDEP_H_
 #define _ATH_AH_OSDEP_H_
@@ -63,6 +63,7 @@
 #define	OS_INLINE	__inline
 #define	OS_MEMZERO(_a, _n)	bzero((_a), (_n))
 #define	OS_MEMCPY(_d, _s, _n)	memcpy(_d,_s,_n)
+#define	OS_MEMCMP(_a, _b, _l)	memcmp((_a), (_b), (_l))
 
 #define	abs(_a)		__builtin_abs(_a)
 
@@ -75,6 +76,10 @@
  * domain registers are not byte swapped!  Thus, on big-endian
  * platforms we have to explicitly byte-swap those registers.
  * OS_REG_UNSWAPPED identifies the registers that need special handling.
+ *
+ * This is not currently used by the FreeBSD HAL osdep code; the HAL
+ * currently does not configure hardware byteswapping for register space
+ * accesses and instead does it through the FreeBSD bus space code.
  */
 #if _BYTE_ORDER == _BIG_ENDIAN
 #define	OS_REG_UNSWAPPED(_reg) \
@@ -85,6 +90,20 @@
 #endif /* _BYTE_ORDER */
 
 /*
+ * For USB/SDIO support (where access latencies are quite high);
+ * some write accesses may be buffered and then flushed when
+ * either a read is done, or an explicit flush is done.
+ *
+ * These are simply placeholders for now.
+ */
+#define	OS_REG_WRITE_BUFFER_ENABLE(_ah)		\
+	    do { } while (0)
+#define	OS_REG_WRITE_BUFFER_DISABLE(_ah)	\
+	    do { } while (0)
+#define	OS_REG_WRITE_BUFFER_FLUSH(_ah)		\
+	    do { } while (0)
+
+/*
  * Register read/write operations are either handled through
  * platform-dependent routines (or when debugging is enabled
  * with AH_DEBUG); or they are inline expanded using the macros
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_dfs/null/dfs_null.c
--- a/head/sys/dev/ath/ath_dfs/null/dfs_null.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_dfs/null/dfs_null.c	Wed Jul 25 17:07:47 2012 +0300
@@ -26,14 +26,15 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/ath_dfs/null/dfs_null.c 231099 2012-02-06 20:23:21Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_dfs/null/dfs_null.c 237529 2012-06-24 08:47:19Z adrian $
  */
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/ath_dfs/null/dfs_null.c 231099 2012-02-06 20:23:21Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/ath_dfs/null/dfs_null.c 237529 2012-06-24 08:47:19Z adrian $");
 
 /*
  * This implements an empty DFS module.
  */
+#include "opt_ath.h"
 #include "opt_inet.h"
 #include "opt_wlan.h"
 
@@ -102,7 +103,7 @@
 int
 ath_dfs_attach(struct ath_softc *sc)
 {
-	return 1;
+	return (1);
 }
 
 /*
@@ -111,11 +112,12 @@
 int
 ath_dfs_detach(struct ath_softc *sc)
 {
-	return 1;
+	return (1);
 }
 
 /*
- * Enable radar check
+ * Enable radar check.  Return 1 if the driver should
+ * enable radar PHY errors, or 0 if not.
  */
 int
 ath_dfs_radar_enable(struct ath_softc *sc, struct ieee80211_channel *chan)
@@ -163,9 +165,12 @@
 
 /*
  * Process DFS related PHY errors
+ *
+ * The mbuf is not "ours" and if we want a copy, we have
+ * to take a copy.  It'll be freed after this function returns.
  */
 void
-ath_dfs_process_phy_err(struct ath_softc *sc, const char *buf,
+ath_dfs_process_phy_err(struct ath_softc *sc, struct mbuf *m,
     uint64_t tsf, struct ath_rx_status *rxstat)
 {
 
@@ -182,7 +187,7 @@
 ath_dfs_process_radar_event(struct ath_softc *sc,
     struct ieee80211_channel *chan)
 {
-	return 0;
+	return (0);
 }
 
 /*
@@ -195,7 +200,7 @@
 int
 ath_dfs_tasklet_needed(struct ath_softc *sc, struct ieee80211_channel *chan)
 {
-	return 0;
+	return (0);
 }
 
 /*
@@ -272,7 +277,7 @@
 		free(indata, M_TEMP);
 	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
 		free(outdata, M_TEMP);
-	return error;
+	return (error);
 }
 
 /*
@@ -282,5 +287,5 @@
 ath_dfs_get_thresholds(struct ath_softc *sc, HAL_PHYERR_PARAM *param)
 {
 	ath_hal_getdfsthresh(sc->sc_ah, param);
-	return 1;
+	return (1);
 }
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ah.c
--- a/head/sys/dev/ath/ath_hal/ah.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ah.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ah.c 230147 2012-01-15 19:22:34Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ah.c 238333 2012-07-10 03:48:07Z adrian $
  */
 #include "opt_ah.h"
 
@@ -630,6 +630,34 @@
 	case HAL_CAP_REG_FLAG:
 		*result = AH_PRIVATE(ah)->ah_currentRDext;
 		return HAL_OK;
+	case HAL_CAP_ENHANCED_DMA_SUPPORT:
+		return pCap->halEnhancedDmaSupport ? HAL_OK : HAL_ENOTSUPP;
+	case HAL_CAP_NUM_TXMAPS:
+		*result = pCap->halNumTxMaps;
+		return HAL_OK;
+	case HAL_CAP_TXDESCLEN:
+		*result = pCap->halTxDescLen;
+		return HAL_OK;
+	case HAL_CAP_TXSTATUSLEN:
+		*result = pCap->halTxStatusLen;
+		return HAL_OK;
+	case HAL_CAP_RXSTATUSLEN:
+		*result = pCap->halRxStatusLen;
+		return HAL_OK;
+	case HAL_CAP_RXFIFODEPTH:
+		switch (capability) {
+		case HAL_RX_QUEUE_HP:
+			*result = pCap->halRxHpFifoDepth;
+			return HAL_OK;
+		case HAL_RX_QUEUE_LP:
+			*result = pCap->halRxLpFifoDepth;
+			return HAL_OK;
+		default:
+			return HAL_ENOTSUPP;
+	}
+	case HAL_CAP_RXBUFSIZE:
+	case HAL_CAP_NUM_MR_RETRIES:
+		return HAL_EINVAL;      /* XXX not yet */
 	case HAL_CAP_BT_COEX:
 		return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_HT20_SGI:
@@ -667,6 +695,7 @@
 		return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_SERIALISE_WAR:		/* PCI register serialisation */
 		return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP;
+
 	default:
 		return HAL_EINVAL;
 	}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ah.h
--- a/head/sys/dev/ath/ath_hal/ah.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ah.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ah.h 234088 2012-04-10 07:11:33Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ah.h 238731 2012-07-24 01:18:19Z adrian $
  */
 
 #ifndef _ATH_AH_H_
@@ -73,6 +73,7 @@
 	HAL_EINPROGRESS	= 15,	/* Operation incomplete */
 	HAL_EEBADREG	= 16,	/* EEPROM invalid regulatory contents */
 	HAL_EEBADCC	= 17,	/* EEPROM invalid country code */
+	HAL_INV_PMODE	= 18,	/* Couldn't bring out of sleep state */
 } HAL_STATUS;
 
 typedef enum {
@@ -109,7 +110,7 @@
 	HAL_CAP_TPC_ACK		= 26,	/* ack txpower with per-packet tpc */
 	HAL_CAP_TPC_CTS		= 27,	/* cts txpower with per-packet tpc */
 	HAL_CAP_11D		= 28,   /* 11d beacon support for changing cc */
-
+	HAL_CAP_PCIE_PS		= 29,
 	HAL_CAP_HT		= 30,   /* hardware can support HT */
 	HAL_CAP_GTXTO		= 31,	/* hardware supports global tx timeout */
 	HAL_CAP_FAST_CC		= 32,	/* hardware supports fast channel change */
@@ -119,6 +120,9 @@
 
 	HAL_CAP_CST		= 38,	/* hardware supports carrier sense timeout */
 
+	HAL_CAP_RIFS_RX		= 39,
+	HAL_CAP_RIFS_TX		= 40,
+	HAL_CAP_FORCE_PPM	= 41,
 	HAL_CAP_RTS_AGGR_LIMIT	= 42,	/* aggregation limit with RTS */
 	HAL_CAP_4ADDR_AGGR	= 43,	/* hardware is capable of 4addr aggregation */
 	HAL_CAP_DFS_DMN		= 44,	/* current DFS domain */
@@ -130,8 +134,32 @@
 	HAL_CAP_MBSSID_AGGR_SUPPORT	= 49, /* Support for mBSSID Aggregation */
 	HAL_CAP_SPLIT_4KB_TRANS	= 50,	/* hardware supports descriptors straddling a 4k page boundary */
 	HAL_CAP_REG_FLAG	= 51,	/* Regulatory domain flags */
+	HAL_CAP_BB_RIFS_HANG	= 52,
+	HAL_CAP_RIFS_RX_ENABLED	= 53,
+	HAL_CAP_BB_DFS_HANG	= 54,
 
 	HAL_CAP_BT_COEX		= 60,	/* hardware is capable of bluetooth coexistence */
+	HAL_CAP_DYNAMIC_SMPS	= 61,	/* Dynamic MIMO Power Save hardware support */
+
+	HAL_CAP_DS		= 67,	/* 2 stream */
+	HAL_CAP_BB_RX_CLEAR_STUCK_HANG	= 68,
+	HAL_CAP_MAC_HANG	= 69,	/* can MAC hang */
+	HAL_CAP_MFP		= 70,	/* Management Frame Protection in hardware */
+
+	HAL_CAP_TS		= 72,	/* 3 stream */
+
+	HAL_CAP_ENHANCED_DMA_SUPPORT	= 75,	/* DMA FIFO support */
+	HAL_CAP_NUM_TXMAPS	= 76,	/* Number of buffers in a transmit descriptor */
+	HAL_CAP_TXDESCLEN	= 77,	/* Length of transmit descriptor */
+	HAL_CAP_TXSTATUSLEN	= 78,	/* Length of transmit status descriptor */
+	HAL_CAP_RXSTATUSLEN	= 79,	/* Length of transmit status descriptor */
+	HAL_CAP_RXFIFODEPTH	= 80,	/* Receive hardware FIFO depth */
+	HAL_CAP_RXBUFSIZE	= 81,	/* Receive Buffer Length */
+	HAL_CAP_NUM_MR_RETRIES	= 82,	/* limit on multirate retries */
+
+	HAL_CAP_OL_PWRCTRL	= 84,	/* Open loop TX power control */
+
+	HAL_CAP_BB_PANIC_WATCHDOG	= 92,
 
 	HAL_CAP_HT20_SGI	= 96,	/* hardware supports HT20 short GI */
 
@@ -143,7 +171,6 @@
 	HAL_CAP_INTMIT		= 229,	/* interference mitigation */
 	HAL_CAP_RXORN_FATAL	= 230,	/* HAL_INT_RXORN treated as fatal */
 	HAL_CAP_BB_HANG		= 235,	/* can baseband hang */
-	HAL_CAP_MAC_HANG	= 236,	/* can MAC hang */
 	HAL_CAP_INTRMASK	= 237,	/* bitmask of supported interrupts */
 	HAL_CAP_BSSIDMATCH	= 238,	/* hardware has disable bssid match */
 	HAL_CAP_STREAMS		= 239,	/* how many 802.11n spatial streams are available */
@@ -180,10 +207,19 @@
 	HAL_TX_QUEUE_CAB	= 3,		/* "crap after beacon" xmit q */
 	HAL_TX_QUEUE_UAPSD	= 4,		/* u-apsd power save xmit q */
 	HAL_TX_QUEUE_PSPOLL	= 5,		/* power save poll xmit q */
+	HAL_TX_QUEUE_CFEND	= 6,
+	HAL_TX_QUEUE_PAPRD	= 7,
 } HAL_TX_QUEUE;
 
 #define	HAL_NUM_TX_QUEUES	10		/* max possible # of queues */
 
+typedef enum {
+	HAL_RX_QUEUE_HP = 0,			/* high priority recv queue */
+	HAL_RX_QUEUE_LP = 1,			/* low priority recv queue */
+} HAL_RX_QUEUE;
+
+#define	HAL_NUM_RX_QUEUES	2		/* max possible # of queues */
+
 /*
  * Transmit queue subtype.  These map directly to
  * WME Access Categories (except for UPSD).  Refer
@@ -373,7 +409,10 @@
  */
 typedef enum {
 	HAL_INT_RX	= 0x00000001,	/* Non-common mapping */
-	HAL_INT_RXDESC	= 0x00000002,
+	HAL_INT_RXDESC	= 0x00000002,	/* Legacy mapping */
+	HAL_INT_RXHP	= 0x00000001,	/* EDMA */
+	HAL_INT_RXLP	= 0x00000002,	/* EDMA */
+	HAL_INT_RXERR	= 0x00000004,
 	HAL_INT_RXNOFRM	= 0x00000008,
 	HAL_INT_RXEOL	= 0x00000010,
 	HAL_INT_RXORN	= 0x00000020,
@@ -420,14 +459,38 @@
 			| HAL_INT_GPIO,
 } HAL_INT;
 
+/*
+ * MSI vector assignments
+ */
 typedef enum {
-	HAL_GPIO_MUX_OUTPUT		= 0,
-	HAL_GPIO_MUX_PCIE_ATTENTION_LED	= 1,
-	HAL_GPIO_MUX_PCIE_POWER_LED	= 2,
-	HAL_GPIO_MUX_TX_FRAME		= 3,
-	HAL_GPIO_MUX_RX_CLEAR_EXTERNAL	= 4,
-	HAL_GPIO_MUX_MAC_NETWORK_LED	= 5,
-	HAL_GPIO_MUX_MAC_POWER_LED	= 6
+	HAL_MSIVEC_MISC = 0,
+	HAL_MSIVEC_TX   = 1,
+	HAL_MSIVEC_RXLP = 2,
+	HAL_MSIVEC_RXHP = 3,
+} HAL_MSIVEC;
+
+typedef enum {
+	HAL_INT_LINE = 0,
+	HAL_INT_MSI  = 1,
+} HAL_INT_TYPE;
+
+/* For interrupt mitigation registers */
+typedef enum {
+	HAL_INT_RX_FIRSTPKT=0,
+	HAL_INT_RX_LASTPKT,
+	HAL_INT_TX_FIRSTPKT,
+	HAL_INT_TX_LASTPKT,
+	HAL_INT_THRESHOLD
+} HAL_INT_MITIGATION;
+
+typedef enum {
+	HAL_GPIO_OUTPUT_MUX_AS_OUTPUT		= 0,
+	HAL_GPIO_OUTPUT_MUX_PCIE_ATTENTION_LED	= 1,
+	HAL_GPIO_OUTPUT_MUX_PCIE_POWER_LED	= 2,
+	HAL_GPIO_OUTPUT_MUX_MAC_NETWORK_LED	= 3,
+	HAL_GPIO_OUTPUT_MUX_MAC_POWER_LED	= 4,
+	HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE	= 5,
+	HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME		= 6
 } HAL_GPIO_MUX_TYPE;
 
 typedef enum {
@@ -568,7 +631,7 @@
 
 typedef struct {
 	uint8_t		kv_type;		/* one of HAL_CIPHER */
-	uint8_t		kv_pad;
+	uint8_t		kv_apsd;		/* Mask for APSD enabled ACs */
 	uint16_t	kv_len;			/* length in bits */
 	uint8_t		kv_val[16];		/* enough for 128-bit keys */
 	uint8_t		kv_mic[8];		/* TKIP MIC key */
@@ -660,7 +723,11 @@
 	uint32_t        tx_busy;
 	uint32_t        rx_busy;
 	uint32_t        chan_busy;
+	uint32_t        ext_chan_busy;
 	uint32_t        cycle_count;
+	/* XXX TODO */
+	uint32_t        ofdm_phyerr_count;
+	uint32_t        cck_phyerr_count;
 } HAL_SURVEY_SAMPLE;
 
 /*
@@ -693,6 +760,7 @@
 	HAL_ANI_SPUR_IMMUNITY_LEVEL = 5,	/* set level */
 	HAL_ANI_MODE = 6,			/* 0 => manual, 1 => auto (XXX do not change) */
 	HAL_ANI_PHYERR_RESET = 7,		/* reset phy error stats */
+	HAL_ANI_MRC_CCK = 8,
 } HAL_ANI_CMD;
 
 /*
@@ -710,6 +778,7 @@
 	HAL_CAP_INTMIT_SPUR_IMMUNITY_LEVEL = 6
 } HAL_CAP_INTMIT_CMD;
 
+/* DFS defines */
 typedef struct {
 	int32_t		pe_firpwr;	/* FIR pwr out threshold */
 	int32_t		pe_rrssi;	/* Radar rssi thresh */
@@ -773,6 +842,139 @@
 };
 typedef struct hal_dfs_event HAL_DFS_EVENT;
 
+/*
+ * BT Co-existence definitions
+ */
+typedef enum {
+	HAL_BT_MODULE_CSR_BC4	= 0,	/* CSR BlueCore v4 */
+	HAL_BT_MODULE_JANUS	= 1,	/* Kite + Valkyrie combo */
+	HAL_BT_MODULE_HELIUS	= 2,	/* Kiwi + Valkyrie combo */
+	HAL_MAX_BT_MODULES
+} HAL_BT_MODULE;
+
+typedef struct {
+	HAL_BT_MODULE	bt_module;
+	u_int8_t	bt_coex_config;
+	u_int8_t	bt_gpio_bt_active;
+	u_int8_t	bt_gpio_bt_priority;
+	u_int8_t	bt_gpio_wlan_active;
+	u_int8_t	bt_active_polarity;
+	HAL_BOOL	bt_single_ant;
+	u_int8_t	bt_dutyCycle;
+	u_int8_t	bt_isolation;
+	u_int8_t	bt_period;
+} HAL_BT_COEX_INFO;
+
+typedef enum {
+	HAL_BT_COEX_MODE_LEGACY		= 0,	/* legacy rx_clear mode */
+	HAL_BT_COEX_MODE_UNSLOTTED	= 1,	/* untimed/unslotted mode */
+	HAL_BT_COEX_MODE_SLOTTED	= 2,	/* slotted mode */
+	HAL_BT_COEX_MODE_DISALBED	= 3,	/* coexistence disabled */
+} HAL_BT_COEX_MODE;
+
+typedef enum {
+	HAL_BT_COEX_CFG_NONE,		/* No bt coex enabled */
+	HAL_BT_COEX_CFG_2WIRE_2CH,	/* 2-wire with 2 chains */
+	HAL_BT_COEX_CFG_2WIRE_CH1,	/* 2-wire with ch1 */
+	HAL_BT_COEX_CFG_2WIRE_CH0,	/* 2-wire with ch0 */
+	HAL_BT_COEX_CFG_3WIRE,		/* 3-wire */
+	HAL_BT_COEX_CFG_MCI		/* MCI */
+} HAL_BT_COEX_CFG;
+
+typedef enum {
+	HAL_BT_COEX_SET_ACK_PWR		= 0,	/* Change ACK power setting */
+	HAL_BT_COEX_LOWER_TX_PWR,		/* Change transmit power */
+	HAL_BT_COEX_ANTENNA_DIVERSITY,	/* Enable RX diversity for Kite */
+} HAL_BT_COEX_SET_PARAMETER;
+
+#define	HAL_BT_COEX_FLAG_LOW_ACK_PWR	0x00000001
+#define	HAL_BT_COEX_FLAG_LOWER_TX_PWR	0x00000002
+/* Check Rx Diversity is allowed */
+#define	HAL_BT_COEX_FLAG_ANT_DIV_ALLOW	0x00000004
+/* Check Diversity is on or off */
+#define	HAL_BT_COEX_FLAG_ANT_DIV_ENABLE	0x00000008
+
+#define	HAL_BT_COEX_ANTDIV_CONTROL1_ENABLE	0x0b
+/* main: LNA1, alt: LNA2 */
+#define	HAL_BT_COEX_ANTDIV_CONTROL2_ENABLE	0x09
+#define	HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_A	0x04
+#define	HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_A	0x09
+#define	HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_B	0x02
+#define	HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_B	0x06
+
+#define	HAL_BT_COEX_ISOLATION_FOR_NO_COEX	30
+
+#define	HAL_BT_COEX_ANT_DIV_SWITCH_COM	0x66666666
+
+#define	HAL_BT_COEX_HELIUS_CHAINMASK	0x02
+
+#define	HAL_BT_COEX_LOW_ACK_POWER	0x0
+#define	HAL_BT_COEX_HIGH_ACK_POWER	0x3f3f3f
+
+typedef enum {
+	HAL_BT_COEX_NO_STOMP = 0,
+	HAL_BT_COEX_STOMP_ALL,
+	HAL_BT_COEX_STOMP_LOW,
+	HAL_BT_COEX_STOMP_NONE,
+	HAL_BT_COEX_STOMP_ALL_FORCE,
+	HAL_BT_COEX_STOMP_LOW_FORCE,
+} HAL_BT_COEX_STOMP_TYPE;
+
+typedef struct {
+	/* extend rx_clear after tx/rx to protect the burst (in usec). */
+	u_int8_t	bt_time_extend;
+
+	/*
+	 * extend rx_clear as long as txsm is
+	 * transmitting or waiting for ack.
+	 */
+	HAL_BOOL	bt_txstate_extend;
+
+	/*
+	 * extend rx_clear so that when tx_frame
+	 * is asserted, rx_clear will drop.
+	 */
+	HAL_BOOL	bt_txframe_extend;
+
+	/*
+	 * coexistence mode
+	 */
+	HAL_BT_COEX_MODE	bt_mode;
+
+	/*
+	 * treat BT high priority traffic as
+	 * a quiet collision
+	 */
+	HAL_BOOL	bt_quiet_collision;
+
+	/*
+	 * invert rx_clear as WLAN_ACTIVE
+	 */
+	HAL_BOOL	bt_rxclear_polarity;
+
+	/*
+	 * slotted mode only. indicate the time in usec
+	 * from the rising edge of BT_ACTIVE to the time
+	 * BT_PRIORITY can be sampled to indicate priority.
+	 */
+	u_int8_t	bt_priority_time;
+
+	/*
+	 * slotted mode only. indicate the time in usec
+	 * from the rising edge of BT_ACTIVE to the time
+	 * BT_PRIORITY can be sampled to indicate tx/rx and
+	 * BT_FREQ is sampled.
+	 */
+	u_int8_t	bt_first_slot_time;
+
+	/*
+	 * slotted mode only. rx_clear and bt_ant decision
+	 * will be held the entire time that BT_ACTIVE is asserted,
+	 * otherwise the decision is made before every slot boundry.
+	 */
+	HAL_BOOL	bt_hold_rxclear;
+} HAL_BT_COEX_CONFIG;
+
 typedef struct
 {
 	int ah_debug;			/* only used if AH_DEBUG is defined */
@@ -828,7 +1030,8 @@
 				HAL_BOOL bChannelChange, HAL_STATUS *status);
 	HAL_BOOL  __ahdecl(*ah_phyDisable)(struct ath_hal *);
 	HAL_BOOL  __ahdecl(*ah_disable)(struct ath_hal *);
-	void	  __ahdecl(*ah_configPCIE)(struct ath_hal *, HAL_BOOL restore);
+	void	  __ahdecl(*ah_configPCIE)(struct ath_hal *, HAL_BOOL restore,
+				HAL_BOOL power_off);
 	void	  __ahdecl(*ah_disablePCIE)(struct ath_hal *);
 	void	  __ahdecl(*ah_setPCUConfig)(struct ath_hal *);
 	HAL_BOOL  __ahdecl(*ah_perCalibration)(struct ath_hal*,
@@ -881,10 +1084,19 @@
 	void	   __ahdecl(*ah_reqTxIntrDesc)(struct ath_hal *, struct ath_desc*);
 	HAL_BOOL	__ahdecl(*ah_getTxCompletionRates)(struct ath_hal *,
 				const struct ath_desc *ds, int *rates, int *tries);
+	void	  __ahdecl(*ah_setTxDescLink)(struct ath_hal *ah, void *ds,
+				uint32_t link);
+	void	  __ahdecl(*ah_getTxDescLink)(struct ath_hal *ah, void *ds,
+				uint32_t *link);
+	void	  __ahdecl(*ah_getTxDescLinkPtr)(struct ath_hal *ah, void *ds,
+				uint32_t **linkptr);
+	void	  __ahdecl(*ah_setupTxStatusRing)(struct ath_hal *,
+				void *ts_start, uint32_t ts_paddr_start,
+				uint16_t size);
 
 	/* Receive Functions */
-	uint32_t __ahdecl(*ah_getRxDP)(struct ath_hal*);
-	void	  __ahdecl(*ah_setRxDP)(struct ath_hal*, uint32_t rxdp);
+	uint32_t __ahdecl(*ah_getRxDP)(struct ath_hal*, HAL_RX_QUEUE);
+	void	  __ahdecl(*ah_setRxDP)(struct ath_hal*, uint32_t rxdp, HAL_RX_QUEUE);
 	void	  __ahdecl(*ah_enableReceive)(struct ath_hal*);
 	HAL_BOOL  __ahdecl(*ah_stopDmaReceive)(struct ath_hal*);
 	void	  __ahdecl(*ah_startPcuReceive)(struct ath_hal*);
@@ -1030,8 +1242,8 @@
 	    			struct ath_desc *);
 	void	  __ahdecl(*ah_set11nBurstDuration)(struct ath_hal *,
 	    			struct ath_desc *, u_int);
-	uint32_t  __ahdecl(*ah_get_mib_cycle_counts_pct) (struct ath_hal *,
-				uint32_t *, uint32_t *, uint32_t *, uint32_t *);
+	HAL_BOOL  __ahdecl(*ah_getMibCycleCounts) (struct ath_hal *,
+				HAL_SURVEY_SAMPLE *);
 
 	uint32_t  __ahdecl(*ah_get11nExtBusy)(struct ath_hal *);
 	void      __ahdecl(*ah_set11nMac2040)(struct ath_hal *,
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ah_debug.h
--- a/head/sys/dev/ath/ath_hal/ah_debug.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ah_debug.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ah_debug.h 221776 2011-05-11 13:22:41Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ah_debug.h 238276 2012-07-09 06:41:18Z adrian $
  */
 #ifndef _ATH_AH_DEBUG_H_
 #define _ATH_AH_DEBUG_H_
@@ -47,8 +47,13 @@
 	HAL_DEBUG_DIVERSITY	= 0x00100000,	/* diversity debugging */
 	HAL_DEBUG_DFS		= 0x00200000,	/* DFS debugging */
 	HAL_DEBUG_HANG		= 0x00400000,	/* BB/MAC hang debugging */
+	HAL_DEBUG_CALIBRATE	= 0x00800000,	/* setup calibration */
+	HAL_DEBUG_POWER_MGMT	= 0x01000000,	/* power calibration */
+	HAL_DEBUG_CHANNEL	= 0x02000000,
+	HAL_DEBUG_QUEUE		= 0x04000000,
+	HAL_DEBUG_PRINT_REG	= 0x08000000,
 
-	HAL_DEBUG_UNMASKABLE	= 0xf0000000,	/* always printed */
+	HAL_DEBUG_UNMASKABLE	= 0x80000000,	/* always printed */
 	HAL_DEBUG_ANY		= 0xffffffff
 };
 #endif /* _ATH_AH_DEBUG_H_ */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ah_desc.h
--- a/head/sys/dev/ath/ath_hal/ah_desc.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ah_desc.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ah_desc.h 229791 2012-01-07 20:23:05Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ah_desc.h 238314 2012-07-09 23:58:22Z adrian $
  */
 
 #ifndef _DEV_ATH_DESC_H
@@ -122,11 +122,12 @@
 	int8_t		rs_rssi_ext[3];	/* rx frame RSSI [ext, chain 0-2] */
 	uint8_t		rs_isaggr;	/* is part of the aggregate */
 	uint8_t		rs_moreaggr;	/* more frames in aggr to follow */
+	uint16_t	rs_flags;	/* misc flags */
 	uint8_t		rs_num_delims;	/* number of delims in aggr */
-	uint8_t		rs_flags;	/* misc flags */
+	uint8_t		rs_spare0;	/* padding */
 	uint32_t	rs_evm0;	/* evm bytes */
 	uint32_t	rs_evm1;
-	uint32_t	rs_evm2;	
+	uint32_t	rs_evm2;
 	uint32_t	rs_evm3;	/* needed for ar9300 and later */
 	uint32_t	rs_evm4;	/* needed for ar9300 and later */
 #endif /* AH_SUPPORT_AR5416 */
@@ -138,16 +139,19 @@
 #define	HAL_RXERR_FIFO		0x04	/* fifo overrun */
 #define	HAL_RXERR_DECRYPT	0x08	/* non-Michael decrypt error */
 #define	HAL_RXERR_MIC		0x10	/* Michael MIC decrypt error */
+#define	HAL_RXERR_INCOMP	0x20	/* Rx Desc processing is incomplete */
+#define	HAL_RXERR_KEYMISS	0x40	/* Key not found in keycache */
 
 /* bits found in rs_flags */
-#define	HAL_RX_MORE		0x01	/* more descriptors follow */
-#define	HAL_RX_MORE_AGGR	0x02	/* more frames in aggr */
-#define	HAL_RX_GI		0x04	/* full gi */
-#define	HAL_RX_2040		0x08	/* 40 Mhz */
-#define	HAL_RX_DELIM_CRC_PRE	0x10	/* crc error in delimiter pre */
-#define	HAL_RX_DELIM_CRC_POST	0x20	/* crc error in delim after */
-#define	HAL_RX_DECRYPT_BUSY	0x40	/* decrypt was too slow */
-#define	HAL_RX_HI_RX_CHAIN	0x80	/* SM power save: hi Rx chain control */
+#define	HAL_RX_MORE		0x0001	/* more descriptors follow */
+#define	HAL_RX_MORE_AGGR	0x0002	/* more frames in aggr */
+#define	HAL_RX_GI		0x0004	/* full gi */
+#define	HAL_RX_2040		0x0008	/* 40 Mhz */
+#define	HAL_RX_DELIM_CRC_PRE	0x0010	/* crc error in delimiter pre */
+#define	HAL_RX_DELIM_CRC_POST	0x0020	/* crc error in delim after */
+#define	HAL_RX_DECRYPT_BUSY	0x0040	/* decrypt was too slow */
+#define	HAL_RX_HI_RX_CHAIN	0x0080	/* SM power save: hi Rx chain control */
+#define	HAL_RX_IS_APSD		0x0100	/* Is ASPD trigger frame */
 
 enum {
 	HAL_PHYERR_UNDERRUN		= 0,	/* Transmit underrun */
@@ -178,6 +182,8 @@
 	HAL_PHYERR_HT_CRC_ERROR		= 34,	/* */
 	HAL_PHYERR_HT_LENGTH_ILLEGAL	= 35,	/* */
 	HAL_PHYERR_HT_RATE_ILLEGAL	= 36,	/* */
+
+	HAL_PHYERR_SPECTRAL		= 38,
 };
 
 /* value found in rs_keyix to mark invalid entries */
@@ -225,6 +231,7 @@
 #define	ds_rxstat	ds_us.rx
 
 /* flags passed to tx descriptor setup methods */
+/* This is a uint16_t field in ath_buf, just be warned! */
 #define	HAL_TXDESC_CLRDMASK	0x0001	/* clear destination filter mask */
 #define	HAL_TXDESC_NOACK	0x0002	/* don't wait for ACK */
 #define	HAL_TXDESC_RTSENA	0x0004	/* enable RTS */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ah_internal.h
--- a/head/sys/dev/ath/ath_hal/ah_internal.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ah_internal.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ah_internal.h 229791 2012-01-07 20:23:05Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ah_internal.h 237868 2012-07-01 03:15:18Z adrian $
  */
 #ifndef _ATH_AH_INTERAL_H_
 #define _ATH_AH_INTERAL_H_
@@ -212,10 +212,24 @@
 			halBssidMatchSupport		: 1,
 			hal4kbSplitTransSupport		: 1,
 			halHasRxSelfLinkedTail		: 1,
-			halSupportsFastClock5GHz	: 1,	/* Hardware supports 5ghz fast clock; check eeprom/channel before using */
+			halSupportsFastClock5GHz	: 1,
 			halHasLongRxDescTsf		: 1,
 			halHasBBReadWar			: 1,
-			halSerialiseRegWar		: 1;
+			halSerialiseRegWar		: 1,
+			halMciSupport			: 1,
+			halRxTxAbortSupport		: 1,
+			halPaprdEnabled			: 1,
+			halHasUapsdSupport		: 1,
+			halWpsPushButtonSupport		: 1,
+			halBtCoexApsmWar		: 1,
+			halGenTimerSupport		: 1,
+			halLDPCSupport			: 1,
+			halHwBeaconProcSupport		: 1,
+			halEnhancedDmaSupport		: 1;
+	uint32_t	halIsrRacSupport		: 1,
+			halApmEnable			: 1,
+			halIntrMitigation		: 1;
+
 	uint32_t	halWirelessModes;
 	uint16_t	halTotalQueues;
 	uint16_t	halKeyCacheSize;
@@ -231,6 +245,13 @@
 	uint32_t	halIntrMask;
 	uint8_t		halTxStreams;
 	uint8_t		halRxStreams;
+
+	int		halNumTxMaps;
+	int		halTxDescLen;
+	int		halTxStatusLen;
+	int		halRxStatusLen;
+	int		halRxHpFifoDepth;
+	int		halRxLpFifoDepth;
 } HAL_CAPABILITIES;
 
 struct regDomain;
@@ -355,8 +376,8 @@
 	AH_PRIVATE(_ah)->ah_getNfAdjust(_ah, _c)
 #define	ath_hal_getNoiseFloor(_ah, _nfArray) \
 	AH_PRIVATE(_ah)->ah_getNoiseFloor(_ah, _nfArray)
-#define	ath_hal_configPCIE(_ah, _reset) \
-	(_ah)->ah_configPCIE(_ah, _reset)
+#define	ath_hal_configPCIE(_ah, _reset, _poweroff) \
+	(_ah)->ah_configPCIE(_ah, _reset, _poweroff)
 #define	ath_hal_disablePCIE(_ah) \
 	(_ah)->ah_disablePCIE(_ah)
 #define	ath_hal_setInterrupts(_ah, _mask) \
@@ -484,6 +505,8 @@
 /* Analog register writes may require a delay between each one (eg Merlin?) */
 #define	OS_A_REG_RMW_FIELD(_a, _r, _f, _v) \
 	do { OS_REG_WRITE(_a, _r, (OS_REG_READ(_a, _r) &~ (_f)) | (((_v) << _f##_S) & (_f))) ; OS_DELAY(100); } while (0)
+#define	OS_A_REG_WRITE(_a, _r, _v) \
+	do { OS_REG_WRITE(_a, _r, _v); OS_DELAY(100); } while (0)
 
 /* wait for the register contents to have the specified value */
 extern	HAL_BOOL ath_hal_wait(struct ath_hal *, u_int reg,
@@ -626,6 +649,46 @@
 /* The diagnostic codes used to be internally defined here -adrian */
 #include "ah_diagcodes.h"
 
+/*
+ * The AR5416 and later HALs have MAC and baseband hang checking.
+ */
+typedef struct {
+	uint32_t hang_reg_offset;
+	uint32_t hang_val;
+	uint32_t hang_mask;
+	uint32_t hang_offset;
+} hal_hw_hang_check_t;
+
+typedef struct {
+	uint32_t dma_dbg_3;
+	uint32_t dma_dbg_4;
+	uint32_t dma_dbg_5;
+	uint32_t dma_dbg_6;
+} mac_dbg_regs_t;
+
+typedef enum {
+	dcu_chain_state		= 0x1,
+	dcu_complete_state	= 0x2,
+	qcu_state		= 0x4,
+	qcu_fsp_ok		= 0x8,
+	qcu_fsp_state		= 0x10,
+	qcu_stitch_state	= 0x20,
+	qcu_fetch_state		= 0x40,
+	qcu_complete_state	= 0x80
+} hal_mac_hangs_t;
+
+typedef struct {
+	int states;
+	uint8_t dcu_chain_state;
+	uint8_t dcu_complete_state;
+	uint8_t qcu_state;
+	uint8_t qcu_fsp_ok;
+	uint8_t qcu_fsp_state;
+	uint8_t qcu_stitch_state;
+	uint8_t qcu_fetch_state;
+	uint8_t qcu_complete_state;
+} hal_mac_hang_check_t;
+
 enum {
     HAL_BB_HANG_DFS		= 0x0001,
     HAL_BB_HANG_RIFS		= 0x0002,
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5210/ar5210.h
--- a/head/sys/dev/ath/ath_hal/ar5210/ar5210.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5210/ar5210.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210.h 225444 2011-09-08 01:23:05Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210.h 238607 2012-07-19 02:25:14Z adrian $
  */
 #ifndef _ATH_AR5210_H_
 #define _ATH_AR5210_H_
@@ -179,9 +179,15 @@
 extern  void ar5210IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *);
 extern	HAL_BOOL ar5210GetTxCompletionRates(struct ath_hal *ah,
 		const struct ath_desc *, int *rates, int *tries);
+extern	void ar5210SetTxDescLink(struct ath_hal *ah, void *ds,
+		uint32_t link);
+extern	void ar5210GetTxDescLink(struct ath_hal *ah, void *ds,
+		uint32_t *link);
+extern	void ar5210GetTxDescLinkPtr(struct ath_hal *ah, void *ds,
+		uint32_t **linkptr);
 
-extern	uint32_t ar5210GetRxDP(struct ath_hal *);
-extern	void ar5210SetRxDP(struct ath_hal *, uint32_t rxdp);
+extern	uint32_t ar5210GetRxDP(struct ath_hal *, HAL_RX_QUEUE);
+extern	void ar5210SetRxDP(struct ath_hal *, uint32_t rxdp, HAL_RX_QUEUE);
 extern	void ar5210EnableReceive(struct ath_hal *);
 extern	HAL_BOOL ar5210StopDmaReceive(struct ath_hal *);
 extern	void ar5210StartPcuReceive(struct ath_hal *);
@@ -249,6 +255,11 @@
 extern	HAL_BOOL ar5210GetDiagState(struct ath_hal *ah, int request,
 		const void *args, uint32_t argsize,
 		void **result, uint32_t *resultsize);
+extern	uint32_t ar5210Get11nExtBusy(struct ath_hal *);
+extern	HAL_BOOL ar5210GetMibCycleCounts(struct ath_hal *,
+		HAL_SURVEY_SAMPLE *);
+extern	void ar5210EnableDfs(struct ath_hal *, HAL_PHYERR_PARAM *);
+extern	void ar5210GetDfsThresh(struct ath_hal *, HAL_PHYERR_PARAM *);
 
 extern	u_int ar5210GetKeyCacheSize(struct ath_hal *);
 extern	HAL_BOOL ar5210IsKeyCacheEntryValid(struct ath_hal *, uint16_t);
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c
--- a/head/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c 230791 2012-01-30 23:07:27Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c 238607 2012-07-19 02:25:14Z adrian $
  */
 #include "opt_ah.h"
 
@@ -33,7 +33,8 @@
 static	HAL_BOOL ar5210GetChipPowerLimits(struct ath_hal *ah,
 		struct ieee80211_channel *chan);
 
-static void ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore);
+static void ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore,
+		HAL_BOOL power_on);
 static void ar5210DisablePCIE(struct ath_hal *ah);
 
 static const struct ath_hal_private ar5210hal = {{
@@ -74,6 +75,9 @@
 	.ah_getTxIntrQueue		= ar5210GetTxIntrQueue,
 	.ah_reqTxIntrDesc 		= ar5210IntrReqTxDesc,
 	.ah_getTxCompletionRates	= ar5210GetTxCompletionRates,
+	.ah_setTxDescLink		= ar5210SetTxDescLink,
+	.ah_getTxDescLink		= ar5210GetTxDescLink,
+	.ah_getTxDescLinkPtr		= ar5210GetTxDescLinkPtr,
 
 	/* RX Functions */
 	.ah_getRxDP			= ar5210GetRxDP,
@@ -129,8 +133,14 @@
 	.ah_getAckCTSRate		= ar5210GetAckCTSRate,
 	.ah_setCTSTimeout		= ar5210SetCTSTimeout,
 	.ah_getCTSTimeout		= ar5210GetCTSTimeout,
-	.ah_setDecompMask               = ar5210SetDecompMask,
-	.ah_setCoverageClass            = ar5210SetCoverageClass,
+	.ah_setDecompMask		= ar5210SetDecompMask,
+	.ah_setCoverageClass		= ar5210SetCoverageClass,
+	.ah_get11nExtBusy		= ar5210Get11nExtBusy,
+	.ah_getMibCycleCounts		= ar5210GetMibCycleCounts,
+	.ah_enableDfs			= ar5210EnableDfs,
+	.ah_getDfsThresh		= ar5210GetDfsThresh,
+	/* XXX procRadarEvent */
+	/* XXX isFastClockEnabled */
 
 	/* Key Cache Functions */
 	.ah_getKeyCacheSize		= ar5210GetKeyCacheSize,
@@ -326,7 +336,7 @@
 }
 
 static void
-ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
 }
 
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c
--- a/head/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c 217684 2011-01-21 05:21:00Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c 235206 2012-05-09 18:17:01Z adrian $
  */
 #include "opt_ah.h"
 
@@ -646,3 +646,34 @@
 	return ath_hal_getdiagstate(ah, request,
 		args, argsize, result, resultsize);
 }
+
+/*
+ * Return what percentage of the extension channel is busy.
+ * This is always disabled for AR5210 series NICs.
+ */
+uint32_t
+ar5210Get11nExtBusy(struct ath_hal *ah)
+{
+
+	return (0);
+}
+
+/*
+ * There's no channel survey support for the AR5210.
+ */
+HAL_BOOL
+ar5210GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample)
+{
+
+	return (AH_FALSE);
+}
+
+void
+ar5210EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+}
+
+void
+ar5210GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c
--- a/head/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c 204644 2010-03-03 17:32:32Z rpaulo $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c 238278 2012-07-09 07:19:11Z adrian $
  */
 #include "opt_ah.h"
 
@@ -30,8 +30,10 @@
  * Get the RXDP.
  */
 uint32_t
-ar5210GetRxDP(struct ath_hal *ah)
+ar5210GetRxDP(struct ath_hal *ah, HAL_RX_QUEUE qtype)
 {
+
+	HALASSERT(qtype == HAL_RX_QUEUE_HP);
 	return OS_REG_READ(ah, AR_RXDP);
 }
 
@@ -39,8 +41,10 @@
  * Set the RxDP.
  */
 void
-ar5210SetRxDP(struct ath_hal *ah, uint32_t rxdp)
+ar5210SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE qtype)
 {
+
+	HALASSERT(qtype == HAL_RX_QUEUE_HP);
 	OS_REG_WRITE(ah, AR_RXDP, rxdp);
 }
 
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c
--- a/head/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c 217621 2011-01-20 05:49:15Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c 238607 2012-07-19 02:25:14Z adrian $
  */
 #include "opt_ah.h"
 
@@ -630,3 +630,36 @@
 {
 	return AH_FALSE;
 }
+
+/*
+ * Set the TX descriptor link pointer
+ */
+void
+ar5210SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link)
+{
+	struct ar5210_desc *ads = AR5210DESC(ds);
+
+	ads->ds_link = link;
+}
+
+/*
+ * Get the TX descriptor link pointer
+ */
+void
+ar5210GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link)
+{
+	struct ar5210_desc *ads = AR5210DESC(ds);
+
+	*link = ads->ds_link;
+}
+
+/*
+ * Get a pointer to the TX descriptor link pointer
+ */
+void
+ar5210GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr)
+{
+	struct ar5210_desc *ads = AR5210DESC(ds);
+
+	*linkptr = &ads->ds_link;
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5211/ar5211.h
--- a/head/sys/dev/ath/ath_hal/ar5211/ar5211.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5211/ar5211.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211.h 225444 2011-09-08 01:23:05Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211.h 238607 2012-07-19 02:25:14Z adrian $
  */
 #ifndef _ATH_AR5211_H_
 #define _ATH_AR5211_H_
@@ -204,9 +204,15 @@
 extern  void ar5211IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *);
 extern	HAL_BOOL ar5211GetTxCompletionRates(struct ath_hal *ah,
 		const struct ath_desc *ds0, int *rates, int *tries);
+extern	void ar5211SetTxDescLink(struct ath_hal *ah, void *ds,
+		uint32_t link);
+extern	void ar5211GetTxDescLink(struct ath_hal *ah, void *ds,
+		uint32_t *link);
+extern	void ar5211GetTxDescLinkPtr(struct ath_hal *ah, void *ds,
+		uint32_t **linkptr);
 
-extern	uint32_t ar5211GetRxDP(struct ath_hal *);
-extern	void ar5211SetRxDP(struct ath_hal *, uint32_t rxdp);
+extern	uint32_t ar5211GetRxDP(struct ath_hal *, HAL_RX_QUEUE);
+extern	void ar5211SetRxDP(struct ath_hal *, uint32_t rxdp, HAL_RX_QUEUE);
 extern	void ar5211EnableReceive(struct ath_hal *);
 extern	HAL_BOOL ar5211StopDmaReceive(struct ath_hal *);
 extern	void ar5211StartPcuReceive(struct ath_hal *);
@@ -276,6 +282,11 @@
 extern	HAL_BOOL ar5211GetDiagState(struct ath_hal *ah, int request,
 		const void *args, uint32_t argsize,
 		void **result, uint32_t *resultsize);
+extern	uint32_t ar5211Get11nExtBusy(struct ath_hal *);
+extern	HAL_BOOL ar5211GetMibCycleCounts(struct ath_hal *,
+		HAL_SURVEY_SAMPLE *);
+extern	void ar5211EnableDfs(struct ath_hal *, HAL_PHYERR_PARAM *);
+extern	void ar5211GetDfsThresh(struct ath_hal *, HAL_PHYERR_PARAM *);
 
 extern	u_int ar5211GetKeyCacheSize(struct ath_hal *);
 extern	HAL_BOOL ar5211IsKeyCacheEntryValid(struct ath_hal *, uint16_t);
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c
--- a/head/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c 230791 2012-01-30 23:07:27Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c 238607 2012-07-19 02:25:14Z adrian $
  */
 #include "opt_ah.h"
 
@@ -33,7 +33,8 @@
 static HAL_BOOL ar5211GetChipPowerLimits(struct ath_hal *ah,
 		struct ieee80211_channel *chan);
 
-static void ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore);
+static void ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore,
+		HAL_BOOL power_off);
 static void ar5211DisablePCIE(struct ath_hal *ah);
 
 static const struct ath_hal_private ar5211hal = {{
@@ -74,6 +75,9 @@
 	.ah_getTxIntrQueue		= ar5211GetTxIntrQueue,
 	.ah_reqTxIntrDesc 		= ar5211IntrReqTxDesc,
 	.ah_getTxCompletionRates	= ar5211GetTxCompletionRates,
+	.ah_setTxDescLink		= ar5211SetTxDescLink,
+	.ah_getTxDescLink		= ar5211GetTxDescLink,
+	.ah_getTxDescLinkPtr		= ar5211GetTxDescLinkPtr,
 
 	/* RX Functions */
 	.ah_getRxDP			= ar5211GetRxDP,
@@ -129,8 +133,14 @@
 	.ah_getAckCTSRate		= ar5211GetAckCTSRate,
 	.ah_setCTSTimeout		= ar5211SetCTSTimeout,
 	.ah_getCTSTimeout		= ar5211GetCTSTimeout,
-	.ah_setDecompMask               = ar5211SetDecompMask,
-	.ah_setCoverageClass            = ar5211SetCoverageClass,
+	.ah_setDecompMask		= ar5211SetDecompMask,
+	.ah_setCoverageClass		= ar5211SetCoverageClass,
+	.ah_get11nExtBusy		= ar5211Get11nExtBusy,
+	.ah_getMibCycleCounts		= ar5211GetMibCycleCounts,
+	.ah_enableDfs			= ar5211EnableDfs,
+	.ah_getDfsThresh		= ar5211GetDfsThresh,
+	/* XXX procRadarEvent */
+	/* XXX isFastClockEnabled */
 
 	/* Key Cache Functions */
 	.ah_getKeyCacheSize		= ar5211GetKeyCacheSize,
@@ -449,7 +459,7 @@
 }
 
 static void
-ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
 }
 
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c
--- a/head/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c 217684 2011-01-21 05:21:00Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c 235206 2012-05-09 18:17:01Z adrian $
  */
 #include "opt_ah.h"
 
@@ -688,3 +688,34 @@
 	}
 	return AH_FALSE;
 }
+
+/*
+ * Return what percentage of the extension channel is busy.
+ * This is always disabled for AR5211 series NICs.
+ */
+uint32_t
+ar5211Get11nExtBusy(struct ath_hal *ah)
+{
+	return (0);
+}
+
+
+/*
+ * There's no channel survey support for the AR5211.
+ */
+HAL_BOOL
+ar5211GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample)
+{
+
+	return (AH_FALSE);
+}
+
+void
+ar5211EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+}
+
+void
+ar5211GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c
--- a/head/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c 204644 2010-03-03 17:32:32Z rpaulo $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c 238278 2012-07-09 07:19:11Z adrian $
  */
 #include "opt_ah.h"
 
@@ -30,8 +30,10 @@
  * Get the RXDP.
  */
 uint32_t
-ar5211GetRxDP(struct ath_hal *ah)
+ar5211GetRxDP(struct ath_hal *ah, HAL_RX_QUEUE qtype)
 {
+
+	HALASSERT(qtype == HAL_RX_QUEUE_HP);
 	return OS_REG_READ(ah, AR_RXDP);
 }
 
@@ -39,8 +41,10 @@
  * Set the RxDP.
  */
 void
-ar5211SetRxDP(struct ath_hal *ah, uint32_t rxdp)
+ar5211SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE qtype)
 {
+
+	HALASSERT(qtype == HAL_RX_QUEUE_HP);
 	OS_REG_WRITE(ah, AR_RXDP, rxdp);
 	HALASSERT(OS_REG_READ(ah, AR_RXDP) == rxdp);
 }
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c
--- a/head/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c 223459 2011-06-23 02:38:36Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c 238607 2012-07-19 02:25:14Z adrian $
  */
 #include "opt_ah.h"
 
@@ -671,3 +671,27 @@
 	return AH_FALSE;
 }
 
+
+void
+ar5211SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link)
+{
+	struct ar5211_desc *ads = AR5211DESC(ds);
+
+	ads->ds_link = link;
+}
+
+void
+ar5211GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link)
+{
+	struct ar5211_desc *ads = AR5211DESC(ds);
+
+	*link = ads->ds_link;
+}
+
+void
+ar5211GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr)
+{
+	struct ar5211_desc *ads = AR5211DESC(ds);
+
+	*linkptr = &ads->ds_link;
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5212/ar5212.h
--- a/head/sys/dev/ath/ath_hal/ar5212/ar5212.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5212/ar5212.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212.h 230791 2012-01-30 23:07:27Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212.h 238607 2012-07-19 02:25:14Z adrian $
  */
 #ifndef _ATH_AR5212_H_
 #define _ATH_AR5212_H_
@@ -320,6 +320,7 @@
 	struct ar5212AniParams ah_aniParams5;	/* 5GHz parameters */
 	struct ar5212AniState	*ah_curani;	/* cached last reference */
 	struct ar5212AniState	ah_ani[AH_MAXCHAN]; /* per-channel state */
+	HAL_CHANNEL_SURVEY	ah_chansurvey; /* channel survey */
 
 	/* AR5416 uses some of the AR5212 ANI code; these are the ANI methods */
 	HAL_BOOL	(*ah_aniControl) (struct ath_hal *, HAL_ANI_CMD cmd, int param);
@@ -510,14 +511,16 @@
 		void **result, uint32_t *resultsize);
 extern	HAL_STATUS ar5212SetQuiet(struct ath_hal *ah, uint32_t period,
 		uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag);
+extern	HAL_BOOL ar5212GetMibCycleCounts(struct ath_hal *,
+		HAL_SURVEY_SAMPLE *);
 
 extern	HAL_BOOL ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode,
 		int setChip);
 extern	HAL_POWER_MODE ar5212GetPowerMode(struct ath_hal *ah);
 extern	HAL_BOOL ar5212GetPowerStatus(struct ath_hal *ah);
 
-extern	uint32_t ar5212GetRxDP(struct ath_hal *ath);
-extern	void ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp);
+extern	uint32_t ar5212GetRxDP(struct ath_hal *ath, HAL_RX_QUEUE);
+extern	void ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE);
 extern	void ar5212EnableReceive(struct ath_hal *ah);
 extern	HAL_BOOL ar5212StopDmaReceive(struct ath_hal *ah);
 extern	void ar5212StartPcuReceive(struct ath_hal *ah);
@@ -599,6 +602,12 @@
 extern  void ar5212IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *);
 extern	HAL_BOOL ar5212GetTxCompletionRates(struct ath_hal *ah,
 		const struct ath_desc *ds0, int *rates, int *tries);
+extern	void ar5212SetTxDescLink(struct ath_hal *ah, void *ds,
+		uint32_t link);
+extern	void ar5212GetTxDescLink(struct ath_hal *ah, void *ds,
+		uint32_t *link);
+extern	void ar5212GetTxDescLinkPtr(struct ath_hal *ah, void *ds,
+		uint32_t **linkptr);
 
 extern	const HAL_RATE_TABLE *ar5212GetRateTable(struct ath_hal *, u_int mode);
 
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c
--- a/head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c 230791 2012-01-30 23:07:27Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c 238607 2012-07-19 02:25:14Z adrian $
  */
 #include "opt_ah.h"
 
@@ -29,7 +29,8 @@
 #define AH_5212_COMMON
 #include "ar5212/ar5212.ini"
 
-static void ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore);
+static void ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore,
+		HAL_BOOL power_off);
 static void ar5212DisablePCIE(struct ath_hal *ah);
 
 static const struct ath_hal_private ar5212hal = {{
@@ -70,6 +71,9 @@
 	.ah_getTxIntrQueue		= ar5212GetTxIntrQueue,
 	.ah_reqTxIntrDesc 		= ar5212IntrReqTxDesc,
 	.ah_getTxCompletionRates	= ar5212GetTxCompletionRates,
+	.ah_setTxDescLink		= ar5212SetTxDescLink,
+	.ah_getTxDescLink		= ar5212GetTxDescLink,
+	.ah_getTxDescLinkPtr		= ar5212GetTxDescLinkPtr,
 
 	/* RX Functions */
 	.ah_getRxDP			= ar5212GetRxDP,
@@ -125,9 +129,10 @@
 	.ah_getAckCTSRate		= ar5212GetAckCTSRate,
 	.ah_setCTSTimeout		= ar5212SetCTSTimeout,
 	.ah_getCTSTimeout		= ar5212GetCTSTimeout,
-	.ah_setDecompMask               = ar5212SetDecompMask,
-	.ah_setCoverageClass            = ar5212SetCoverageClass,
+	.ah_setDecompMask		= ar5212SetDecompMask,
+	.ah_setCoverageClass		= ar5212SetCoverageClass,
 	.ah_setQuiet			= ar5212SetQuiet,
+	.ah_getMibCycleCounts		= ar5212GetMibCycleCounts,
 
 	/* DFS Functions */
 	.ah_enableDfs			= ar5212EnableDfs,
@@ -369,7 +374,7 @@
 
 	if (AH_PRIVATE(ah)->ah_ispcie) {
 		/* XXX: build flag to disable this? */
-		ath_hal_configPCIE(ah, AH_FALSE);
+		ath_hal_configPCIE(ah, AH_FALSE, AH_FALSE);
 	}
 
 	if (!ar5212ChipTest(ah)) {
@@ -665,7 +670,7 @@
  * XXX Clean up the magic numbers.
  */
 static void
-ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar5212ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
 	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
 	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c
--- a/head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c 230791 2012-01-30 23:07:27Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c 234873 2012-05-01 14:48:51Z adrian $
  */
 #include "opt_ah.h"
 
@@ -1090,6 +1090,11 @@
 				return AH_FALSE;
 			return ar5212AniSetParams(ah, args, args);
 		}
+		break;
+	case HAL_DIAG_CHANSURVEY:
+		*result = &ahp->ah_chansurvey;
+		*resultsize = sizeof(HAL_CHANNEL_SURVEY);
+		return AH_TRUE;
 	}
 	return AH_FALSE;
 }
@@ -1243,3 +1248,13 @@
 {
 	return 0;
 }
+
+/*
+ * There's no channel survey support for the AR5211.
+ */
+HAL_BOOL
+ar5212GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample)
+{
+
+	return (AH_FALSE);
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c
--- a/head/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c 220423 2011-04-07 13:14:51Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c 238278 2012-07-09 07:19:11Z adrian $
  */
 #include "opt_ah.h"
 
@@ -29,8 +29,10 @@
  * Get the RXDP.
  */
 uint32_t
-ar5212GetRxDP(struct ath_hal *ath)
+ar5212GetRxDP(struct ath_hal *ath, HAL_RX_QUEUE qtype)
 {
+
+	HALASSERT(qtype == HAL_RX_QUEUE_HP);
 	return OS_REG_READ(ath, AR_RXDP);
 }
 
@@ -38,8 +40,10 @@
  * Set the RxDP.
  */
 void
-ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp)
+ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE qtype)
 {
+
+	HALASSERT(qtype == HAL_RX_QUEUE_HP);
 	OS_REG_WRITE(ah, AR_RXDP, rxdp);
 	HALASSERT(OS_REG_READ(ah, AR_RXDP) == rxdp);
 }
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c
--- a/head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c 234450 2012-04-19 03:26:21Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c 234774 2012-04-28 22:03:19Z adrian $
  */
 #include "opt_ah.h"
 
@@ -195,6 +195,9 @@
 		saveFrameSeqCount = OS_REG_READ(ah, AR_D_SEQNUM);
 	} else
 		saveFrameSeqCount = 0;		/* NB: silence compiler */
+
+	/* Blank the channel survey statistics */
+	OS_MEMZERO(&ahp->ah_chansurvey, sizeof(ahp->ah_chansurvey));
 #if 0
 	/*
 	 * XXX disable for now; this appears to sometimes cause OFDM
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c
--- a/head/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c 226765 2011-10-25 23:28:16Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c 238607 2012-07-19 02:25:14Z adrian $
  */
 #include "opt_ah.h"
 
@@ -971,3 +971,27 @@
 
 	return AH_TRUE;
 }  
+
+void
+ar5212SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link)
+{
+	struct ar5212_desc *ads = AR5212DESC(ds);
+
+	ads->ds_link = link;
+}
+
+void
+ar5212GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link)
+{
+	struct ar5212_desc *ads = AR5212DESC(ds);
+
+	*link = ads->ds_link;
+}
+
+void
+ar5212GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr)
+{
+	struct ar5212_desc *ads = AR5212DESC(ds);
+
+	*linkptr = &ads->ds_link;
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416.h
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416.h 233895 2012-04-04 21:49:49Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416.h 237611 2012-06-26 22:16:53Z adrian $
  */
 #ifndef _ATH_AR5416_H_
 #define _ATH_AR5416_H_
@@ -132,6 +132,28 @@
 	int		initPDADC;
 
 	int		ah_need_an_top2_fixup;	/* merlin or later chips that may need this workaround */
+
+	/*
+	 * Bluetooth coexistence static setup according to the registry
+	 */
+	HAL_BT_MODULE ah_btModule;            /* Bluetooth module identifier */
+	uint8_t		ah_btCoexConfigType;  /* BT coex configuration */
+	uint8_t		ah_btActiveGpioSelect;  /* GPIO pin for BT_ACTIVE */
+	uint8_t		ah_btPriorityGpioSelect; /* GPIO pin for BT_PRIORITY */
+	uint8_t		ah_wlanActiveGpioSelect; /* GPIO pin for WLAN_ACTIVE */
+	uint8_t		ah_btActivePolarity;  /* Polarity of BT_ACTIVE */
+	HAL_BOOL	ah_btCoexSingleAnt;   /* Single or dual antenna configuration */
+	uint8_t		ah_btWlanIsolation;   /* Isolation between BT and WLAN in dB */
+
+	/*
+	 * Bluetooth coexistence runtime settings
+	 */
+	HAL_BOOL	ah_btCoexEnabled;     /* If Bluetooth coexistence is enabled */
+	uint32_t	ah_btCoexMode;        /* Register setting for AR_BT_COEX_MODE */
+	uint32_t	ah_btCoexBTWeight;    /* Register setting for AR_BT_COEX_WEIGHT */
+	uint32_t	ah_btCoexWLANWeight;  /* Register setting for AR_BT_COEX_WEIGHT */
+	uint32_t	ah_btCoexMode2;       /* Register setting for AR_BT_COEX_MODE2 */
+	uint32_t	ah_btCoexFlag;        /* Special tuning flags for BT coex */
 };
 #define	AH5416(_ah)	((struct ath_hal_5416 *)(_ah))
 
@@ -173,6 +195,21 @@
 		const HAL_BEACON_STATE *);
 extern	uint64_t ar5416GetNextTBTT(struct ath_hal *);
 
+/* ar5416_btcoex.c */
+extern	void ar5416SetBTCoexInfo(struct ath_hal *ah,
+		HAL_BT_COEX_INFO *btinfo);
+extern	void ar5416BTCoexConfig(struct ath_hal *ah,
+		HAL_BT_COEX_CONFIG *btconf);
+extern	void ar5416BTCoexSetQcuThresh(struct ath_hal *ah, int qnum);
+extern	void ar5416BTCoexSetWeights(struct ath_hal *ah, uint32_t stompType);
+extern	void ar5416BTCoexSetupBmissThresh(struct ath_hal *ah,
+		uint32_t thresh);
+extern	void ar5416BTCoexSetParameter(struct ath_hal *ah, uint32_t type,
+		uint32_t value);
+extern	void ar5416BTCoexDisable(struct ath_hal *ah);
+extern	int ar5416BTCoexEnable(struct ath_hal *ah);
+extern	void ar5416InitBTCoex(struct ath_hal *ah);
+
 extern	HAL_BOOL ar5416EepromRead(struct ath_hal *, u_int off, uint16_t *data);
 extern	HAL_BOOL ar5416EepromWrite(struct ath_hal *, u_int off, uint16_t data);
 
@@ -196,9 +233,8 @@
 extern	HAL_BOOL ar5416SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING);
 extern	HAL_BOOL ar5416SetDecompMask(struct ath_hal *, uint16_t, int);
 extern	void ar5416SetCoverageClass(struct ath_hal *, uint8_t, int);
-extern	uint32_t ar5416GetMibCycleCountsPct(struct ath_hal *ah,
-    uint32_t *rxc_pcnt, uint32_t *rxextc_pcnt, uint32_t *rxf_pcnt,
-    uint32_t *txf_pcnt);
+extern	HAL_BOOL ar5416GetMibCycleCounts(struct ath_hal *ah,
+	    HAL_SURVEY_SAMPLE *hsample);
 extern	uint32_t ar5416Get11nExtBusy(struct ath_hal *ah);
 extern	void ar5416Set11nMac2040(struct ath_hal *ah, HAL_HT_MACMODE mode);
 extern	HAL_HT_RXCLEAR ar5416Get11nRxClear(struct ath_hal *ah);
@@ -234,6 +270,7 @@
 
 extern	uint32_t ar5416GetRxFilter(struct ath_hal *ah);
 extern	void ar5416SetRxFilter(struct ath_hal *ah, uint32_t bits);
+extern	HAL_BOOL ar5416StopDmaReceive(struct ath_hal *ah);
 extern	void ar5416StartPcuReceive(struct ath_hal *ah);
 extern	void ar5416StopPcuReceive(struct ath_hal *ah);
 extern	HAL_BOOL ar5416SetupRxDesc(struct ath_hal *,
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 227468 2011-11-12 16:47:23Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 234873 2012-05-01 14:48:51Z adrian $
  */
 #include "opt_ah.h"
 
@@ -804,21 +804,50 @@
  * deducting the cycles spent tx'ing and rx'ing from the total
  * cycle count since our last call.  A return value <0 indicates
  * an invalid/inconsistent time.
+ *
+ * This may be called with ANI disabled; in which case simply keep
+ * the statistics and don't write to the aniState pointer.
+ *
+ * XXX TODO: Make this cleaner!
  */
 static int32_t
 ar5416AniGetListenTime(struct ath_hal *ah)
 {
 	struct ath_hal_5212 *ahp = AH5212(ah);
-	struct ar5212AniState *aniState;
-	uint32_t rxc_pct, extc_pct, rxf_pct, txf_pct;
-	int32_t listenTime;
+	struct ar5212AniState *aniState = NULL;
+	int32_t listenTime = 0;
 	int good;
+	HAL_SURVEY_SAMPLE hs;
+	HAL_CHANNEL_SURVEY *cs = AH_NULL;
 
-	good = ar5416GetMibCycleCountsPct(ah,
-	&rxc_pct, &extc_pct, &rxf_pct, &txf_pct);
+	/*
+	 * We shouldn't see ah_curchan be NULL, but just in case..
+	 */
+	if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) {
+		ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__);
+		return (0);
+	}
 
-	aniState = ahp->ah_curani;
-	if (good == 0) {
+	cs = &ahp->ah_chansurvey;
+
+	/*
+	 * Fetch the current statistics, squirrel away the current
+	 * sample, bump the sequence/sample counter.
+	 */
+	OS_MEMZERO(&hs, sizeof(hs));
+	good = ar5416GetMibCycleCounts(ah, &hs);
+	if (cs != AH_NULL) {
+		OS_MEMCPY(&cs->samples[cs->cur_sample], &hs, sizeof(hs));
+		cs->samples[cs->cur_sample].seq_num = cs->cur_seq;
+		cs->cur_sample =
+		    (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT;
+		cs->cur_seq++;
+	}
+
+	if (ANI_ENA(ah))
+		aniState = ahp->ah_curani;
+
+	if (good == AH_FALSE) {
 		/*
 		 * Cycle counter wrap (or initial call); it's not possible
 		 * to accurately calculate a value because the registers
@@ -826,18 +855,28 @@
 		 */
 		listenTime = 0;
 		ahp->ah_stats.ast_ani_lzero++;
-	} else {
-		int32_t ccdelta = AH5416(ah)->ah_cycleCount - aniState->cycleCount;
-		int32_t rfdelta = AH5416(ah)->ah_rxBusy - aniState->rxFrameCount;
-		int32_t tfdelta = AH5416(ah)->ah_txBusy - aniState->txFrameCount;
+	} else if (ANI_ENA(ah)) {
+		/*
+		 * Only calculate and update the cycle count if we have
+		 * an ANI state.
+		 */
+		int32_t ccdelta =
+		    AH5416(ah)->ah_cycleCount - aniState->cycleCount;
+		int32_t rfdelta =
+		    AH5416(ah)->ah_rxBusy - aniState->rxFrameCount;
+		int32_t tfdelta =
+		    AH5416(ah)->ah_txBusy - aniState->txFrameCount;
 		listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE;
 	}
-	aniState->cycleCount = AH5416(ah)->ah_cycleCount;
-	aniState->txFrameCount = AH5416(ah)->ah_rxBusy;
-	aniState->rxFrameCount = AH5416(ah)->ah_txBusy;
 
-	HALDEBUG(ah, HAL_DEBUG_ANI, "rxc=%d, extc=%d, rxf=%d, txf=%d\n",
-	    rxc_pct, extc_pct, rxf_pct, txf_pct);
+	/*
+	 * Again, only update ANI state if we have it.
+	 */
+	if (ANI_ENA(ah)) {
+		aniState->cycleCount = AH5416(ah)->ah_cycleCount;
+		aniState->txFrameCount = AH5416(ah)->ah_rxBusy;
+		aniState->rxFrameCount = AH5416(ah)->ah_txBusy;
+	}
 
 	return listenTime;
 }
@@ -902,13 +941,13 @@
 	const struct ar5212AniParams *params;
 	int32_t listenTime;
 
+	/* Always update from the MIB, for statistics gathering */
+	listenTime = ar5416AniGetListenTime(ah);
+
 	/* XXX can aniState be null? */
 	if (aniState == AH_NULL)
 		return;
 
-	/* Always update from the MIB, for statistics gathering */
-	listenTime = ar5416AniGetListenTime(ah);
-
 	if (!ANI_ENA(ah))
 		return;
 
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c 231368 2012-02-10 09:58:20Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c 236017 2012-05-25 17:53:57Z adrian $
  */
 #include "opt_ah.h"
 
@@ -30,7 +30,9 @@
 
 #include "ar5416/ar5416.ini"
 
-static void ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore);
+static void ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore,
+		HAL_BOOL power_off);
+static void ar5416DisablePCIE(struct ath_hal *ah);
 static void ar5416WriteIni(struct ath_hal *ah,
 	    const struct ieee80211_channel *chan);
 static void ar5416SpurMitigate(struct ath_hal *ah,
@@ -99,6 +101,7 @@
 	ah->ah_phyDisable		= ar5416PhyDisable;
 	ah->ah_disable			= ar5416Disable;
 	ah->ah_configPCIE		= ar5416ConfigPCIE;
+	ah->ah_disablePCIE		= ar5416DisablePCIE;
 	ah->ah_perCalibration		= ar5416PerCalibration;
 	ah->ah_perCalibrationN		= ar5416PerCalibrationN,
 	ah->ah_resetCalValid		= ar5416ResetCalValid,
@@ -119,6 +122,7 @@
 	/* Receive Functions */
 	ah->ah_getRxFilter		= ar5416GetRxFilter;
 	ah->ah_setRxFilter		= ar5416SetRxFilter;
+	ah->ah_stopDmaReceive		= ar5416StopDmaReceive;
 	ah->ah_startPcuReceive		= ar5416StartPcuReceive;
 	ah->ah_stopPcuReceive		= ar5416StopPcuReceive;
 	ah->ah_setupRxDesc		= ar5416SetupRxDesc;
@@ -144,6 +148,7 @@
 	ah->ah_setDecompMask		= ar5416SetDecompMask;
 	ah->ah_setCoverageClass		= ar5416SetCoverageClass;
 	ah->ah_setQuiet			= ar5416SetQuiet;
+	ah->ah_getMibCycleCounts	= ar5416GetMibCycleCounts;
 
 	ah->ah_resetKeyCacheEntry	= ar5416ResetKeyCacheEntry;
 	ah->ah_setKeyCacheEntry		= ar5416SetKeyCacheEntry;
@@ -455,22 +460,73 @@
 ar5416AttachPCIE(struct ath_hal *ah)
 {
 	if (AH_PRIVATE(ah)->ah_ispcie)
-		ath_hal_configPCIE(ah, AH_FALSE);
+		ath_hal_configPCIE(ah, AH_FALSE, AH_FALSE);
 	else
 		ath_hal_disablePCIE(ah);
 }
 
 static void
-ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar5416ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
-	if (AH_PRIVATE(ah)->ah_ispcie && !restore) {
+
+	/* This is only applicable for AR5418 (AR5416 PCIe) */
+	if (! AH_PRIVATE(ah)->ah_ispcie)
+		return;
+
+	if (! restore) {
 		ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0);
 		OS_DELAY(1000);
+	}
+
+	if (power_off) {		/* Power-off */
+		/* clear bit 19 to disable L1 */
+		OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
+	} else {			/* Power-on */
+		/* Set default WAR values for Owl */
+		OS_REG_WRITE(ah, AR_WA, AR_WA_DEFAULT);
+
+		/* set bit 19 to allow forcing of pcie core into L1 state */
 		OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
-		OS_REG_WRITE(ah, AR_WA, AR_WA_DEFAULT);
 	}
 }
 
+/*
+ * Disable PCIe PHY if PCIe isn't used.
+ */
+static void
+ar5416DisablePCIE(struct ath_hal *ah)
+{
+
+	/* PCIe? Don't */
+	if (AH_PRIVATE(ah)->ah_ispcie)
+		return;
+
+	/* .. Only applicable for AR5416v2 or later */
+	if (! (AR_SREV_OWL(ah) && AR_SREV_OWL_20_OR_LATER(ah)))
+		return;
+
+	OS_REG_WRITE_BUFFER_ENABLE(ah);
+
+	/*
+	 * Disable the PCIe PHY.
+	 */
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029);
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824);
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579);
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000);
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
+	OS_REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007);
+
+	/* Load the new settings */
+	OS_REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
+
+	OS_REG_WRITE_BUFFER_FLUSH(ah);
+	OS_REG_WRITE_BUFFER_DISABLE(ah);
+}
+
 static void
 ar5416WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan)
 {
@@ -581,6 +637,8 @@
         AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK |
         AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK);
 
+    OS_REG_WRITE_BUFFER_ENABLE(ah);
+
     OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4_CHAIN(0), new);
 
     new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL |
@@ -765,6 +823,9 @@
           | (mask_p[47] <<  2) | (mask_p[46] <<  0);
     OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask);
     OS_REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask);
+
+    OS_REG_WRITE_BUFFER_FLUSH(ah);
+    OS_REG_WRITE_BUFFER_DISABLE(ah);
 }
 
 /*
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Atheros Communications, Inc.
+ * Copyright (c) 2008-2010, Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c 237611 2012-06-26 22:16:53Z adrian $
+ */
+
+#include "opt_ah.h"
+
+#include "ah.h"
+#include "ah_internal.h"
+#include "ah_devid.h"
+#ifdef	AH_DEBUG
+#include "ah_desc.h"                    /* NB: for HAL_PHYERR* */
+#endif
+
+#include "ar5416/ar5416.h"
+#include "ar5416/ar5416reg.h"
+#include "ar5416/ar5416phy.h"
+#include "ar5416/ar5416desc.h" /* AR5416_CONTTXMODE */
+#include "ar5416/ar5416_btcoex.h"
+
+void
+ar5416SetBTCoexInfo(struct ath_hal *ah, HAL_BT_COEX_INFO *btinfo)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	ahp->ah_btModule = btinfo->bt_module;
+	ahp->ah_btCoexConfigType = btinfo->bt_coex_config;
+	ahp->ah_btActiveGpioSelect = btinfo->bt_gpio_bt_active;
+	ahp->ah_btPriorityGpioSelect = btinfo->bt_gpio_bt_priority;
+	ahp->ah_wlanActiveGpioSelect = btinfo->bt_gpio_wlan_active;
+	ahp->ah_btActivePolarity = btinfo->bt_active_polarity;
+	ahp->ah_btCoexSingleAnt = btinfo->bt_single_ant;
+	ahp->ah_btWlanIsolation = btinfo->bt_isolation;
+}
+
+void
+ar5416BTCoexConfig(struct ath_hal *ah, HAL_BT_COEX_CONFIG *btconf)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+	HAL_BOOL rxClearPolarity = btconf->bt_rxclear_polarity;
+
+	/*
+	 * For Kiwi and Osprey, the polarity of rx_clear is active high.
+	 * The bt_rxclear_polarity flag from ath(4) needs to be inverted.
+	 */
+	if (AR_SREV_KIWI(ah)) {
+		rxClearPolarity = !btconf->bt_rxclear_polarity;
+	}
+
+	ahp->ah_btCoexMode = (ahp->ah_btCoexMode & AR_BT_QCU_THRESH) |
+	    SM(btconf->bt_time_extend, AR_BT_TIME_EXTEND) |
+	    SM(btconf->bt_txstate_extend, AR_BT_TXSTATE_EXTEND) |
+	    SM(btconf->bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) |
+	    SM(btconf->bt_mode, AR_BT_MODE) |
+	    SM(btconf->bt_quiet_collision, AR_BT_QUIET) |
+	    SM(rxClearPolarity, AR_BT_RX_CLEAR_POLARITY) |
+	    SM(btconf->bt_priority_time, AR_BT_PRIORITY_TIME) |
+	    SM(btconf->bt_first_slot_time, AR_BT_FIRST_SLOT_TIME);
+
+	ahp->ah_btCoexMode2 |= SM(btconf->bt_hold_rxclear,
+	    AR_BT_HOLD_RX_CLEAR);
+
+	if (ahp->ah_btCoexSingleAnt == AH_FALSE) {
+		/* Enable ACK to go out even though BT has higher priority. */
+		ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT;
+	}
+}
+
+void
+ar5416BTCoexSetQcuThresh(struct ath_hal *ah, int qnum)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	ahp->ah_btCoexMode |= SM(qnum, AR_BT_QCU_THRESH);
+}
+
+void
+ar5416BTCoexSetWeights(struct ath_hal *ah, u_int32_t stompType)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	if (AR_SREV_KIWI_10_OR_LATER(ah)) {
+		/* TODO: TX RX seperate is not enabled. */
+		switch (stompType) {
+		case HAL_BT_COEX_STOMP_ALL:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight = AR5416_STOMP_ALL_WLAN_WGHT;
+			break;
+		case HAL_BT_COEX_STOMP_LOW:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight = AR5416_STOMP_LOW_WLAN_WGHT;
+			break;
+		case HAL_BT_COEX_STOMP_ALL_FORCE:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight =
+			    AR5416_STOMP_ALL_FORCE_WLAN_WGHT;
+			break;
+		case HAL_BT_COEX_STOMP_LOW_FORCE:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight =
+			    AR5416_STOMP_LOW_FORCE_WLAN_WGHT;
+			break;
+		case HAL_BT_COEX_STOMP_NONE:
+		case HAL_BT_COEX_NO_STOMP:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight = AR5416_STOMP_NONE_WLAN_WGHT;
+			break;
+		default:
+			/* There is a forceWeight from registry */
+			ahp->ah_btCoexBTWeight = stompType & 0xffff;
+			ahp->ah_btCoexWLANWeight = stompType >> 16;
+			break;
+		}
+	} else {
+		switch (stompType) {
+		case HAL_BT_COEX_STOMP_ALL:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight = AR5416_STOMP_ALL_WLAN_WGHT;
+			break;
+		case HAL_BT_COEX_STOMP_LOW:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight = AR5416_STOMP_LOW_WLAN_WGHT;
+			break;
+		case HAL_BT_COEX_STOMP_ALL_FORCE:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight =
+			    AR5416_STOMP_ALL_FORCE_WLAN_WGHT;
+			break;
+		case HAL_BT_COEX_STOMP_LOW_FORCE:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight =
+			    AR5416_STOMP_LOW_FORCE_WLAN_WGHT;
+			break;
+		case HAL_BT_COEX_STOMP_NONE:
+		case HAL_BT_COEX_NO_STOMP:
+			ahp->ah_btCoexBTWeight = AR5416_BT_WGHT;
+			ahp->ah_btCoexWLANWeight = AR5416_STOMP_NONE_WLAN_WGHT;
+			break;
+		default:
+			/* There is a forceWeight from registry */
+			ahp->ah_btCoexBTWeight = stompType & 0xffff;
+			ahp->ah_btCoexWLANWeight = stompType >> 16;
+			break;
+		}
+	}
+}
+
+void
+ar5416BTCoexSetupBmissThresh(struct ath_hal *ah, u_int32_t thresh)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	ahp->ah_btCoexMode2 |= SM(thresh, AR_BT_BCN_MISS_THRESH);
+}
+
+/*
+ * There is no antenna diversity for Owl, Kiwi, etc.
+ *
+ * Kite will override this particular method.
+ */
+static void
+ar5416BTCoexAntennaDiversity(struct ath_hal *ah)
+{
+}
+
+void
+ar5416BTCoexSetParameter(struct ath_hal *ah, u_int32_t type, u_int32_t value)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	switch (type) {
+	case HAL_BT_COEX_SET_ACK_PWR:
+		if (value) {
+			ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_LOW_ACK_PWR;
+			OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_LOW_ACK_POWER);
+		} else {
+			ahp->ah_btCoexFlag &= ~HAL_BT_COEX_FLAG_LOW_ACK_PWR;
+			OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_HIGH_ACK_POWER);
+		}
+		break;
+	case HAL_BT_COEX_ANTENNA_DIVERSITY:
+		/* This is overridden for Kite */
+		break;
+#if 0
+        case HAL_BT_COEX_LOWER_TX_PWR:
+            if (value) {
+                if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) == 0) {
+                    ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_LOWER_TX_PWR;
+		    AH_PRIVATE(ah)->ah_config.ath_hal_desc_tpc = 1;
+                    ar5416SetTxPowerLimit(ah, AH_PRIVATE(ah)->ah_power_limit, AH_PRIVATE(ah)->ah_extra_txpow, 0);
+                }
+            }
+            else {
+                if (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) {
+                    ahp->ah_btCoexFlag &= ~HAL_BT_COEX_FLAG_LOWER_TX_PWR;
+		    AH_PRIVATE(ah)->ah_config.ath_hal_desc_tpc = 0;
+                    ar5416SetTxPowerLimit(ah, AH_PRIVATE(ah)->ah_power_limit, AH_PRIVATE(ah)->ah_extra_txpow, 0);
+                }
+            }
+            break;
+#endif
+	default:
+			break;
+	}
+}
+
+void
+ar5416BTCoexDisable(struct ath_hal *ah)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	/* Always drive rx_clear_external output as 0 */
+	ar5416GpioSet(ah, ahp->ah_wlanActiveGpioSelect, 0);
+	ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect,
+	    HAL_GPIO_OUTPUT_MUX_AS_OUTPUT);
+
+	if (AR_SREV_9271(ah)) {
+		/*
+		 * Set wlanActiveGpio to input when disabling BT-COEX to
+		 * reduce power consumption
+		 */
+		ar5416GpioCfgInput(ah, ahp->ah_wlanActiveGpioSelect);
+	}
+
+	if (ahp->ah_btCoexSingleAnt == AH_TRUE) {
+		OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE,
+		    1);
+		OS_REG_RMW_FIELD(ah, AR_MISC_MODE, AR_PCU_BT_ANT_PREVENT_RX,
+		    0);
+	}
+
+	OS_REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE);
+	OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0);
+	if (AR_SREV_KIWI_10_OR_LATER(ah))
+		OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT2, 0);
+	OS_REG_WRITE(ah, AR_BT_COEX_MODE2, 0);
+
+	ahp->ah_btCoexEnabled = AH_FALSE;
+}
+
+int
+ar5416BTCoexEnable(struct ath_hal *ah)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	/* Program coex mode and weight registers to actually enable coex */
+	OS_REG_WRITE(ah, AR_BT_COEX_MODE, ahp->ah_btCoexMode);
+	OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT,
+	    SM(ahp->ah_btCoexWLANWeight & 0xFFFF, AR_BT_WL_WGHT) |
+	    SM(ahp->ah_btCoexBTWeight & 0xFFFF, AR_BT_BT_WGHT));
+	if (AR_SREV_KIWI_10_OR_LATER(ah)) {
+	OS_REG_WRITE(ah, AR_BT_COEX_WEIGHT2,
+	    SM(ahp->ah_btCoexWLANWeight >> 16, AR_BT_WL_WGHT));
+	}
+	OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2);
+
+#if 0
+    /* Added Select GPIO5~8 instaed SPI */
+    if (AR_SREV_9271(ah)) {
+        val = OS_REG_READ(ah, AR9271_CLOCK_CONTROL);
+        val &= 0xFFFFFEFF;
+        OS_REG_WRITE(ah, AR9271_CLOCK_CONTROL, val);
+    }
+#endif
+
+	if (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_LOW_ACK_PWR)
+		OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_LOW_ACK_POWER);
+	else
+		OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_HIGH_ACK_POWER);
+
+	if (ahp->ah_btCoexSingleAnt == AH_TRUE) {
+		OS_REG_RMW_FIELD(ah, AR_QUIET1,
+		    AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
+		/* XXX should update miscMode? */
+		OS_REG_RMW_FIELD(ah, AR_MISC_MODE,
+		    AR_PCU_BT_ANT_PREVENT_RX, 1);
+	} else {
+		OS_REG_RMW_FIELD(ah, AR_QUIET1,
+		    AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
+		/* XXX should update miscMode? */
+		OS_REG_RMW_FIELD(ah, AR_MISC_MODE,
+		    AR_PCU_BT_ANT_PREVENT_RX, 0);
+	}
+
+	if (ahp->ah_btCoexConfigType == HAL_BT_COEX_CFG_3WIRE) {
+		/* For 3-wire, configure the desired GPIO port for rx_clear */
+		ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect,
+		    HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE);
+	} else {
+		/*
+		 * For 2-wire, configure the desired GPIO port
+		 * for TX_FRAME output
+		 */
+		ar5416GpioCfgOutput(ah, ahp->ah_wlanActiveGpioSelect,
+		    HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME);
+	}
+
+	/*
+	 * Enable a weak pull down on BT_ACTIVE.
+	 * When BT device is disabled, BT_ACTIVE might be floating.
+	 */
+	OS_REG_RMW(ah, AR_GPIO_PDPU,
+	    (0x2 << (ahp->ah_btActiveGpioSelect * 2)),
+	    (0x3 << (ahp->ah_btActiveGpioSelect * 2)));
+
+	ahp->ah_btCoexEnabled = AH_TRUE;
+
+	return (0);
+}
+
+void
+ar5416InitBTCoex(struct ath_hal *ah)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	if (ahp->ah_btCoexConfigType == HAL_BT_COEX_CFG_3WIRE) {
+		OS_REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+		    (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB |
+		    AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB));
+
+		/*
+		 * Set input mux for bt_prority_async and
+		 * bt_active_async to GPIO pins
+		 */
+		OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+		    AR_GPIO_INPUT_MUX1_BT_ACTIVE,
+		    ahp->ah_btActiveGpioSelect);
+		OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+		    AR_GPIO_INPUT_MUX1_BT_PRIORITY,
+		    ahp->ah_btPriorityGpioSelect);
+
+		/*
+		 * Configure the desired GPIO ports for input
+		 */
+		ar5416GpioCfgInput(ah, ahp->ah_btActiveGpioSelect);
+		ar5416GpioCfgInput(ah, ahp->ah_btPriorityGpioSelect);
+
+		if (AR_SREV_KITE(ah))
+			ar5416BTCoexAntennaDiversity(ah);
+
+		if (ahp->ah_btCoexEnabled)
+			ar5416BTCoexEnable(ah);
+		else
+			ar5416BTCoexDisable(ah);
+	} else if (ahp->ah_btCoexConfigType != HAL_BT_COEX_CFG_NONE) {
+		/* 2-wire */
+		if (ahp->ah_btCoexEnabled) {
+			/* Connect bt_active_async to baseband */
+			OS_REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+			    (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF |
+			     AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF));
+			OS_REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+			    AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB);
+
+			/*
+			 * Set input mux for bt_prority_async and
+			 * bt_active_async to GPIO pins
+			 */
+			OS_REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+			    AR_GPIO_INPUT_MUX1_BT_ACTIVE,
+                            ahp->ah_btActiveGpioSelect);
+
+			/* Configure the desired GPIO ports for input */
+			ar5416GpioCfgInput(ah, ahp->ah_btActiveGpioSelect);
+
+			/* Enable coexistence on initialization */
+			ar5416BTCoexEnable(ah);
+		}
+	}
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h 237621 2012-06-27 03:00:29Z adrian $
+ */
+
+#ifndef	__ATH_AR5416_BTCOEX_H__
+#define	__ATH_AR5416_BTCOEX_H__
+/*
+ * Weight table configurations.
+ */
+#define AR5416_BT_WGHT                     0xff55
+#define AR5416_STOMP_ALL_WLAN_WGHT         0xfcfc
+#define AR5416_STOMP_LOW_WLAN_WGHT         0xa8a8
+#define AR5416_STOMP_NONE_WLAN_WGHT        0x0000
+#define AR5416_STOMP_ALL_FORCE_WLAN_WGHT   0xffff       // Stomp BT even when WLAN is idle
+#define AR5416_STOMP_LOW_FORCE_WLAN_WGHT   0xaaaa       // Stomp BT even when WLAN is idle
+
+#endif	/* __ATH_AR5416_BTCOEX_H__ */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c 228836 2011-12-23 08:53:22Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c 237611 2012-06-26 22:16:53Z adrian $
  */
 #include "opt_ah.h"
 
@@ -85,23 +85,49 @@
 {
 	uint32_t gpio_shift, reg;
 
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+
 	HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins);
 
+	/*
+	 * This table maps the HAL GPIO pins to the actual hardware
+	 * values.
+	 */
+	static const u_int32_t MuxSignalConversionTable[] = {
+		AR_GPIO_OUTPUT_MUX_AS_OUTPUT,
+		AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED,
+		AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED,
+		AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED,
+		AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED,
+		AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL,
+		AR_GPIO_OUTPUT_MUX_AS_TX_FRAME,
+	};
+
 	HALDEBUG(ah, HAL_DEBUG_GPIO,
 	    "%s: gpio=%d, type=%d\n", __func__, gpio, type);
 
-	/* NB: type maps directly to hardware */
-	/* XXX this may not actually be the case, for anything but output */
-	cfgOutputMux(ah, gpio, type);
-	gpio_shift = gpio << 1;			/* 2 bits per output mode */
+	/*
+	 * Convert HAL signal type definitions to hardware-specific values.
+	 */
+	if (type >= N(MuxSignalConversionTable)) {
+		ath_hal_printf(ah, "%s: mux %d is invalid!\n",
+		    __func__,
+		    type);
+		return AH_FALSE;
+	}
+	cfgOutputMux(ah, gpio, MuxSignalConversionTable[type]);
 
+	/* 2 bits per output mode */
+	gpio_shift = gpio << 1;
+
+	/* Always drive, rather than tristate/drive low/drive high */
 	reg = OS_REG_READ(ah, AR_GPIO_OE_OUT);
 	reg &= ~(AR_GPIO_OE_OUT_DRV << gpio_shift);
-	/* Always drive, rather than tristate/drive low/drive high */
 	reg |= AR_GPIO_OE_OUT_DRV_ALL << gpio_shift;
 	OS_REG_WRITE(ah, AR_GPIO_OE_OUT, reg);
 
 	return AH_TRUE;
+#undef	N
 }
  
 /*
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c 231927 2012-02-20 03:07:07Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c 237868 2012-07-01 03:15:18Z adrian $
  */
 #include "opt_ah.h"
 
@@ -184,12 +184,11 @@
 /*
  * Return the busy for rx_frame, rx_clear, and tx_frame
  */
-uint32_t
-ar5416GetMibCycleCountsPct(struct ath_hal *ah, uint32_t *rxc_pcnt,
-    uint32_t *extc_pcnt, uint32_t *rxf_pcnt, uint32_t *txf_pcnt)
+HAL_BOOL
+ar5416GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample)
 {
 	struct ath_hal_5416 *ahp = AH5416(ah);
-	u_int32_t good = 1;
+	u_int32_t good = AH_TRUE;
 
 	/* XXX freeze/unfreeze mib counters */
 	uint32_t rc = OS_REG_READ(ah, AR_RCCNT);
@@ -206,36 +205,34 @@
 		 */
 		HALDEBUG(ah, HAL_DEBUG_ANY,
 			    "%s: cycle counter wrap. ExtBusy = 0\n", __func__);
-			good = 0;
+			good = AH_FALSE;
 	} else {
-		uint32_t cc_d = cc - ahp->ah_cycleCount;
-		uint32_t rc_d = rc - ahp->ah_ctlBusy;
-		uint32_t ec_d = ec - ahp->ah_extBusy;
-		uint32_t rf_d = rf - ahp->ah_rxBusy;
-		uint32_t tf_d = tf - ahp->ah_txBusy;
+		hsample->cycle_count = cc - ahp->ah_cycleCount;
+		hsample->chan_busy = rc - ahp->ah_ctlBusy;
+		hsample->ext_chan_busy = ec - ahp->ah_extBusy;
+		hsample->rx_busy = rf - ahp->ah_rxBusy;
+		hsample->tx_busy = tf - ahp->ah_txBusy;
+	}
 
-		if (cc_d != 0) {
-			*rxc_pcnt = rc_d * 100 / cc_d;
-			*rxf_pcnt = rf_d * 100 / cc_d;
-			*txf_pcnt = tf_d * 100 / cc_d;
-			*extc_pcnt = ec_d * 100 / cc_d;
-		} else {
-			good = 0;
-		}
-	}
+	/*
+	 * Keep a copy of the MIB results so the next sample has something
+	 * to work from.
+	 */
 	ahp->ah_cycleCount = cc;
 	ahp->ah_rxBusy = rf;
 	ahp->ah_ctlBusy = rc;
 	ahp->ah_txBusy = tf;
 	ahp->ah_extBusy = ec;
 
-	return good;
+	return (good);
 }
 
 /*
  * Return approximation of extension channel busy over an time interval
  * 0% (clear) -> 100% (busy)
  *
+ * XXX TODO: update this to correctly sample all the counters,
+ *           rather than a subset of it.
  */
 uint32_t
 ar5416Get11nExtBusy(struct ath_hal *ah)
@@ -496,36 +493,6 @@
 	    args, argsize, result, resultsize);
 }
 
-typedef struct {
-	uint32_t dma_dbg_3;
-	uint32_t dma_dbg_4;
-	uint32_t dma_dbg_5;
-	uint32_t dma_dbg_6;
-} mac_dbg_regs_t;
-
-typedef enum {
-	dcu_chain_state		= 0x1,
-	dcu_complete_state	= 0x2,
-	qcu_state		= 0x4,
-	qcu_fsp_ok		= 0x8,
-	qcu_fsp_state		= 0x10,
-	qcu_stitch_state	= 0x20,
-	qcu_fetch_state		= 0x40,
-	qcu_complete_state	= 0x80
-} hal_mac_hangs_t;
-
-typedef struct {
-	int states;
-	uint8_t dcu_chain_state;
-	uint8_t dcu_complete_state;
-	uint8_t qcu_state;
-	uint8_t qcu_fsp_ok;
-	uint8_t qcu_fsp_state;
-	uint8_t qcu_stitch_state;
-	uint8_t qcu_fetch_state;
-	uint8_t qcu_complete_state;
-} hal_mac_hang_check_t;
-
 HAL_BOOL
 ar5416SetRifsDelay(struct ath_hal *ah, const struct ieee80211_channel *chan,
     HAL_BOOL enable)
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c 225926 2011-10-02 14:10:25Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c 237626 2012-06-27 05:23:33Z adrian $
  */
 #include "opt_ah.h"
 
@@ -67,6 +67,40 @@
 }
 
 /*
+ * Stop Receive at the DMA engine
+ */
+HAL_BOOL
+ar5416StopDmaReceive(struct ath_hal *ah)
+{
+	HAL_BOOL status;
+
+	OS_MARK(ah, AH_MARK_RX_CTL, AH_MARK_RX_CTL_DMA_STOP);
+	OS_REG_WRITE(ah, AR_CR, AR_CR_RXD);	/* Set receive disable bit */
+	if (!ath_hal_wait(ah, AR_CR, AR_CR_RXE, 0)) {
+		OS_MARK(ah, AH_MARK_RX_CTL, AH_MARK_RX_CTL_DMA_STOP_ERR);
+#ifdef AH_DEBUG
+		ath_hal_printf(ah, "%s: dma failed to stop in 10ms\n"
+			"AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n",
+			__func__,
+			OS_REG_READ(ah, AR_CR),
+			OS_REG_READ(ah, AR_DIAG_SW));
+#endif
+		status = AH_FALSE;
+	} else {
+		status = AH_TRUE;
+	}
+
+	/*
+	 * XXX Is this to flush whatever is in a FIFO somewhere?
+	 * XXX If so, what should the correct behaviour should be?
+	 */
+	if (AR_SREV_9100(ah))
+		OS_DELAY(3000);
+
+	return (status);
+}
+
+/*
  * Start receive at the PCU engine
  */
 void
@@ -194,15 +228,40 @@
 		 * Consequently we filter them out here so we don't
 		 * confuse and/or complicate drivers.
 		 */
+
+		/*
+		 * The AR5416 sometimes sets both AR_CRCErr and AR_PHYErr
+		 * when reporting radar pulses.  In this instance
+		 * set HAL_RXERR_PHY as well as HAL_RXERR_CRC and
+		 * let the driver layer figure out what to do.
+		 *
+		 * See PR kern/169362.
+		 */
+		if (ads->ds_rxstatus8 & AR_PHYErr) {
+			u_int phyerr;
+
+			/*
+			 * Packets with OFDM_RESTART on post delimiter are CRC OK and
+			 * usable and MAC ACKs them.
+			 * To avoid packet from being lost, we remove the PHY Err flag
+			 * so that driver layer does not drop them.
+			 */
+			phyerr = MS(ads->ds_rxstatus8, AR_PHYErrCode);
+
+			if ((phyerr == HAL_PHYERR_OFDM_RESTART) &&
+			    (ads->ds_rxstatus8 & AR_PostDelimCRCErr)) {
+				ath_hal_printf(ah,
+				    "%s: OFDM_RESTART on post-delim CRC error\n",
+				    __func__);
+				rs->rs_phyerr = 0;
+			} else {
+				rs->rs_status |= HAL_RXERR_PHY;
+				rs->rs_phyerr = phyerr;
+			}
+		}
 		if (ads->ds_rxstatus8 & AR_CRCErr)
 			rs->rs_status |= HAL_RXERR_CRC;
-		else if (ads->ds_rxstatus8 & AR_PHYErr) {
-			u_int phyerr;
-
-			rs->rs_status |= HAL_RXERR_PHY;
-			phyerr = MS(ads->ds_rxstatus8, AR_PHYErrCode);
-			rs->rs_phyerr = phyerr;
-		} else if (ads->ds_rxstatus8 & AR_DecryptCRCErr)
+		else if (ads->ds_rxstatus8 & AR_DecryptCRCErr)
 			rs->rs_status |= HAL_RXERR_DECRYPT;
 		else if (ads->ds_rxstatus8 & AR_MichaelErr)
 			rs->rs_status |= HAL_RXERR_MIC;
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c 234450 2012-04-19 03:26:21Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c 234774 2012-04-28 22:03:19Z adrian $
  */
 #include "opt_ah.h"
 
@@ -118,6 +118,9 @@
 	}
 	HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1);
 
+	/* Blank the channel survey statistics */
+	OS_MEMZERO(&ahp->ah_chansurvey, sizeof(ahp->ah_chansurvey));
+
 	/* XXX Turn on fast channel change for 5416 */
 	/*
 	 * Preserve the bmiss rssi threshold and count threshold
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416phy.h
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416phy.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416phy.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,13 +14,49 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416phy.h 227468 2011-11-12 16:47:23Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416phy.h 234692 2012-04-26 02:03:16Z adrian $
  */
 #ifndef _DEV_ATH_AR5416PHY_H_
 #define _DEV_ATH_AR5416PHY_H_
 
 #include "ar5212/ar5212phy.h"
 
+#define	AR_BT_COEX_MODE            0x8170
+#define	AR_BT_TIME_EXTEND          0x000000ff
+#define	AR_BT_TIME_EXTEND_S        0
+#define	AR_BT_TXSTATE_EXTEND       0x00000100
+#define	AR_BT_TXSTATE_EXTEND_S     8
+#define	AR_BT_TX_FRAME_EXTEND      0x00000200
+#define	AR_BT_TX_FRAME_EXTEND_S    9
+#define	AR_BT_MODE                 0x00000c00
+#define	AR_BT_MODE_S               10
+#define	AR_BT_QUIET                0x00001000
+#define	AR_BT_QUIET_S              12
+#define	AR_BT_QCU_THRESH           0x0001e000
+#define	AR_BT_QCU_THRESH_S         13
+#define	AR_BT_RX_CLEAR_POLARITY    0x00020000
+#define	AR_BT_RX_CLEAR_POLARITY_S  17
+#define	AR_BT_PRIORITY_TIME        0x00fc0000
+#define	AR_BT_PRIORITY_TIME_S      18
+#define	AR_BT_FIRST_SLOT_TIME      0xff000000
+#define	AR_BT_FIRST_SLOT_TIME_S    24
+
+#define	AR_BT_COEX_WEIGHT          0x8174
+#define	AR_BT_BT_WGHT              0x0000ffff
+#define	AR_BT_BT_WGHT_S            0
+#define	AR_BT_WL_WGHT              0xffff0000
+#define	AR_BT_WL_WGHT_S            16
+
+#define	AR_BT_COEX_MODE2           0x817c
+#define	AR_BT_BCN_MISS_THRESH      0x000000ff
+#define	AR_BT_BCN_MISS_THRESH_S    0
+#define	AR_BT_BCN_MISS_CNT         0x0000ff00
+#define	AR_BT_BCN_MISS_CNT_S       8
+#define	AR_BT_HOLD_RX_CLEAR        0x00010000
+#define	AR_BT_HOLD_RX_CLEAR_S      16
+#define	AR_BT_DISABLE_BT_ANT       0x00100000
+#define	AR_BT_DISABLE_BT_ANT_S     20
+
 /* For AR_PHY_RADAR0 */
 #define	AR_PHY_RADAR_0_FFT_ENA		0x80000000
 
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h
--- a/head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h 233885 2012-04-04 20:42:32Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416reg.h 236009 2012-05-25 16:45:56Z adrian $
  */
 #ifndef _DEV_ATH_AR5416REG_H
 #define	_DEV_ATH_AR5416REG_H
@@ -47,14 +47,58 @@
 #define	AR_GPIO_IN_OUT		0x4048	/* GPIO input/output register */
 #define	AR_GPIO_OE_OUT		0x404c	/* GPIO output enable register */
 #define	AR_GPIO_INTR_POL	0x4050	/* GPIO interrupt polarity */
+
 #define	AR_GPIO_INPUT_EN_VAL	0x4054	/* GPIO input enable and value */
+#define	AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF     0x00000004
+#define	AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_S       2
+#define	AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF    0x00000008
+#define	AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_S      3
+#define	AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_DEF       0x00000010
+#define	AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_S         4
+#define	AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF        0x00000080
+#define	AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S      7
+#define	AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB      0x00000400
+#define	AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S    10
+#define	AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_BB     0x00000800
+#define	AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_BB_S   11
+#define	AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB        0x00001000
+#define	AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S      12
+#define	AR_GPIO_INPUT_EN_VAL_RFSILENT_BB         0x00008000
+#define	AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S       15
+#define	AR_GPIO_RTC_RESET_OVERRIDE_ENABLE        0x00010000
+#define	AR_GPIO_JTAG_DISABLE                     0x00020000
+
 #define	AR_GPIO_INPUT_MUX1	0x4058
+#define	AR_GPIO_INPUT_MUX1_BT_PRIORITY           0x00000f00
+#define	AR_GPIO_INPUT_MUX1_BT_PRIORITY_S         8
+#define	AR_GPIO_INPUT_MUX1_BT_FREQUENCY          0x0000f000
+#define	AR_GPIO_INPUT_MUX1_BT_FREQUENCY_S        12
+#define	AR_GPIO_INPUT_MUX1_BT_ACTIVE             0x000f0000
+#define	AR_GPIO_INPUT_MUX1_BT_ACTIVE_S           16
+
 #define	AR_GPIO_INPUT_MUX2	0x405c
+#define	AR_GPIO_INPUT_MUX2_CLK25                 0x0000000f
+#define	AR_GPIO_INPUT_MUX2_CLK25_S               0
+#define	AR_GPIO_INPUT_MUX2_RFSILENT              0x000000f0
+#define	AR_GPIO_INPUT_MUX2_RFSILENT_S            4
+#define	AR_GPIO_INPUT_MUX2_RTC_RESET             0x00000f00
+#define	AR_GPIO_INPUT_MUX2_RTC_RESET_S           8
+
 #define	AR_GPIO_OUTPUT_MUX1	0x4060
 #define	AR_GPIO_OUTPUT_MUX2	0x4064
 #define	AR_GPIO_OUTPUT_MUX3	0x4068
+
+#define	AR_GPIO_OUTPUT_MUX_AS_OUTPUT             0
+#define	AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1
+#define	AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED     2
+#define	AR_GPIO_OUTPUT_MUX_AS_TX_FRAME           3
+#define	AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL  4
+#define	AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED    5
+#define	AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED      6
+
 #define	AR_EEPROM_STATUS_DATA	0x407c
 #define	AR_OBS			0x4080
+#define	AR_GPIO_PDPU		0x4088
 
 #ifdef	AH_SUPPORT_AR9130
 #define	AR_RTC_BASE		0x20000
@@ -95,6 +139,7 @@
 #define	AR_TSFOOR_THRESHOLD	0x813c
 #define	AR_PHY_ERR_3		0x8168
 #define	AR_PHY_ERR_MASK_3	0x816c	/* mask for AR_PHY_ERR_3 */
+#define	AR_BT_COEX_WEIGHT2	0x81c4
 #define	AR_TXOP_X		0x81ec	/* txop for legacy non-qos */
 #define	AR_TXOP_0_3		0x81f0	/* txop for various tid's */
 #define	AR_TXOP_4_7		0x81f4
@@ -208,11 +253,15 @@
 #define	AR_MAC_LED_ASSOC_PEND	0x2	/* STA is trying to associate */
 #define	AR_MAC_LED_ASSOC_S	10
 
+#define	AR_WA_BIT6		0x00000040
+#define	AR_WA_BIT7		0x00000080
+#define	AR_WA_D3_L1_DISABLE	0x00004000	/* */
 #define	AR_WA_UNTIE_RESET_EN	0x00008000	/* ena PCI reset to POR */
 #define	AR_WA_RESET_EN		0x00040000	/* ena AR_WA_UNTIE_RESET_EN */
 #define	AR_WA_ANALOG_SHIFT	0x00100000
 #define	AR_WA_POR_SHORT		0x00200000	/* PCIE phy reset control */
-#define	AR_WA_D3_L1_DISABLE	0x00800000	/* bit 23 */
+#define	AR_WA_BIT22		0x00400000
+#define	AR_WA_BIT23		0x00800000
 
 #define	AR_WA_DEFAULT		0x0000073f
 #define	AR9280_WA_DEFAULT	0x0040073b	/* disable bit 2, see commit */
@@ -460,6 +509,8 @@
 #define	AR_PCU_MISS_BCN_IN_SLEEP	0x00004000 /* count bmiss's when sleeping */
 #define	AR_PCU_BUG_12306_FIX_ENA	0x00020000 /* use rx_clear to count sifs */
 #define	AR_PCU_FORCE_QUIET_COLL		0x00040000 /* kill xmit for channel change */
+#define	AR_PCU_BT_ANT_PREVENT_RX	0x00100000
+#define	AR_PCU_BT_ANT_PREVENT_RX_S	20
 #define	AR_PCU_TBTT_PROTECT		0x00200000 /* no xmit upto tbtt+20 uS */
 #define	AR_PCU_CLEAR_VMF		0x01000000 /* clear vmf mode (fast cc)*/
 #define	AR_PCU_CLEAR_BA_VALID		0x04000000 /* clear ba state */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c
--- a/head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c 234510 2012-04-20 22:07:21Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c 236039 2012-05-26 01:36:25Z adrian $
  */
 #include "opt_ah.h"
 
@@ -61,7 +61,9 @@
 	.calPostProc	= ar5416AdcDcCalibration
 };
 
-static void ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore);
+static void ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore,
+		HAL_BOOL power_off);
+static void ar9280DisablePCIE(struct ath_hal *ah);
 static HAL_BOOL ar9280FillCapabilityInfo(struct ath_hal *ah);
 static void ar9280WriteIni(struct ath_hal *ah,
 	const struct ieee80211_channel *chan);
@@ -187,6 +189,7 @@
 
 	ah->ah_setAntennaSwitch		= ar9280SetAntennaSwitch;
 	ah->ah_configPCIE		= ar9280ConfigPCIE;
+	ah->ah_disablePCIE		= ar9280DisablePCIE;
 
 	AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal;
 	AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal;
@@ -415,17 +418,73 @@
 }
 
 static void
-ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
+	uint32_t val;
+
 	if (AH_PRIVATE(ah)->ah_ispcie && !restore) {
 		ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0);
 		OS_DELAY(1000);
+	}
+
+
+	/*
+	 * Set PCIe workaround bits
+	 *
+	 * NOTE:
+	 *
+	 * In Merlin and Kite, bit 14 in WA register (disable L1) should only
+	 * be set when device enters D3 and be cleared when device comes back
+	 * to D0.
+	 */
+	if (power_off) {		/* Power-off */
+		OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
+
+		val = OS_REG_READ(ah, AR_WA);
+
+		/*
+		 * Disable bit 6 and 7 before entering D3 to prevent
+		 * system hang.
+		 */
+		val &= ~(AR_WA_BIT6 | AR_WA_BIT7);
+
+		/*
+		 * XXX Not sure, is specified in the reference HAL.
+		 */
+		val |= AR_WA_BIT22;
+
+		/*
+		 * See above: set AR_WA_D3_L1_DISABLE when entering D3 state.
+		 *
+		 * XXX The reference HAL does it this way - it only sets
+		 * AR_WA_D3_L1_DISABLE if it's set in AR9280_WA_DEFAULT,
+		 * which it (currently) isn't.  So the following statement
+		 * is currently a NOP.
+		 */
+		if (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE)
+			val |= AR_WA_D3_L1_DISABLE;
+
+		OS_REG_WRITE(ah, AR_WA, val);
+	} else {			/* Power-on */
+		val = AR9280_WA_DEFAULT;
+
+		/*
+		 * See note above: make sure L1_DISABLE is not set.
+		 */
+		val &= (~AR_WA_D3_L1_DISABLE);
+		OS_REG_WRITE(ah, AR_WA, val);
+
+		/* set bit 19 to allow forcing of pcie core into L1 state */
 		OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
-		OS_REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT);
 	}
 }
 
 static void
+ar9280DisablePCIE(struct ath_hal *ah)
+{
+}
+
+static void
 ar9280WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan)
 {
 	u_int modesIndex, freqIndex;
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar9002/ar9285.h
--- a/head/sys/dev/ath/ath_hal/ar9002/ar9285.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar9002/ar9285.h	Wed Jul 25 17:07:47 2012 +0300
@@ -13,7 +13,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9285.h 221694 2011-05-09 15:19:49Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9285.h 237611 2012-06-26 22:16:53Z adrian $
  */
 #ifndef _ATH_AR9285_H_
 #define _ATH_AR9285_H_
@@ -86,4 +86,9 @@
 extern HAL_BOOL ar9285SetBoardValues(struct ath_hal *,
 		const struct ieee80211_channel *);
 
+/* ar9285_btcoex.h */
+extern	void ar9285BTCoexAntennaDiversity(struct ath_hal *ah);
+extern	void ar9285BTCoexSetParameter(struct ath_hal *ah,
+		u_int32_t value, u_int32_t type);
+
 #endif	/* _ATH_AR9285_H_ */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c
--- a/head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c 234508 2012-04-20 21:56:13Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c 237183 2012-06-17 05:34:41Z adrian $
  */
 #include "opt_ah.h"
 
@@ -66,7 +66,9 @@
 	.calPostProc	= ar5416AdcDcCalibration
 };
 
-static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore);
+static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore,
+		HAL_BOOL power_off);
+static void ar9285DisablePCIE(struct ath_hal *ah);
 static HAL_BOOL ar9285FillCapabilityInfo(struct ath_hal *ah);
 static void ar9285WriteIni(struct ath_hal *ah,
 	const struct ieee80211_channel *chan);
@@ -152,6 +154,7 @@
 
 	ah->ah_setAntennaSwitch		= ar9285SetAntennaSwitch;
 	ah->ah_configPCIE		= ar9285ConfigPCIE;
+	ah->ah_disablePCIE		= ar9285DisablePCIE;
 	ah->ah_setTxPower		= ar9285SetTransmitPower;
 	ah->ah_setBoardValues		= ar9285SetBoardValues;
 
@@ -362,17 +365,95 @@
 }
 
 static void
-ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
+	uint32_t val;
+
+	/*
+	 * This workaround needs some integration work with the HAL
+	 * config parameters and the if_ath_pci.c glue.
+	 * Specifically, read the value of the PCI register 0x70c
+	 * (4 byte PCI config space register) and store it in ath_hal_war70c.
+	 * Then if it's non-zero, the below WAR would override register
+	 * 0x570c upon suspend/resume.
+	 */
+#if 0
+	if (AR_SREV_9285E_20(ah)) {
+		val = AH_PRIVATE(ah)->ah_config.ath_hal_war70c;
+		if (val) {
+			val &= 0xffff00ff;
+			val |= 0x6f00;
+			OS_REG_WRITE(ah, 0x570c, val);
+		}
+	}
+#endif
+
 	if (AH_PRIVATE(ah)->ah_ispcie && !restore) {
 		ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0);
 		OS_DELAY(1000);
+	}
+
+	/*
+	 * Set PCIe workaround bits
+	 *
+	 * NOTE:
+	 *
+	 * In Merlin and Kite, bit 14 in WA register (disable L1) should only
+	 * be set when device enters D3 and be cleared when device comes back
+	 * to D0.
+	 */
+	if (power_off) {                /* Power-off */
+		OS_REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
+
+		val = OS_REG_READ(ah, AR_WA);
+
+		/*
+		 * Disable bit 6 and 7 before entering D3 to prevent
+		 * system hang.
+		 */
+		val &= ~(AR_WA_BIT6 | AR_WA_BIT7);
+
+		/*
+		 * See above: set AR_WA_D3_L1_DISABLE when entering D3 state.
+		 *
+		 * XXX The reference HAL does it this way - it only sets
+		 * AR_WA_D3_L1_DISABLE if it's set in AR9280_WA_DEFAULT,
+		 * which it (currently) isn't.  So the following statement
+		 * is currently a NOP.
+		 */
+		if (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)
+			val |= AR_WA_D3_L1_DISABLE;
+
+		if (AR_SREV_9285E_20(ah))
+			val |= AR_WA_BIT23;
+
+		OS_REG_WRITE(ah, AR_WA, val);
+	} else {			/* Power-on */
+		val = AR9285_WA_DEFAULT;
+		/*
+		 * See note above: make sure L1_DISABLE is not set.
+		 */
+		val &= (~AR_WA_D3_L1_DISABLE);
+
+		/* Software workaroud for ASPM system hang. */
+		val |= (AR_WA_BIT6 | AR_WA_BIT7);
+
+		if (AR_SREV_9285E_20(ah))
+			val |= AR_WA_BIT23;
+
+		OS_REG_WRITE(ah, AR_WA, val);
+
+		/* set bit 19 to allow forcing of pcie core into L1 state */
 		OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
-		OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT);
 	}
 }
 
 static void
+ar9285DisablePCIE(struct ath_hal *ah)
+{
+}
+
+static void
 ar9285WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan)
 {
 	u_int modesIndex, freqIndex;
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Atheros Communications, Inc.
+ * Copyright (c) 2008-2010, Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c 237611 2012-06-26 22:16:53Z adrian $
+ */
+
+#include "opt_ah.h"
+
+#include "ah.h"
+#include "ah_internal.h"
+#include "ah_devid.h"
+#ifdef AH_DEBUG
+#include "ah_desc.h"                    /* NB: for HAL_PHYERR* */
+#endif
+
+#include "ar5416/ar5416.h"
+#include "ar5416/ar5416reg.h"
+#include "ar5416/ar5416phy.h"
+#include "ar5416/ar5416desc.h" /* AR5416_CONTTXMODE */
+
+#include "ar9002/ar9285phy.h"
+#include "ar9002/ar9285.h"
+
+/*
+ * This is specific to Kite.
+ *
+ * Kiwi and others don't have antenna diversity like this.
+ */
+void
+ar9285BTCoexAntennaDiversity(struct ath_hal *ah)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+	u_int32_t regVal;
+	u_int8_t ant_div_control1, ant_div_control2;
+
+    if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ALLOW) ||
+        (AH5212(ah)->ah_diversity != HAL_ANT_VARIABLE)) {
+        if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ENABLE) &&
+             (AH5212(ah)->ah_diversity == HAL_ANT_VARIABLE)) {
+            /* Enable antenna diversity */
+            ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_ENABLE;
+            ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_ENABLE;
+
+            /* Don't disable BT ant to allow BB to control SWCOM */
+            ahp->ah_btCoexMode2 &= (~(AR_BT_DISABLE_BT_ANT));
+            OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2);
+
+            /* Program the correct SWCOM table */
+            OS_REG_WRITE(ah, AR_PHY_SWITCH_COM,
+              HAL_BT_COEX_ANT_DIV_SWITCH_COM);
+            OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
+        } else if (AH5212(ah)->ah_diversity == HAL_ANT_FIXED_B) {
+            /* Disable antenna diversity. Use antenna B(LNA2) only. */
+            ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_B;
+            ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_B;
+
+            /* Disable BT ant to allow concurrent BT and WLAN receive */
+            ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT;
+            OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2);
+
+            /* Program SWCOM talbe to make sure RF switch always parks at WLAN side */
+            OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, HAL_BT_COEX_ANT_DIV_SWITCH_COM);
+            OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0x60000000, 0xf0000000);
+        } else {
+            /* Disable antenna diversity. Use antenna A(LNA1) only */
+            ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_A;
+            ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_A;
+
+            /* Disable BT ant to allow concurrent BT and WLAN receive */
+            ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT;
+            OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2);
+
+            /* Program SWCOM talbe to make sure RF switch always parks at BT side */
+            OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, 0);
+            OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
+        }
+
+        regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+        regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL));
+        /* Clear ant_fast_div_bias [14:9] since for Janus the main LNA is always LNA1. */
+        regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
+
+        regVal |= SM(ant_div_control1, AR_PHY_9285_ANT_DIV_CTL);
+        regVal |= SM(ant_div_control2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
+        regVal |= SM((ant_div_control2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
+        regVal |= SM((ant_div_control1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB);
+        regVal |= SM((ant_div_control1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
+        OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
+
+        regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
+        regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+        regVal |= SM((ant_div_control1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+        OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal);
+    }
+}
+
+void
+ar9285BTCoexSetParameter(struct ath_hal *ah, u_int32_t type, u_int32_t value)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+
+	switch (type) {
+	case HAL_BT_COEX_ANTENNA_DIVERSITY:
+		if (AR_SREV_KITE(ah)) {
+			ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_ANT_DIV_ALLOW;
+			if (value)
+				ahp->ah_btCoexFlag |=
+				    HAL_BT_COEX_FLAG_ANT_DIV_ENABLE;
+			else
+				ahp->ah_btCoexFlag &=
+				    ~HAL_BT_COEX_FLAG_ANT_DIV_ENABLE;
+			ar9285BTCoexAntennaDiversity(ah);
+		}
+		break;
+	default:
+		ar5416BTCoexSetParameter(ah, type, value);
+		break;
+	}
+}
+
+
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c
--- a/head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c 227387 2011-11-09 14:30:58Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c 235972 2012-05-25 05:01:27Z adrian $
  */
 #include "opt_ah.h"
 
@@ -65,7 +65,9 @@
 	.calPostProc	= ar5416AdcDcCalibration
 };
 
-static void ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore);
+static void ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore,
+		HAL_BOOL power_off);
+static void ar9287DisablePCIE(struct ath_hal *ah);
 static HAL_BOOL ar9287FillCapabilityInfo(struct ath_hal *ah);
 static void ar9287WriteIni(struct ath_hal *ah,
 	const struct ieee80211_channel *chan);
@@ -154,6 +156,7 @@
 
 	ah->ah_setAntennaSwitch		= ar9287SetAntennaSwitch;
 	ah->ah_configPCIE		= ar9287ConfigPCIE;
+	ah->ah_disablePCIE		= ar9287DisablePCIE;
 
 	AH5416(ah)->ah_cal.iqCalData.calData = &ar9287_iq_cal;
 	AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9287_adc_gain_cal;
@@ -370,17 +373,24 @@
 }
 
 static void
-ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar9287ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
 	if (AH_PRIVATE(ah)->ah_ispcie && !restore) {
 		ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0);
 		OS_DELAY(1000);
 		OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
-		OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT);	/* Yes, Kiwi uses the Kite PCIe PHY WA */
+		/* Yes, Kiwi uses the Kite PCIe PHY WA */
+		OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT);
 	}
 }
 
 static void
+ar9287DisablePCIE(struct ath_hal *ah)
+{
+	/* XXX TODO */
+}
+
+static void
 ar9287WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan)
 {
 	u_int modesIndex, freqIndex;
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c
--- a/head/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c	Wed Jul 25 17:07:47 2012 +0300
@@ -14,7 +14,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c 222424 2011-05-28 15:43:56Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c 237184 2012-06-17 05:56:27Z adrian $
  */
 
 #include "opt_ah.h"
@@ -45,70 +45,70 @@
 ar9287SetPowerCalTable(struct ath_hal *ah,
     const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
 {
-        struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop;
-        uint8_t *pCalBChans = NULL;
-        uint16_t pdGainOverlap_t2;
-        uint16_t numPiers = 0, i;
-        uint16_t numXpdGain, xpdMask;
-        uint16_t xpdGainValues[AR5416_NUM_PD_GAINS] = {0, 0, 0, 0};
-        uint32_t regChainOffset;
+	struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop;
+	uint8_t *pCalBChans = NULL;
+	uint16_t pdGainOverlap_t2;
+	uint16_t numPiers = 0, i;
+	uint16_t numXpdGain, xpdMask;
+	uint16_t xpdGainValues[AR5416_NUM_PD_GAINS] = {0, 0, 0, 0};
+	uint32_t regChainOffset;
 	HAL_EEPROM_9287 *ee = AH_PRIVATE(ah)->ah_eeprom;
-        struct ar9287_eeprom *pEepData = &ee->ee_base;
+	struct ar9287_eeprom *pEepData = &ee->ee_base;
 
-        xpdMask = pEepData->modalHeader.xpdGain;
+	xpdMask = pEepData->modalHeader.xpdGain;
 
-        if ((pEepData->baseEepHeader.version & AR9287_EEP_VER_MINOR_MASK) >=
-            AR9287_EEP_MINOR_VER_2)
-                pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap;
-        else
-                pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5),
-                                            AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
+	if ((pEepData->baseEepHeader.version & AR9287_EEP_VER_MINOR_MASK) >=
+	    AR9287_EEP_MINOR_VER_2)
+		pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap;
+	else
+		pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5),
+					    AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
 
 	/* Note: Kiwi should only be 2ghz.. */
-        if (IEEE80211_IS_CHAN_2GHZ(chan)) {
-                pCalBChans = pEepData->calFreqPier2G;
-                numPiers = AR9287_NUM_2G_CAL_PIERS;
-                pRawDatasetOpenLoop = (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[0];
-                AH5416(ah)->initPDADC = pRawDatasetOpenLoop->vpdPdg[0][0];
-        }
-        numXpdGain = 0;
+	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
+		pCalBChans = pEepData->calFreqPier2G;
+		numPiers = AR9287_NUM_2G_CAL_PIERS;
+		pRawDatasetOpenLoop = (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[0];
+		AH5416(ah)->initPDADC = pRawDatasetOpenLoop->vpdPdg[0][0];
+	}
+	numXpdGain = 0;
 
-        /* Calculate the value of xpdgains from the xpdGain Mask */
-        for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) {
-                if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) {
-                        if (numXpdGain >= AR5416_NUM_PD_GAINS)
-                                break;
-                        xpdGainValues[numXpdGain] =
-                                (uint16_t)(AR5416_PD_GAINS_IN_MASK-i);
-                        numXpdGain++;
-                }
-        }
+	/* Calculate the value of xpdgains from the xpdGain Mask */
+	for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) {
+		if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) {
+			if (numXpdGain >= AR5416_NUM_PD_GAINS)
+				break;
+			xpdGainValues[numXpdGain] =
+				(uint16_t)(AR5416_PD_GAINS_IN_MASK-i);
+			numXpdGain++;
+		}
+	}
 
-        OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
-                      (numXpdGain - 1) & 0x3);
-        OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1,
-                      xpdGainValues[0]);
-        OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2,
-                      xpdGainValues[1]);
-        OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3,
-                      xpdGainValues[2]);
+	OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
+		      (numXpdGain - 1) & 0x3);
+	OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1,
+		      xpdGainValues[0]);
+	OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2,
+		      xpdGainValues[1]);
+	OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3,
+		      xpdGainValues[2]);
 
-        for (i = 0; i < AR9287_MAX_CHAINS; i++) {
-                regChainOffset = i * 0x1000;
+	for (i = 0; i < AR9287_MAX_CHAINS; i++) {
+		regChainOffset = i * 0x1000;
 
-                if (pEepData->baseEepHeader.txMask & (1 << i)) {
-                        int8_t txPower;
-                        pRawDatasetOpenLoop =
-                        (struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[i];
-                                ar9287olcGetTxGainIndex(ah, chan,
-                                    pRawDatasetOpenLoop,
-                                    pCalBChans, numPiers,
-                                    &txPower);
-                                ar9287olcSetPDADCs(ah, txPower, i);
-                }
-        }
+		if (pEepData->baseEepHeader.txMask & (1 << i)) {
+			int8_t txPower;
+			pRawDatasetOpenLoop =
+			(struct cal_data_op_loop_ar9287 *)pEepData->calPierData2G[i];
+				ar9287olcGetTxGainIndex(ah, chan,
+				    pRawDatasetOpenLoop,
+				    pCalBChans, numPiers,
+				    &txPower);
+				ar9287olcSetPDADCs(ah, txPower, i);
+		}
+	}
 
-        *pTxPowerIndexOffset = 0;
+	*pTxPowerIndexOffset = 0;
 }
 
 
@@ -329,20 +329,20 @@
 	const struct ieee80211_channel *chan, uint16_t *rfXpdGain)
 {
 #define	POW_SM(_r, _s)     (((_r) & 0x3f) << (_s))
-#define	N(a)            (sizeof (a) / sizeof (a[0]))
+#define	N(a)	    (sizeof (a) / sizeof (a[0]))
 
 	const struct modal_eep_ar9287_header *pModal;
 	struct ath_hal_5212 *ahp = AH5212(ah);
-	int16_t             ratesArray[Ar5416RateSize];
-	int16_t             txPowerIndexOffset = 0;
-	uint8_t             ht40PowerIncForPdadc = 2;
-	int                 i;
+	int16_t	     ratesArray[Ar5416RateSize];
+	int16_t	     txPowerIndexOffset = 0;
+	uint8_t	     ht40PowerIncForPdadc = 2;
+	int		 i;
 
-	uint16_t            cfgCtl;
-	uint16_t            powerLimit;
-	uint16_t            twiceAntennaReduction;
-	uint16_t            twiceMaxRegulatoryPower;
-	int16_t             maxPower;
+	uint16_t	    cfgCtl;
+	uint16_t	    powerLimit;
+	uint16_t	    twiceAntennaReduction;
+	uint16_t	    twiceMaxRegulatoryPower;
+	int16_t	     maxPower;
 	HAL_EEPROM_9287 *ee = AH_PRIVATE(ah)->ah_eeprom;
 	struct ar9287_eeprom *pEepData = &ee->ee_base;
 
@@ -351,7 +351,8 @@
 	cfgCtl = ath_hal_getctl(ah, chan);
 	powerLimit = chan->ic_maxregpower * 2;
 	twiceAntennaReduction = chan->ic_maxantgain;
-	twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit);
+	twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER,
+	    AH_PRIVATE(ah)->ah_powerLimit);
 	pModal = &pEepData->modalHeader;
 	HALDEBUG(ah, HAL_DEBUG_RESET, "%s Channel=%u CfgCtl=%u\n",
 	    __func__,chan->ic_freq, cfgCtl );
@@ -361,9 +362,9 @@
 
 	/* Fetch per-rate power table for the given channel */
 	if (! ar9287SetPowerPerRateTable(ah, pEepData,  chan,
-            &ratesArray[0],cfgCtl,
-            twiceAntennaReduction,
-            twiceMaxRegulatoryPower, powerLimit)) {
+	    &ratesArray[0],cfgCtl,
+	    twiceAntennaReduction,
+	    twiceMaxRegulatoryPower, powerLimit)) {
 		HALDEBUG(ah, HAL_DEBUG_ANY,
 		    "%s: unable to set tx power per rate table\n", __func__);
 		return AH_FALSE;
@@ -476,7 +477,8 @@
 			  pModal->antCtrlChain[i]);
 
 		OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4_CHAIN(0) + regChainOffset,
-			  (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4_CHAIN(0) + regChainOffset)
+			  (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4_CHAIN(0)
+			      + regChainOffset)
 			   & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
 			       AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
 			  SM(pModal->iqCalICh[i],
@@ -500,7 +502,6 @@
 			      pModal->rxTxMarginCh[i]);
 	}
 
-
 	if (IEEE80211_IS_CHAN_HT40(chan))
 		OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING,
 			      AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40);
@@ -539,8 +540,8 @@
 		   SM(pModal->ob_qam, AR9287_AN_RF2G3_OB_QAM) |
 		   SM(pModal->ob_pal_off, AR9287_AN_RF2G3_OB_PAL_OFF));
 
-	OS_REG_WRITE(ah, AR9287_AN_RF2G3_CH0, regval);
-	OS_DELAY(100);	/* analog write */
+	/* Analog write - requires a 100usec delay */
+	OS_A_REG_WRITE(ah, AR9287_AN_RF2G3_CH0, regval);
 
 	regval = OS_REG_READ(ah, AR9287_AN_RF2G3_CH1);
 	regval &= ~(AR9287_AN_RF2G3_DB1 |
@@ -556,8 +557,7 @@
 		   SM(pModal->ob_qam, AR9287_AN_RF2G3_OB_QAM) |
 		   SM(pModal->ob_pal_off, AR9287_AN_RF2G3_OB_PAL_OFF));
 
-	OS_REG_WRITE(ah, AR9287_AN_RF2G3_CH1, regval);
-	OS_DELAY(100);	/* analog write */
+	OS_A_REG_WRITE(ah, AR9287_AN_RF2G3_CH1, regval);
 
 	OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2,
 	    AR_PHY_TX_FRAME_TO_DATA_START, pModal->txFrameToDataStart);
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: head/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h 237611 2012-06-26 22:16:53Z adrian $
+ */
+
+#ifndef	__ATH_AR9300_BTCOEX_H__
+#define	__ATH_AR9300_BTCOEX_H__
+/*
+ * Weight table configurations.
+ */
+#define	AR9300_BT_WGHT				0xcccc4444
+#define	AR9300_STOMP_ALL_WLAN_WGHT0		0xfffffff0
+#define	AR9300_STOMP_ALL_WLAN_WGHT1		0xfffffff0
+#define	AR9300_STOMP_LOW_WLAN_WGHT0		0x88888880
+#define	AR9300_STOMP_LOW_WLAN_WGHT1		0x88888880
+#define	AR9300_STOMP_NONE_WLAN_WGHT0		0x00000000
+#define	AR9300_STOMP_NONE_WLAN_WGHT1		0x00000000
+/* Stomp BT even when WLAN is idle */
+#define	AR9300_STOMP_ALL_FORCE_WLAN_WGHT0	0xffffffff
+#define	AR9300_STOMP_ALL_FORCE_WLAN_WGHT1	0xffffffff
+/* Stomp BT even when WLAN is idle */
+#define	AR9300_STOMP_LOW_FORCE_WLAN_WGHT0	0x88888888
+#define	AR9300_STOMP_LOW_FORCE_WLAN_WGHT1	0x88888888
+
+#define	JUPITER_STOMP_ALL_WLAN_WGHT0		0x00007d00
+#define	JUPITER_STOMP_ALL_WLAN_WGHT1		0x7d7d7d00
+#define	JUPITER_STOMP_ALL_WLAN_WGHT2		0x7d7d7d00
+#define	JUPITER_STOMP_ALL_WLAN_WGHT3		0x7d7d7d7d
+#define	JUPITER_STOMP_LOW_WLAN_WGHT0		0x00007d00
+#define	JUPITER_STOMP_LOW_WLAN_WGHT1		0x7d3b3b00
+#define	JUPITER_STOMP_LOW_WLAN_WGHT2		0x3b3b3b00
+#define	JUPITER_STOMP_LOW_WLAN_WGHT3		0x3b3b3b3b
+#define	JUPITER_STOMP_NONE_WLAN_WGHT0		0x00007d00
+#define	JUPITER_STOMP_NONE_WLAN_WGHT1		0x7d000000
+#define	JUPITER_STOMP_NONE_WLAN_WGHT2		0x00000000
+#define	JUPITER_STOMP_NONE_WLAN_WGHT3		0x00000000
+#define	JUPITER_STOMP_ALL_FORCE_WLAN_WGHT0	0x00007d7d
+#define	JUPITER_STOMP_ALL_FORCE_WLAN_WGHT1	0x7d7d7d00
+#define	JUPITER_STOMP_ALL_FORCE_WLAN_WGHT2	0x7d7d7d7d
+#define	JUPITER_STOMP_ALL_FORCE_WLAN_WGHT3	0x7d7d7d7d
+#define	JUPITER_STOMP_LOW_FORCE_WLAN_WGHT0	0x00003b3b
+#define	JUPITER_STOMP_LOW_FORCE_WLAN_WGHT1	0x3b3b3b00
+#define	JUPITER_STOMP_LOW_FORCE_WLAN_WGHT2	0x3b3b3b3b
+#define	JUPITER_STOMP_LOW_FORCE_WLAN_WGHT3	0x3b3b3b3b
+
+#endif	/* __ATH_AR9300_BTCOEX_H__ */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_rate/amrr/amrr.c
--- a/head/sys/dev/ath/ath_rate/amrr/amrr.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_rate/amrr/amrr.c	Wed Jul 25 17:07:47 2012 +0300
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/ath_rate/amrr/amrr.c 227364 2011-11-08 22:43:13Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/ath_rate/amrr/amrr.c 238633 2012-07-20 01:36:02Z adrian $");
 
 /*
  * AMRR rate control. See:
@@ -45,6 +45,7 @@
  * "IEEE 802.11 Rate Adaptation: A Practical Approach" by
  *    Mathieu Lacage, Hossein Manshaei, Thierry Turletti
  */
+#include "opt_ath.h"
 #include "opt_inet.h"
 #include "opt_wlan.h"
 
@@ -420,6 +421,14 @@
 	}
 }
 
+static int
+ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an,
+    struct ath_rateioctl *re)
+{
+
+	return (EINVAL);
+}
+
 static void
 ath_rate_sysctlattach(struct ath_softc *sc)
 {
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_rate/onoe/onoe.c
--- a/head/sys/dev/ath/ath_rate/onoe/onoe.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_rate/onoe/onoe.c	Wed Jul 25 17:07:47 2012 +0300
@@ -28,11 +28,12 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/ath_rate/onoe/onoe.c 227364 2011-11-08 22:43:13Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/ath_rate/onoe/onoe.c 238633 2012-07-20 01:36:02Z adrian $");
 
 /*
  * Atsushi Onoe's rate control algorithm.
  */
+#include "opt_ath.h"
 #include "opt_inet.h"
 #include "opt_wlan.h"
 
@@ -406,6 +407,14 @@
 		"rate control: # good periods before raising rate");
 }
 
+static int
+ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an,
+    struct ath_rateioctl *re)
+{
+
+	return (EINVAL);
+}
+
 struct ath_ratectrl *
 ath_rate_attach(struct ath_softc *sc)
 {
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_rate/sample/sample.c
--- a/head/sys/dev/ath/ath_rate/sample/sample.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_rate/sample/sample.c	Wed Jul 25 17:07:47 2012 +0300
@@ -36,11 +36,12 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.c 232170 2012-02-26 06:04:44Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.c 238638 2012-07-20 02:17:48Z adrian $");
 
 /*
  * John Bicket's SampleRate control algorithm.
  */
+#include "opt_ath.h"
 #include "opt_inet.h"
 #include "opt_wlan.h"
 #include "opt_ah.h"
@@ -104,8 +105,6 @@
 
 static void	ath_rate_ctl_reset(struct ath_softc *, struct ieee80211_node *);
 
-static const int packet_size_bins[NUM_PACKET_SIZE_BINS] = { 250, 1600 };
-
 static __inline int
 size_to_bin(int size) 
 {
@@ -127,12 +126,6 @@
 	return NUM_PACKET_SIZE_BINS-1;
 }
 
-static __inline int
-bin_to_size(int index)
-{
-	return packet_size_bins[index];
-}
-
 void
 ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
 {
@@ -1197,6 +1190,93 @@
 #undef DOT11RATE
 }
 
+/*
+ * Fetch the statistics for the given node.
+ *
+ * The ieee80211 node must be referenced and unlocked, however the ath_node
+ * must be locked.
+ *
+ * The main difference here is that we convert the rate indexes
+ * to 802.11 rates, or the userland output won't make much sense
+ * as it has no access to the rix table.
+ */
+int
+ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an,
+    struct ath_rateioctl *rs)
+{
+	struct sample_node *sn = ATH_NODE_SAMPLE(an);
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	struct ath_rateioctl_tlv av;
+	struct ath_rateioctl_rt *tv;
+	int y;
+	int o = 0;
+
+	ATH_NODE_LOCK_ASSERT(an);
+
+	/*
+	 * Ensure there's enough space for the statistics.
+	 */
+	if (rs->len <
+	    sizeof(struct ath_rateioctl_tlv) +
+	    sizeof(struct ath_rateioctl_rt) +
+	    sizeof(struct ath_rateioctl_tlv) +
+	    sizeof(struct sample_node)) {
+		device_printf(sc->sc_dev, "%s: len=%d, too short\n",
+		    __func__,
+		    rs->len);
+		return (EINVAL);
+	}
+
+	/*
+	 * Take a temporary copy of the sample node state so we can
+	 * modify it before we copy it.
+	 */
+	tv = malloc(sizeof(struct ath_rateioctl_rt), M_TEMP,
+	    M_NOWAIT | M_ZERO);
+	if (tv == NULL) {
+		return (ENOMEM);
+	}
+
+	/*
+	 * Populate the rate table mapping TLV.
+	 */
+	tv->nentries = rt->rateCount;
+	for (y = 0; y < rt->rateCount; y++) {
+		tv->ratecode[y] = rt->info[y].dot11Rate & IEEE80211_RATE_VAL;
+		if (rt->info[y].phy == IEEE80211_T_HT)
+			tv->ratecode[y] |= IEEE80211_RATE_MCS;
+	}
+
+	o = 0;
+	/*
+	 * First TLV - rate code mapping
+	 */
+	av.tlv_id = ATH_RATE_TLV_RATETABLE;
+	av.tlv_len = sizeof(struct ath_rateioctl_rt);
+	copyout(&av, rs->buf + o, sizeof(struct ath_rateioctl_tlv));
+	o += sizeof(struct ath_rateioctl_tlv);
+	copyout(tv, rs->buf + o, sizeof(struct ath_rateioctl_rt));
+	o += sizeof(struct ath_rateioctl_rt);
+
+	/*
+	 * Second TLV - sample node statistics
+	 */
+	av.tlv_id = ATH_RATE_TLV_SAMPLENODE;
+	av.tlv_len = sizeof(struct sample_node);
+	copyout(&av, rs->buf + o, sizeof(struct ath_rateioctl_tlv));
+	o += sizeof(struct ath_rateioctl_tlv);
+
+	/*
+	 * Copy the statistics over to the provided buffer.
+	 */
+	copyout(sn, rs->buf + o, sizeof(struct sample_node));
+	o += sizeof(struct sample_node);
+
+	free(tv, M_TEMP);
+
+	return (0);
+}
+
 static void
 sample_stats(void *arg, struct ieee80211_node *ni)
 {
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/ath_rate/sample/sample.h
--- a/head/sys/dev/ath/ath_rate/sample/sample.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/ath_rate/sample/sample.h	Wed Jul 25 17:07:47 2012 +0300
@@ -33,7 +33,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.h 232170 2012-02-26 06:04:44Z adrian $
+ * $FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.h 238636 2012-07-20 01:41:18Z adrian $
  */
 
 /*
@@ -79,10 +79,18 @@
  */
 #define NUM_PACKET_SIZE_BINS 2
 
+static const int packet_size_bins[NUM_PACKET_SIZE_BINS]  = { 250, 1600 };
+
+static inline int
+bin_to_size(int index)
+{
+	return packet_size_bins[index];
+}
+
 /* per-node state */
 struct sample_node {
 	int static_rix;			/* rate index of fixed tx rate */
-#define	SAMPLE_MAXRATES	32		/* NB: corresponds to hal info[32] */
+#define	SAMPLE_MAXRATES	64		/* NB: corresponds to hal info[32] */
 	uint32_t ratemask;		/* bit mask of valid rate indices */
 	const struct txschedule *sched;	/* tx schedule table */
 
@@ -101,6 +109,9 @@
 	int packets_since_sample[NUM_PACKET_SIZE_BINS];
 	unsigned sample_tt[NUM_PACKET_SIZE_BINS];
 };
+
+#ifdef	_KERNEL
+
 #define	ATH_NODE_SAMPLE(an)	((struct sample_node *)&(an)[1])
 #define	IS_RATE_DEFINED(sn, rix)	(((sn)->ratemask & (1<<(rix))) != 0)
 
@@ -225,4 +236,7 @@
 	}
 	return tt;
 }
+
+#endif	/* _KERNEL */
+
 #endif /* _DEV_ATH_RATE_SAMPLE_H */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath.c
--- a/head/sys/dev/ath/if_ath.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath.c	Wed Jul 25 17:07:47 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath.c 234369 2012-04-17 06:02:41Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath.c 238729 2012-07-23 23:40:13Z adrian $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -102,19 +102,21 @@
 
 #include <dev/ath/if_ath_debug.h>
 #include <dev/ath/if_ath_misc.h>
+#include <dev/ath/if_ath_tsf.h>
 #include <dev/ath/if_ath_tx.h>
 #include <dev/ath/if_ath_sysctl.h>
 #include <dev/ath/if_ath_led.h>
 #include <dev/ath/if_ath_keycache.h>
+#include <dev/ath/if_ath_rx.h>
+#include <dev/ath/if_ath_rx_edma.h>
+#include <dev/ath/if_ath_tx_edma.h>
+#include <dev/ath/if_ath_beacon.h>
 #include <dev/ath/if_athdfs.h>
 
 #ifdef ATH_TX99_DIAG
 #include <dev/ath/ath_tx99/ath_tx99.h>
 #endif
 
-#define	ATH_KTR_INTR	KTR_SPARE4
-#define	ATH_KTR_ERR	KTR_SPARE3
-
 /*
  * ATH_BCBUF determines the number of vap's that can transmit
  * beacons and also (currently) the number of vap's that can
@@ -139,7 +141,6 @@
 static void	ath_init(void *);
 static void	ath_stop_locked(struct ifnet *);
 static void	ath_stop(struct ifnet *);
-static void	ath_start(struct ifnet *);
 static int	ath_reset_vap(struct ieee80211vap *, u_long);
 static int	ath_media_change(struct ifnet *);
 static void	ath_watchdog(void *);
@@ -151,23 +152,9 @@
 static void	ath_key_update_end(struct ieee80211vap *);
 static void	ath_update_mcast(struct ifnet *);
 static void	ath_update_promisc(struct ifnet *);
-static void	ath_mode_init(struct ath_softc *);
-static void	ath_setslottime(struct ath_softc *);
 static void	ath_updateslot(struct ifnet *);
-static int	ath_beaconq_setup(struct ath_hal *);
-static int	ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
-static void	ath_beacon_update(struct ieee80211vap *, int item);
-static void	ath_beacon_setup(struct ath_softc *, struct ath_buf *);
-static void	ath_beacon_proc(void *, int);
-static struct ath_buf *ath_beacon_generate(struct ath_softc *,
-			struct ieee80211vap *);
 static void	ath_bstuck_proc(void *, int);
 static void	ath_reset_proc(void *, int);
-static void	ath_beacon_return(struct ath_softc *, struct ath_buf *);
-static void	ath_beacon_free(struct ath_softc *);
-static void	ath_beacon_config(struct ath_softc *, struct ieee80211vap *);
-static void	ath_descdma_cleanup(struct ath_softc *sc,
-			struct ath_descdma *, ath_bufhead *);
 static int	ath_desc_alloc(struct ath_softc *);
 static void	ath_desc_free(struct ath_softc *);
 static struct ieee80211_node *ath_node_alloc(struct ieee80211vap *,
@@ -176,16 +163,9 @@
 static void	ath_node_free(struct ieee80211_node *);
 static void	ath_node_getsignal(const struct ieee80211_node *,
 			int8_t *, int8_t *);
-static int	ath_rxbuf_init(struct ath_softc *, struct ath_buf *);
-static void	ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
-			int subtype, int rssi, int nf);
-static void	ath_setdefantenna(struct ath_softc *, u_int);
-static void	ath_rx_proc(struct ath_softc *sc, int);
-static void	ath_rx_tasklet(void *, int);
 static void	ath_txq_init(struct ath_softc *sc, struct ath_txq *, int);
 static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype);
 static int	ath_tx_setup(struct ath_softc *, int, int);
-static int	ath_wme_update(struct ieee80211com *);
 static void	ath_tx_cleanupq(struct ath_softc *, struct ath_txq *);
 static void	ath_tx_cleanup(struct ath_softc *);
 static void	ath_tx_proc_q0(void *, int);
@@ -194,8 +174,6 @@
 static void	ath_txq_sched_tasklet(void *, int);
 static int	ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
 static void	ath_draintxq(struct ath_softc *, ATH_RESET_TYPE reset_type);
-static void	ath_stoprecv(struct ath_softc *, int);
-static int	ath_startrecv(struct ath_softc *);
 static void	ath_chan_change(struct ath_softc *, struct ieee80211_channel *);
 static void	ath_scan_start(struct ieee80211com *);
 static void	ath_scan_end(struct ieee80211com *);
@@ -222,16 +200,10 @@
 static void	ath_dfs_tasklet(void *, int);
 
 #ifdef IEEE80211_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, int);
-static void	ath_tdma_beacon_send(struct ath_softc *sc,
-		    struct ieee80211vap *vap);
-
+#include <dev/ath/if_ath_tdma.h>
+#endif
+
+#if 0
 #define	TDMA_EP_MULTIPLIER	(1<<10) /* pow2 to optimize out * and / */
 #define	TDMA_LPF_LEN		6
 #define	TDMA_DUMMY_MARKER	0x127
@@ -263,16 +235,20 @@
 SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval,
 	    0, "ANI calibration (msecs)");
 
-static	int ath_rxbuf = ATH_RXBUF;		/* # rx buffers to allocate */
+int ath_rxbuf = ATH_RXBUF;		/* # rx buffers to allocate */
 SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf,
 	    0, "rx buffers allocated");
 TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf);
-static	int ath_txbuf = ATH_TXBUF;		/* # tx buffers to allocate */
+int ath_txbuf = ATH_TXBUF;		/* # tx buffers to allocate */
 SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf,
 	    0, "tx buffers allocated");
 TUNABLE_INT("hw.ath.txbuf", &ath_txbuf);
-
-static	int ath_bstuck_threshold = 4;		/* max missed beacons */
+int ath_txbuf_mgmt = ATH_MGMT_TXBUF;	/* # mgmt tx buffers to allocate */
+SYSCTL_INT(_hw_ath, OID_AUTO, txbuf_mgmt, CTLFLAG_RW, &ath_txbuf_mgmt,
+	    0, "tx (mgmt) buffers allocated");
+TUNABLE_INT("hw.ath.txbuf_mgmt", &ath_txbuf_mgmt);
+
+int ath_bstuck_threshold = 4;		/* max missed beacons */
 SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold,
 	    0, "max missed beacon xmits before chip reset");
 
@@ -323,6 +299,21 @@
 #endif
 
 	/*
+	 * Setup the DMA/EDMA functions based on the current
+	 * hardware support.
+	 *
+	 * This is required before the descriptors are allocated.
+	 */
+	if (ath_hal_hasedma(sc->sc_ah)) {
+		sc->sc_isedma = 1;
+		ath_recv_setup_edma(sc);
+		ath_xmit_setup_edma(sc);
+	} else {
+		ath_recv_setup_legacy(sc);
+		ath_xmit_setup_legacy(sc);
+	}
+
+	/*
 	 * Check if the MAC has multi-rate retry support.
 	 * We do this by trying to setup a fake extended
 	 * descriptor.  MAC's that don't have support will
@@ -380,13 +371,31 @@
 	ath_setcurmode(sc, IEEE80211_MODE_11A);
 
 	/*
-	 * Allocate tx+rx descriptors and populate the lists.
+	 * Allocate TX descriptors and populate the lists.
 	 */
 	error = ath_desc_alloc(sc);
 	if (error != 0) {
-		if_printf(ifp, "failed to allocate descriptors: %d\n", error);
+		if_printf(ifp, "failed to allocate TX descriptors: %d\n",
+		    error);
 		goto bad;
 	}
+	error = ath_txdma_setup(sc);
+	if (error != 0) {
+		if_printf(ifp, "failed to allocate TX descriptors: %d\n",
+		    error);
+		goto bad;
+	}
+
+	/*
+	 * Allocate RX descriptors and populate the lists.
+	 */
+	error = ath_rxdma_setup(sc);
+	if (error != 0) {
+		if_printf(ifp, "failed to allocate RX descriptors: %d\n",
+		    error);
+		goto bad;
+	}
+
 	callout_init_mtx(&sc->sc_cal_ch, &sc->sc_mtx, 0);
 	callout_init_mtx(&sc->sc_wd_ch, &sc->sc_mtx, 0);
 
@@ -397,7 +406,7 @@
 	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET,
 		"%s taskq", ifp->if_xname);
 
-	TASK_INIT(&sc->sc_rxtask, 0, ath_rx_tasklet, sc);
+	TASK_INIT(&sc->sc_rxtask, 0, sc->sc_rx.recv_tasklet, sc);
 	TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc);
 	TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc);
 	TASK_INIT(&sc->sc_resettask,0, ath_reset_proc, sc);
@@ -545,7 +554,9 @@
 		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
 		| IEEE80211_C_SHSLOT		/* short slot time supported */
 		| IEEE80211_C_WPA		/* capable of WPA1+WPA2 */
+#ifndef	ATH_ENABLE_11N
 		| IEEE80211_C_BGSCAN		/* capable of bg scanning */
+#endif
 		| IEEE80211_C_TXFRAG		/* handle tx frags */
 #ifdef	ATH_ENABLE_DFS
 		| IEEE80211_C_DFS		/* Enable radar detection */
@@ -681,12 +692,6 @@
 		(void) ath_hal_settxchainmask(sc->sc_ah, tx_chainmask);
 	}
 
-	/*
-	 * The if_ath 11n support is completely not ready for normal use.
-	 * Enabling this option will likely break everything and everything.
-	 * Don't think of doing that unless you know what you're doing.
-	 */
-
 #ifdef	ATH_ENABLE_11N
 	/*
 	 * Query HT capabilities
@@ -748,6 +753,13 @@
 #endif
 
 	/*
+	 * Initial aggregation settings.
+	 */
+	sc->sc_hwq_limit = ATH_AGGR_MIN_QDEPTH;
+	sc->sc_tid_hwq_lo = ATH_AGGR_SCHED_LOW;
+	sc->sc_tid_hwq_hi = ATH_AGGR_SCHED_HIGH;
+
+	/*
 	 * Check if the hardware requires PCI register serialisation.
 	 * Some of the Owl based MACs require this.
 	 */
@@ -824,11 +836,26 @@
 	ic->ic_update_chw = ath_update_chw;
 #endif	/* ATH_ENABLE_11N */
 
+#ifdef	ATH_ENABLE_RADIOTAP_VENDOR_EXT
+	/*
+	 * There's one vendor bitmap entry in the RX radiotap
+	 * header; make sure that's taken into account.
+	 */
+	ieee80211_radiotap_attachv(ic,
+	    &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 0,
+		ATH_TX_RADIOTAP_PRESENT,
+	    &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 1,
+		ATH_RX_RADIOTAP_PRESENT);
+#else
+	/*
+	 * No vendor bitmap/extensions are present.
+	 */
 	ieee80211_radiotap_attach(ic,
 	    &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th),
 		ATH_TX_RADIOTAP_PRESENT,
 	    &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th),
 		ATH_RX_RADIOTAP_PRESENT);
+#endif	/* ATH_ENABLE_RADIOTAP_VENDOR_EXT */
 
 	/*
 	 * Setup dynamic sysctl's now that country code and
@@ -845,6 +872,8 @@
 bad2:
 	ath_tx_cleanup(sc);
 	ath_desc_free(sc);
+	ath_txdma_teardown(sc);
+	ath_rxdma_teardown(sc);
 bad:
 	if (ah)
 		ath_hal_detach(ah);
@@ -887,6 +916,7 @@
 
 	ath_dfs_detach(sc);
 	ath_desc_free(sc);
+	ath_rxdma_teardown(sc);
 	ath_tx_cleanup(sc);
 	ath_hal_detach(sc->sc_ah);	/* NB: sets chip in full sleep */
 	if_free(ifp);
@@ -1334,15 +1364,22 @@
 		__func__, ifp->if_flags);
 
 	sc->sc_resume_up = (ifp->if_flags & IFF_UP) != 0;
-	if (ic->ic_opmode == IEEE80211_M_STA)
-		ath_stop(ifp);
-	else
-		ieee80211_suspend_all(ic);
+
+	ieee80211_suspend_all(ic);
 	/*
 	 * NB: don't worry about putting the chip in low power
 	 * mode; pci will power off our socket on suspend and
 	 * CardBus detaches the device.
 	 */
+
+	/*
+	 * XXX ensure none of the taskqueues are running
+	 * XXX ensure sc_invalid is 1
+	 * XXX ensure the calibration callout is disabled
+	 */
+
+	/* Disable the PCIe PHY, complete with workarounds */
+	ath_hal_enablepcie(sc->sc_ah, 1, 1);
 }
 
 /*
@@ -1375,6 +1412,9 @@
 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
 		__func__, ifp->if_flags);
 
+	/* Re-enable PCIe, re-enable the PCIe bus */
+	ath_hal_enablepcie(ah, 0, 0);
+
 	/*
 	 * Must reset the chip before we reload the
 	 * keycache as we were powered down on suspend.
@@ -1391,23 +1431,8 @@
 	ath_led_config(sc);
 	ath_hal_setledstate(ah, HAL_LED_INIT);
 
-	if (sc->sc_resume_up) {
-		if (ic->ic_opmode == IEEE80211_M_STA) {
-			ath_init(sc);
-			ath_hal_setledstate(ah, HAL_LED_RUN);
-			/*
-			 * Program the beacon registers using the last rx'd
-			 * beacon frame and enable sync on the next beacon
-			 * we see.  This should handle the case where we
-			 * wakeup and find the same AP and also the case where
-			 * we wakeup and need to roam.  For the latter we
-			 * should get bmiss events that trigger a roam.
-			 */
-			ath_beacon_config(sc, NULL);
-			sc->sc_syncbeacon = 1;
-		} else
-			ieee80211_resume_all(ic);
-	}
+	if (sc->sc_resume_up)
+		ieee80211_resume_all(ic);
 
 	/* XXX beacons ? */
 }
@@ -1611,7 +1636,11 @@
 			/* bump tx trigger level */
 			ath_hal_updatetxtriglevel(ah, AH_TRUE);
 		}
-		if (status & HAL_INT_RX) {
+		/*
+		 * Handle both the legacy and RX EDMA interrupt bits.
+		 * Note that HAL_INT_RXLP is also HAL_INT_RXDESC.
+		 */
+		if (status & (HAL_INT_RX | HAL_INT_RXHP | HAL_INT_RXLP)) {
 			sc->sc_stats.ast_rx_intr++;
 			taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
 		}
@@ -1839,13 +1868,6 @@
 	sc->sc_beacons = 0;
 
 	/*
-	 * Initial aggregation settings.
-	 */
-	sc->sc_hwq_limit = ATH_AGGR_MIN_QDEPTH;
-	sc->sc_tid_hwq_lo = ATH_AGGR_SCHED_LOW;
-	sc->sc_tid_hwq_hi = ATH_AGGR_SCHED_HIGH;
-
-	/*
 	 * Setup the hardware after reset: the key cache
 	 * is filled as needed and the receive engine is
 	 * set going.  Frame transmit is handled entirely
@@ -1864,6 +1886,14 @@
 	sc->sc_imask = HAL_INT_RX | HAL_INT_TX
 		  | HAL_INT_RXEOL | HAL_INT_RXORN
 		  | HAL_INT_FATAL | HAL_INT_GLOBAL;
+
+	/*
+	 * Enable RX EDMA bits.  Note these overlap with
+	 * HAL_INT_RX and HAL_INT_RXDESC respectively.
+	 */
+	if (sc->sc_isedma)
+		sc->sc_imask |= (HAL_INT_RXHP | HAL_INT_RXLP);
+
 	/*
 	 * Enable MIB interrupts when there are hardware phy counters.
 	 * Note we only do this (at the moment) for station mode.
@@ -2115,7 +2145,7 @@
 	 * That way frames aren't dropped which shouldn't be.
 	 */
 	ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS));
-	ath_rx_proc(sc, 0);
+	ath_rx_flush(sc);
 
 	ath_settkipmic(sc);		/* configure TKIP MIC handling */
 	/* NB: indicate channel change so we do a full reset */
@@ -2225,13 +2255,17 @@
 }
 
 struct ath_buf *
-_ath_getbuf_locked(struct ath_softc *sc)
+_ath_getbuf_locked(struct ath_softc *sc, ath_buf_type_t btype)
 {
 	struct ath_buf *bf;
 
 	ATH_TXBUF_LOCK_ASSERT(sc);
 
-	bf = TAILQ_FIRST(&sc->sc_txbuf);
+	if (btype == ATH_BUFTYPE_MGMT)
+		bf = TAILQ_FIRST(&sc->sc_txbuf_mgmt);
+	else
+		bf = TAILQ_FIRST(&sc->sc_txbuf);
+
 	if (bf == NULL) {
 		sc->sc_stats.ast_tx_getnobuf++;
 	} else {
@@ -2241,18 +2275,43 @@
 		}
 	}
 
-	if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0)
-		TAILQ_REMOVE(&sc->sc_txbuf, bf, bf_list);
-	else
+	if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0) {
+		if (btype == ATH_BUFTYPE_MGMT)
+			TAILQ_REMOVE(&sc->sc_txbuf_mgmt, bf, bf_list);
+		else {
+			TAILQ_REMOVE(&sc->sc_txbuf, bf, bf_list);
+			sc->sc_txbuf_cnt--;
+
+			/*
+			 * This shuldn't happen; however just to be
+			 * safe print a warning and fudge the txbuf
+			 * count.
+			 */
+			if (sc->sc_txbuf_cnt < 0) {
+				device_printf(sc->sc_dev,
+				    "%s: sc_txbuf_cnt < 0?\n",
+				    __func__);
+				sc->sc_txbuf_cnt = 0;
+			}
+		}
+	} else
 		bf = NULL;
 
 	if (bf == NULL) {
+		/* XXX should check which list, mgmt or otherwise */
 		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: %s\n", __func__,
 		    TAILQ_FIRST(&sc->sc_txbuf) == NULL ?
 			"out of xmit buffers" : "xmit buffer busy");
 		return NULL;
 	}
 
+	/* XXX TODO: should do this at buffer list initialisation */
+	/* XXX (then, ensure the buffer has the right flag set) */
+	if (btype == ATH_BUFTYPE_MGMT)
+		bf->bf_flags |= ATH_BUF_MGMT;
+	else
+		bf->bf_flags &= (~ATH_BUF_MGMT);
+
 	/* Valid bf here; clear some basic fields */
 	bf->bf_next = NULL;	/* XXX just to be sure */
 	bf->bf_last = NULL;	/* XXX again, just to be sure */
@@ -2287,7 +2346,9 @@
 {
 	struct ath_buf *tbf;
 
-	tbf = ath_getbuf(sc);
+	tbf = ath_getbuf(sc,
+	    (bf->bf_flags & ATH_BUF_MGMT) ?
+	     ATH_BUFTYPE_MGMT : ATH_BUFTYPE_NORMAL);
 	if (tbf == NULL)
 		return NULL;	/* XXX failure? Why? */
 
@@ -2315,12 +2376,18 @@
 }
 
 struct ath_buf *
-ath_getbuf(struct ath_softc *sc)
+ath_getbuf(struct ath_softc *sc, ath_buf_type_t btype)
 {
 	struct ath_buf *bf;
 
 	ATH_TXBUF_LOCK(sc);
-	bf = _ath_getbuf_locked(sc);
+	bf = _ath_getbuf_locked(sc, btype);
+	/*
+	 * If a mgmt buffer was requested but we're out of those,
+	 * try requesting a normal one.
+	 */
+	if (bf == NULL && btype == ATH_BUFTYPE_MGMT)
+		bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL);
 	ATH_TXBUF_UNLOCK(sc);
 	if (bf == NULL) {
 		struct ifnet *ifp = sc->sc_ifp;
@@ -2334,7 +2401,7 @@
 	return bf;
 }
 
-static void
+void
 ath_start(struct ifnet *ifp)
 {
 	struct ath_softc *sc = ifp->if_softc;
@@ -2353,6 +2420,7 @@
 		    "%s: sc_inreset_cnt > 0; bailing\n", __func__);
 		ATH_PCU_UNLOCK(sc);
 		IF_LOCK(&ifp->if_snd);
+		sc->sc_stats.ast_tx_qstop++;
 		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
 		IF_UNLOCK(&ifp->if_snd);
 		return;
@@ -2361,17 +2429,28 @@
 	ATH_PCU_UNLOCK(sc);
 
 	for (;;) {
+		ATH_TXBUF_LOCK(sc);
+		if (sc->sc_txbuf_cnt <= sc->sc_txq_data_minfree) {
+			/* XXX increment counter? */
+			ATH_TXBUF_UNLOCK(sc);
+			IF_LOCK(&ifp->if_snd);
+			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+			IF_UNLOCK(&ifp->if_snd);
+			break;
+		}
+		ATH_TXBUF_UNLOCK(sc);
+		
 		/*
 		 * Grab a TX buffer and associated resources.
 		 */
-		bf = ath_getbuf(sc);
+		bf = ath_getbuf(sc, ATH_BUFTYPE_NORMAL);
 		if (bf == NULL)
 			break;
 
 		IFQ_DEQUEUE(&ifp->if_snd, m);
 		if (m == NULL) {
 			ATH_TXBUF_LOCK(sc);
-			TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+			ath_returnbuf_head(sc, bf);
 			ATH_TXBUF_UNLOCK(sc);
 			break;
 		}
@@ -2414,7 +2493,7 @@
 			bf->bf_m = NULL;
 			bf->bf_node = NULL;
 			ATH_TXBUF_LOCK(sc);
-			TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+			ath_returnbuf_head(sc, bf);
 			ath_txfrag_cleanup(sc, &frags, ni);
 			ATH_TXBUF_UNLOCK(sc);
 			if (ni != NULL)
@@ -2485,102 +2564,6 @@
 	taskqueue_unblock(sc->sc_tq);
 }
 
-/*
- * Calculate the receive filter according to the
- * operating mode and state:
- *
- * o always accept unicast, broadcast, and multicast traffic
- * o accept PHY error frames when hardware doesn't have MIB support
- *   to count and we need them for ANI (sta mode only until recently)
- *   and we are not scanning (ANI is disabled)
- *   NB: older hal's add rx filter bits out of sight and we need to
- *	 blindly preserve them
- * o probe request frames are accepted only when operating in
- *   hostap, adhoc, mesh, or monitor modes
- * o enable promiscuous mode
- *   - when in monitor mode
- *   - if interface marked PROMISC (assumes bridge setting is filtered)
- * o accept beacons:
- *   - when operating in station mode for collecting rssi data when
- *     the station is otherwise quiet, or
- *   - when operating in adhoc mode so the 802.11 layer creates
- *     node table entries for peers,
- *   - when scanning
- *   - when doing s/w beacon miss (e.g. for ap+sta)
- *   - when operating in ap mode in 11g to detect overlapping bss that
- *     require protection
- *   - when operating in mesh mode to detect neighbors
- * o accept control frames:
- *   - when in monitor mode
- * XXX HT protection for 11n
- */
-static u_int32_t
-ath_calcrxfilter(struct ath_softc *sc)
-{
-	struct ifnet *ifp = sc->sc_ifp;
-	struct ieee80211com *ic = ifp->if_l2com;
-	u_int32_t rfilt;
-
-	rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
-	if (!sc->sc_needmib && !sc->sc_scanning)
-		rfilt |= HAL_RX_FILTER_PHYERR;
-	if (ic->ic_opmode != IEEE80211_M_STA)
-		rfilt |= HAL_RX_FILTER_PROBEREQ;
-	/* XXX ic->ic_monvaps != 0? */
-	if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC))
-		rfilt |= HAL_RX_FILTER_PROM;
-	if (ic->ic_opmode == IEEE80211_M_STA ||
-	    ic->ic_opmode == IEEE80211_M_IBSS ||
-	    sc->sc_swbmiss || sc->sc_scanning)
-		rfilt |= HAL_RX_FILTER_BEACON;
-	/*
-	 * NB: We don't recalculate the rx filter when
-	 * ic_protmode changes; otherwise we could do
-	 * this only when ic_protmode != NONE.
-	 */
-	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
-	    IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
-		rfilt |= HAL_RX_FILTER_BEACON;
-
-	/*
-	 * Enable hardware PS-POLL RX only for hostap mode;
-	 * STA mode sends PS-POLL frames but never
-	 * receives them.
-	 */
-	if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL,
-	    0, NULL) == HAL_OK &&
-	    ic->ic_opmode == IEEE80211_M_HOSTAP)
-		rfilt |= HAL_RX_FILTER_PSPOLL;
-
-	if (sc->sc_nmeshvaps) {
-		rfilt |= HAL_RX_FILTER_BEACON;
-		if (sc->sc_hasbmatch)
-			rfilt |= HAL_RX_FILTER_BSSID;
-		else
-			rfilt |= HAL_RX_FILTER_PROM;
-	}
-	if (ic->ic_opmode == IEEE80211_M_MONITOR)
-		rfilt |= HAL_RX_FILTER_CONTROL;
-
-	/*
-	 * Enable RX of compressed BAR frames only when doing
-	 * 802.11n. Required for A-MPDU.
-	 */
-	if (IEEE80211_IS_CHAN_HT(ic->ic_curchan))
-		rfilt |= HAL_RX_FILTER_COMPBAR;
-
-	/*
-	 * Enable radar PHY errors if requested by the
-	 * DFS module.
-	 */
-	if (sc->sc_dodfs)
-		rfilt |= HAL_RX_FILTER_PHYRADAR;
-
-	DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n",
-	    __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags);
-	return rfilt;
-}
-
 static void
 ath_update_promisc(struct ifnet *ifp)
 {
@@ -2630,7 +2613,7 @@
 		__func__, mfilt[0], mfilt[1]);
 }
 
-static void
+void
 ath_mode_init(struct ath_softc *sc)
 {
 	struct ifnet *ifp = sc->sc_ifp;
@@ -2644,6 +2627,13 @@
 	/* configure operational mode */
 	ath_hal_setopmode(ah);
 
+	DPRINTF(sc, ATH_DEBUG_STATE | ATH_DEBUG_MODE,
+	    "%s: ah=%p, ifp=%p, if_addr=%p\n",
+	    __func__,
+	    ah,
+	    ifp,
+	    (ifp == NULL) ? NULL : ifp->if_addr);
+
 	/* handle any link-level address change */
 	ath_hal_setmac(ah, IF_LLADDR(ifp));
 
@@ -2654,7 +2644,7 @@
 /*
  * Set the slot time based on the current setting.
  */
-static void
+void
 ath_setslottime(struct ath_softc *sc)
 {
 	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
@@ -2707,248 +2697,10 @@
 }
 
 /*
- * Setup a h/w transmit queue for beacons.
- */
-static int
-ath_beaconq_setup(struct ath_hal *ah)
-{
-	HAL_TXQ_INFO qi;
-
-	memset(&qi, 0, sizeof(qi));
-	qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
-	qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
-	qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
-	/* NB: for dynamic turbo, don't enable any other interrupts */
-	qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE;
-	return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi);
-}
-
-/*
- * Setup the transmit queue parameters for the beacon queue.
- */
-static int
-ath_beaconq_config(struct ath_softc *sc)
-{
-#define	ATH_EXPONENT_TO_VALUE(v)	((1<<(v))-1)
-	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
-	struct ath_hal *ah = sc->sc_ah;
-	HAL_TXQ_INFO qi;
-
-	ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi);
-	if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
-	    ic->ic_opmode == IEEE80211_M_MBSS) {
-		/*
-		 * Always burst out beacon and CAB traffic.
-		 */
-		qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT;
-		qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT;
-		qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT;
-	} else {
-		struct wmeParams *wmep =
-			&ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE];
-		/*
-		 * Adhoc mode; important thing is to use 2x cwmin.
-		 */
-		qi.tqi_aifs = wmep->wmep_aifsn;
-		qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
-		qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
-	}
-
-	if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) {
-		device_printf(sc->sc_dev, "unable to update parameters for "
-			"beacon hardware queue!\n");
-		return 0;
-	} else {
-		ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */
-		return 1;
-	}
-#undef ATH_EXPONENT_TO_VALUE
-}
-
-/*
- * Allocate and setup an initial beacon frame.
- */
-static int
-ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
-{
-	struct ieee80211vap *vap = ni->ni_vap;
-	struct ath_vap *avp = ATH_VAP(vap);
-	struct ath_buf *bf;
-	struct mbuf *m;
-	int error;
-
-	bf = avp->av_bcbuf;
-	DPRINTF(sc, ATH_DEBUG_NODE, "%s: bf_m=%p, bf_node=%p\n",
-	    __func__, bf->bf_m, bf->bf_node);
-	if (bf->bf_m != NULL) {
-		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-		m_freem(bf->bf_m);
-		bf->bf_m = NULL;
-	}
-	if (bf->bf_node != NULL) {
-		ieee80211_free_node(bf->bf_node);
-		bf->bf_node = NULL;
-	}
-
-	/*
-	 * NB: the beacon data buffer must be 32-bit aligned;
-	 * we assume the mbuf routines will return us something
-	 * with this alignment (perhaps should assert).
-	 */
-	m = ieee80211_beacon_alloc(ni, &avp->av_boff);
-	if (m == NULL) {
-		device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__);
-		sc->sc_stats.ast_be_nombuf++;
-		return ENOMEM;
-	}
-	error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
-				     bf->bf_segs, &bf->bf_nseg,
-				     BUS_DMA_NOWAIT);
-	if (error != 0) {
-		device_printf(sc->sc_dev,
-		    "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n",
-		    __func__, error);
-		m_freem(m);
-		return error;
-	}
-
-	/*
-	 * Calculate a TSF adjustment factor required for staggered
-	 * beacons.  Note that we assume the format of the beacon
-	 * frame leaves the tstamp field immediately following the
-	 * header.
-	 */
-	if (sc->sc_stagbeacons && avp->av_bslot > 0) {
-		uint64_t tsfadjust;
-		struct ieee80211_frame *wh;
-
-		/*
-		 * The beacon interval is in TU's; the TSF is in usecs.
-		 * We figure out how many TU's to add to align the timestamp
-		 * then convert to TSF units and handle byte swapping before
-		 * inserting it in the frame.  The hardware will then add this
-		 * each time a beacon frame is sent.  Note that we align vap's
-		 * 1..N and leave vap 0 untouched.  This means vap 0 has a
-		 * timestamp in one beacon interval while the others get a
-		 * timstamp aligned to the next interval.
-		 */
-		tsfadjust = ni->ni_intval *
-		    (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF;
-		tsfadjust = htole64(tsfadjust << 10);	/* TU -> TSF */
-
-		DPRINTF(sc, ATH_DEBUG_BEACON,
-		    "%s: %s beacons bslot %d intval %u tsfadjust %llu\n",
-		    __func__, sc->sc_stagbeacons ? "stagger" : "burst",
-		    avp->av_bslot, ni->ni_intval,
-		    (long long unsigned) le64toh(tsfadjust));
-
-		wh = mtod(m, struct ieee80211_frame *);
-		memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust));
-	}
-	bf->bf_m = m;
-	bf->bf_node = ieee80211_ref_node(ni);
-
-	return 0;
-}
-
-/*
- * Setup the beacon frame for transmit.
- */
-static void
-ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
-{
-#define	USE_SHPREAMBLE(_ic) \
-	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
-		== IEEE80211_F_SHPREAMBLE)
-	struct ieee80211_node *ni = bf->bf_node;
-	struct ieee80211com *ic = ni->ni_ic;
-	struct mbuf *m = bf->bf_m;
-	struct ath_hal *ah = sc->sc_ah;
-	struct ath_desc *ds;
-	int flags, antenna;
-	const HAL_RATE_TABLE *rt;
-	u_int8_t rix, rate;
-
-	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n",
-		__func__, m, m->m_len);
-
-	/* setup descriptors */
-	ds = bf->bf_desc;
-	bf->bf_last = bf;
-	bf->bf_lastds = ds;
-
-	flags = HAL_TXDESC_NOACK;
-	if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) {
-		ds->ds_link = bf->bf_daddr;	/* self-linked */
-		flags |= HAL_TXDESC_VEOL;
-		/*
-		 * Let hardware handle antenna switching.
-		 */
-		antenna = sc->sc_txantenna;
-	} else {
-		ds->ds_link = 0;
-		/*
-		 * Switch antenna every 4 beacons.
-		 * XXX assumes two antenna
-		 */
-		if (sc->sc_txantenna != 0)
-			antenna = sc->sc_txantenna;
-		else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0)
-			antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1);
-		else
-			antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
-	}
-
-	KASSERT(bf->bf_nseg == 1,
-		("multi-segment beacon frame; nseg %u", bf->bf_nseg));
-	ds->ds_data = bf->bf_segs[0].ds_addr;
-	/*
-	 * Calculate rate code.
-	 * XXX everything at min xmit rate
-	 */
-	rix = 0;
-	rt = sc->sc_currates;
-	rate = rt->info[rix].rateCode;
-	if (USE_SHPREAMBLE(ic))
-		rate |= rt->info[rix].shortPreamble;
-	ath_hal_setuptxdesc(ah, ds
-		, m->m_len + IEEE80211_CRC_LEN	/* frame length */
-		, sizeof(struct ieee80211_frame)/* header length */
-		, HAL_PKT_TYPE_BEACON		/* Atheros packet type */
-		, ni->ni_txpower		/* txpower XXX */
-		, rate, 1			/* series 0 rate/tries */
-		, HAL_TXKEYIX_INVALID		/* no encryption */
-		, antenna			/* antenna mode */
-		, flags				/* no ack, veol for beacons */
-		, 0				/* rts/cts rate */
-		, 0				/* rts/cts duration */
-	);
-	/* NB: beacon's BufLen must be a multiple of 4 bytes */
-	ath_hal_filltxdesc(ah, ds
-		, roundup(m->m_len, 4)		/* buffer length */
-		, AH_TRUE			/* first segment */
-		, AH_TRUE			/* last segment */
-		, ds				/* first descriptor */
-	);
-#if 0
-	ath_desc_swap(ds);
-#endif
-#undef USE_SHPREAMBLE
-}
-
-static void
-ath_beacon_update(struct ieee80211vap *vap, int item)
-{
-	struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff;
-
-	setbit(bo->bo_flags, item);
-}
-
-/*
  * Append the contents of src to dst; both queues
  * are assumed to be locked.
  */
-static void
+void
 ath_txqmove(struct ath_txq *dst, struct ath_txq *src)
 {
 
@@ -2965,255 +2717,6 @@
 }
 
 /*
- * Transmit a beacon frame at SWBA.  Dynamic updates to the
- * frame contents are done as needed and the slot time is
- * also adjusted based on current state.
- */
-static void
-ath_beacon_proc(void *arg, int pending)
-{
-	struct ath_softc *sc = arg;
-	struct ath_hal *ah = sc->sc_ah;
-	struct ieee80211vap *vap;
-	struct ath_buf *bf;
-	int slot, otherant;
-	uint32_t bfaddr;
-
-	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n",
-		__func__, pending);
-	/*
-	 * 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++;
-		sc->sc_stats.ast_be_missed++;
-		DPRINTF(sc, ATH_DEBUG_BEACON,
-			"%s: missed %u consecutive beacons\n",
-			__func__, sc->sc_bmisscount);
-		if (sc->sc_bmisscount >= ath_bstuck_threshold)
-			taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask);
-		return;
-	}
-	if (sc->sc_bmisscount != 0) {
-		DPRINTF(sc, ATH_DEBUG_BEACON,
-			"%s: resume beacon xmit after %u misses\n",
-			__func__, sc->sc_bmisscount);
-		sc->sc_bmisscount = 0;
-	}
-
-	if (sc->sc_stagbeacons) {			/* staggered beacons */
-		struct ieee80211com *ic = sc->sc_ifp->if_l2com;
-		uint32_t tsftu;
-
-		tsftu = ath_hal_gettsf32(ah) >> 10;
-		/* XXX lintval */
-		slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval;
-		vap = sc->sc_bslot[(slot+1) % ATH_BCBUF];
-		bfaddr = 0;
-		if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) {
-			bf = ath_beacon_generate(sc, vap);
-			if (bf != NULL)
-				bfaddr = bf->bf_daddr;
-		}
-	} else {					/* burst'd beacons */
-		uint32_t *bflink = &bfaddr;
-
-		for (slot = 0; slot < ATH_BCBUF; slot++) {
-			vap = sc->sc_bslot[slot];
-			if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) {
-				bf = ath_beacon_generate(sc, vap);
-				if (bf != NULL) {
-					*bflink = bf->bf_daddr;
-					bflink = &bf->bf_desc->ds_link;
-				}
-			}
-		}
-		*bflink = 0;				/* terminate list */
-	}
-
-	/*
-	 * Handle slot time change when a non-ERP station joins/leaves
-	 * an 11g network.  The 802.11 layer notifies us via callback,
-	 * we mark updateslot, then wait one beacon before effecting
-	 * the change.  This gives associated stations at least one
-	 * beacon interval to note the state change.
-	 */
-	/* XXX locking */
-	if (sc->sc_updateslot == UPDATE) {
-		sc->sc_updateslot = COMMIT;	/* commit next beacon */
-		sc->sc_slotupdate = slot;
-	} else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot)
-		ath_setslottime(sc);		/* commit change to h/w */
-
-	/*
-	 * 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 && (!sc->sc_stagbeacons || slot == 0)) {
-		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;
-	}
-
-	if (bfaddr != 0) {
-		/*
-		 * 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: cabq traffic should already be queued and primed */
-		ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr);
-		ath_hal_txstart(ah, sc->sc_bhalq);
-
-		sc->sc_stats.ast_be_xmit++;
-	}
-}
-
-static struct ath_buf *
-ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap)
-{
-	struct ath_vap *avp = ATH_VAP(vap);
-	struct ath_txq *cabq = sc->sc_cabq;
-	struct ath_buf *bf;
-	struct mbuf *m;
-	int nmcastq, error;
-
-	KASSERT(vap->iv_state >= IEEE80211_S_RUN,
-	    ("not running, state %d", vap->iv_state));
-	KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer"));
-
-	/*
-	 * Update dynamic beacon contents.  If this returns
-	 * non-zero then we need to remap the memory because
-	 * the beacon frame changed size (probably because
-	 * of the TIM bitmap).
-	 */
-	bf = avp->av_bcbuf;
-	m = bf->bf_m;
-	/* XXX lock mcastq? */
-	nmcastq = avp->av_mcastq.axq_depth;
-
-	if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) {
-		/* XXX too conservative? */
-		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
-					     bf->bf_segs, &bf->bf_nseg,
-					     BUS_DMA_NOWAIT);
-		if (error != 0) {
-			if_printf(vap->iv_ifp,
-			    "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
-			    __func__, error);
-			return NULL;
-		}
-	}
-	if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) {
-		DPRINTF(sc, ATH_DEBUG_BEACON,
-		    "%s: cabq did not drain, mcastq %u cabq %u\n",
-		    __func__, nmcastq, cabq->axq_depth);
-		sc->sc_stats.ast_cabq_busy++;
-		if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) {
-			/*
-			 * CABQ traffic from a previous vap is still pending.
-			 * We must drain the q before this beacon frame goes
-			 * out as otherwise this vap's stations will get cab
-			 * frames from a different vap.
-			 * XXX could be slow causing us to miss DBA
-			 */
-			ath_tx_draintxq(sc, cabq);
-		}
-	}
-	ath_beacon_setup(sc, bf);
-	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
-
-	/*
-	 * Enable the CAB queue before the beacon queue to
-	 * insure cab frames are triggered by this beacon.
-	 */
-	if (avp->av_boff.bo_tim[4] & 1) {
-		struct ath_hal *ah = sc->sc_ah;
-
-		/* NB: only at DTIM */
-		ATH_TXQ_LOCK(cabq);
-		ATH_TXQ_LOCK(&avp->av_mcastq);
-		if (nmcastq) {
-			struct ath_buf *bfm;
-
-			/*
-			 * Move frames from the s/w mcast q to the h/w cab q.
-			 * XXX MORE_DATA bit
-			 */
-			bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q);
-			if (cabq->axq_link != NULL) {
-				*cabq->axq_link = bfm->bf_daddr;
-			} else
-				ath_hal_puttxbuf(ah, cabq->axq_qnum,
-					bfm->bf_daddr);
-			ath_txqmove(cabq, &avp->av_mcastq);
-
-			sc->sc_stats.ast_cabq_xmit += nmcastq;
-		}
-		/* NB: gated by beacon so safe to start here */
-		if (! TAILQ_EMPTY(&(cabq->axq_q)))
-			ath_hal_txstart(ah, cabq->axq_qnum);
-		ATH_TXQ_UNLOCK(&avp->av_mcastq);
-		ATH_TXQ_UNLOCK(cabq);
-	}
-	return bf;
-}
-
-static void
-ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap)
-{
-	struct ath_vap *avp = ATH_VAP(vap);
-	struct ath_hal *ah = sc->sc_ah;
-	struct ath_buf *bf;
-	struct mbuf *m;
-	int error;
-
-	KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer"));
-
-	/*
-	 * Update dynamic beacon contents.  If this returns
-	 * non-zero then we need to remap the memory because
-	 * the beacon frame changed size (probably because
-	 * of the TIM bitmap).
-	 */
-	bf = avp->av_bcbuf;
-	m = bf->bf_m;
-	if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) {
-		/* XXX too conservative? */
-		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
-					     bf->bf_segs, &bf->bf_nseg,
-					     BUS_DMA_NOWAIT);
-		if (error != 0) {
-			if_printf(vap->iv_ifp,
-			    "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
-			    __func__, error);
-			return;
-		}
-	}
-	ath_beacon_setup(sc, bf);
-	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
-
-	/* NB: caller is known to have already stopped tx dma */
-	ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
-	ath_hal_txstart(ah, sc->sc_bhalq);
-}
-
-/*
  * Reset the hardware, with no loss.
  *
  * This can't be used for a general case reset.
@@ -3253,255 +2756,6 @@
 	ath_reset(ifp, ATH_RESET_NOLOSS);
 }
 
-/*
- * Reclaim beacon resources and return buffer to the pool.
- */
-static void
-ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf)
-{
-
-	DPRINTF(sc, ATH_DEBUG_NODE, "%s: free bf=%p, bf_m=%p, bf_node=%p\n",
-	    __func__, bf, bf->bf_m, bf->bf_node);
-	if (bf->bf_m != NULL) {
-		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-		m_freem(bf->bf_m);
-		bf->bf_m = NULL;
-	}
-	if (bf->bf_node != NULL) {
-		ieee80211_free_node(bf->bf_node);
-		bf->bf_node = NULL;
-	}
-	TAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list);
-}
-
-/*
- * Reclaim beacon resources.
- */
-static void
-ath_beacon_free(struct ath_softc *sc)
-{
-	struct ath_buf *bf;
-
-	TAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) {
-		DPRINTF(sc, ATH_DEBUG_NODE,
-		    "%s: free bf=%p, bf_m=%p, bf_node=%p\n",
-		        __func__, bf, bf->bf_m, bf->bf_node);
-		if (bf->bf_m != NULL) {
-			bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-			m_freem(bf->bf_m);
-			bf->bf_m = NULL;
-		}
-		if (bf->bf_node != NULL) {
-			ieee80211_free_node(bf->bf_node);
-			bf->bf_node = NULL;
-		}
-	}
-}
-
-/*
- * Configure the beacon and sleep timers.
- *
- * When operating as an AP this resets the TSF and sets
- * up the hardware to notify us when we need to issue beacons.
- *
- * When operating in station mode this sets up the beacon
- * timers according to the timestamp of the last received
- * beacon and the current TSF, configures PCF and DTIM
- * handling, programs the sleep registers so the hardware
- * will wakeup in time to receive beacons, and configures
- * the beacon miss handling so we'll receive a BMISS
- * interrupt when we stop seeing beacons from the AP
- * we've associated with.
- */
-static void
-ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
-{
-#define	TSF_TO_TU(_h,_l) \
-	((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
-#define	FUDGE	2
-	struct ath_hal *ah = sc->sc_ah;
-	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
-	struct ieee80211_node *ni;
-	u_int32_t nexttbtt, intval, tsftu;
-	u_int64_t tsf;
-
-	if (vap == NULL)
-		vap = TAILQ_FIRST(&ic->ic_vaps);	/* XXX */
-	ni = ieee80211_ref_node(vap->iv_bss);
-
-	/* extract tstamp from last beacon and convert to TU */
-	nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4),
-			     LE_READ_4(ni->ni_tstamp.data));
-	if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
-	    ic->ic_opmode == IEEE80211_M_MBSS) {
-		/*
-		 * For multi-bss ap/mesh support beacons are either staggered
-		 * evenly over N slots or burst together.  For the former
-		 * arrange for the SWBA to be delivered for each slot.
-		 * Slots that are not occupied will generate nothing.
-		 */
-		/* NB: the beacon interval is kept internally in TU's */
-		intval = ni->ni_intval & HAL_BEACON_PERIOD;
-		if (sc->sc_stagbeacons)
-			intval /= ATH_BCBUF;
-	} else {
-		/* NB: the beacon interval is kept internally in TU's */
-		intval = ni->ni_intval & HAL_BEACON_PERIOD;
-	}
-	if (nexttbtt == 0)		/* e.g. for ap mode */
-		nexttbtt = intval;
-	else if (intval)		/* NB: can be 0 for monitor mode */
-		nexttbtt = roundup(nexttbtt, intval);
-	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
-		__func__, nexttbtt, intval, ni->ni_intval);
-	if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) {
-		HAL_BEACON_STATE bs;
-		int dtimperiod, dtimcount;
-		int cfpperiod, cfpcount;
-
-		/*
-		 * Setup dtim and cfp parameters according to
-		 * last beacon we received (which may be none).
-		 */
-		dtimperiod = ni->ni_dtim_period;
-		if (dtimperiod <= 0)		/* NB: 0 if not known */
-			dtimperiod = 1;
-		dtimcount = ni->ni_dtim_count;
-		if (dtimcount >= dtimperiod)	/* NB: sanity check */
-			dtimcount = 0;		/* XXX? */
-		cfpperiod = 1;			/* NB: no PCF support yet */
-		cfpcount = 0;
-		/*
-		 * Pull nexttbtt forward to reflect the current
-		 * TSF and calculate dtim+cfp state for the result.
-		 */
-		tsf = ath_hal_gettsf64(ah);
-		tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
-		do {
-			nexttbtt += intval;
-			if (--dtimcount < 0) {
-				dtimcount = dtimperiod - 1;
-				if (--cfpcount < 0)
-					cfpcount = cfpperiod - 1;
-			}
-		} while (nexttbtt < tsftu);
-		memset(&bs, 0, sizeof(bs));
-		bs.bs_intval = intval;
-		bs.bs_nexttbtt = nexttbtt;
-		bs.bs_dtimperiod = dtimperiod*intval;
-		bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
-		bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
-		bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
-		bs.bs_cfpmaxduration = 0;
-#if 0
-		/*
-		 * The 802.11 layer records the offset to the DTIM
-		 * bitmap while receiving beacons; use it here to
-		 * enable h/w detection of our AID being marked in
-		 * the bitmap vector (to indicate frames for us are
-		 * pending at the AP).
-		 * XXX do DTIM handling in s/w to WAR old h/w bugs
-		 * XXX enable based on h/w rev for newer chips
-		 */
-		bs.bs_timoffset = ni->ni_timoff;
-#endif
-		/*
-		 * Calculate the number of consecutive beacons to miss
-		 * before taking a BMISS interrupt.
-		 * Note that we clamp the result to at most 10 beacons.
-		 */
-		bs.bs_bmissthreshold = vap->iv_bmissthreshold;
-		if (bs.bs_bmissthreshold > 10)
-			bs.bs_bmissthreshold = 10;
-		else if (bs.bs_bmissthreshold <= 0)
-			bs.bs_bmissthreshold = 1;
-
-		/*
-		 * Calculate sleep duration.  The configuration is
-		 * given in ms.  We insure a multiple of the beacon
-		 * period is used.  Also, if the sleep duration is
-		 * greater than the DTIM period then it makes senses
-		 * to make it a multiple of that.
-		 *
-		 * XXX fixed at 100ms
-		 */
-		bs.bs_sleepduration =
-			roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval);
-		if (bs.bs_sleepduration > bs.bs_dtimperiod)
-			bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
-
-		DPRINTF(sc, ATH_DEBUG_BEACON,
-			"%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n"
-			, __func__
-			, tsf, tsftu
-			, bs.bs_intval
-			, bs.bs_nexttbtt
-			, bs.bs_dtimperiod
-			, bs.bs_nextdtim
-			, bs.bs_bmissthreshold
-			, bs.bs_sleepduration
-			, bs.bs_cfpperiod
-			, bs.bs_cfpmaxduration
-			, bs.bs_cfpnext
-			, bs.bs_timoffset
-		);
-		ath_hal_intrset(ah, 0);
-		ath_hal_beacontimers(ah, &bs);
-		sc->sc_imask |= HAL_INT_BMISS;
-		ath_hal_intrset(ah, sc->sc_imask);
-	} else {
-		ath_hal_intrset(ah, 0);
-		if (nexttbtt == intval)
-			intval |= HAL_BEACON_RESET_TSF;
-		if (ic->ic_opmode == IEEE80211_M_IBSS) {
-			/*
-			 * In IBSS mode enable the beacon timers but only
-			 * enable SWBA interrupts if we need to manually
-			 * prepare beacon frames.  Otherwise we use a
-			 * self-linked tx descriptor and let the hardware
-			 * deal with things.
-			 */
-			intval |= HAL_BEACON_ENA;
-			if (!sc->sc_hasveol)
-				sc->sc_imask |= HAL_INT_SWBA;
-			if ((intval & HAL_BEACON_RESET_TSF) == 0) {
-				/*
-				 * Pull nexttbtt forward to reflect
-				 * the current TSF.
-				 */
-				tsf = ath_hal_gettsf64(ah);
-				tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
-				do {
-					nexttbtt += intval;
-				} while (nexttbtt < tsftu);
-			}
-			ath_beaconq_config(sc);
-		} else if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
-		    ic->ic_opmode == IEEE80211_M_MBSS) {
-			/*
-			 * In AP/mesh mode we enable the beacon timers
-			 * and SWBA interrupts to prepare beacon frames.
-			 */
-			intval |= HAL_BEACON_ENA;
-			sc->sc_imask |= HAL_INT_SWBA;	/* beacon prepare */
-			ath_beaconq_config(sc);
-		}
-		ath_hal_beaconinit(ah, nexttbtt, intval);
-		sc->sc_bmisscount = 0;
-		ath_hal_intrset(ah, sc->sc_imask);
-		/*
-		 * When using a self-linked beacon descriptor in
-		 * ibss mode load it once here.
-		 */
-		if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol)
-			ath_beacon_start_adhoc(sc, vap);
-	}
-	sc->sc_syncbeacon = 0;
-	ieee80211_free_node(ni);
-#undef FUDGE
-#undef TSF_TO_TU
-}
-
 static void
 ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
 {
@@ -3510,10 +2764,10 @@
 	*paddr = segs->ds_addr;
 }
 
-static int
+int
 ath_descdma_setup(struct ath_softc *sc,
 	struct ath_descdma *dd, ath_bufhead *head,
-	const char *name, int nbuf, int ndesc)
+	const char *name, int ds_size, int nbuf, int ndesc)
 {
 #define	DS2PHYS(_dd, _ds) \
 	((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
@@ -3523,15 +2777,15 @@
 	uint8_t *ds;
 	struct ath_buf *bf;
 	int i, bsize, error;
-	int desc_len;
-
-	desc_len = sizeof(struct ath_desc);
-
-	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n",
-	    __func__, name, nbuf, ndesc);
+
+	dd->dd_descsize = ds_size;
+
+	DPRINTF(sc, ATH_DEBUG_RESET,
+	    "%s: %s DMA: %u buffers %u desc/buf, %d bytes per descriptor\n",
+	    __func__, name, nbuf, ndesc, dd->dd_descsize);
 
 	dd->dd_name = name;
-	dd->dd_desc_len = desc_len * nbuf * ndesc;
+	dd->dd_desc_len = dd->dd_descsize * nbuf * ndesc;
 
 	/*
 	 * Merlin work-around:
@@ -3539,7 +2793,7 @@
 	 * Assume one skipped descriptor per 4KB page.
 	 */
 	if (! ath_hal_split4ktrans(sc->sc_ah)) {
-		int numdescpage = 4096 / (desc_len * ndesc);
+		int numdescpage = 4096 / (dd->dd_descsize * ndesc);
 		dd->dd_desc_len = (nbuf / numdescpage + 1) * 4096;
 	}
 
@@ -3606,7 +2860,7 @@
 	dd->dd_bufptr = bf;
 
 	TAILQ_INIT(head);
-	for (i = 0; i < nbuf; i++, bf++, ds += (ndesc * desc_len)) {
+	for (i = 0; i < nbuf; i++, bf++, ds += (ndesc * dd->dd_descsize)) {
 		bf->bf_desc = (struct ath_desc *) ds;
 		bf->bf_daddr = DS2PHYS(dd, ds);
 		if (! ath_hal_split4ktrans(sc->sc_ah)) {
@@ -3616,7 +2870,7 @@
 			 * in the descriptor.
 			 */
 			 if (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr,
-			     desc_len * ndesc)) {
+			     dd->dd_descsize * ndesc)) {
 				/* Start at the next page */
 				ds += 0x1000 - (bf->bf_daddr & 0xFFF);
 				bf->bf_desc = (struct ath_desc *) ds;
@@ -3649,17 +2903,82 @@
 #undef ATH_DESC_4KB_BOUND_CHECK
 }
 
-static void
+/*
+ * Allocate ath_buf entries but no descriptor contents.
+ *
+ * This is for RX EDMA where the descriptors are the header part of
+ * the RX buffer.
+ */
+int
+ath_descdma_setup_rx_edma(struct ath_softc *sc,
+	struct ath_descdma *dd, ath_bufhead *head,
+	const char *name, int nbuf, int rx_status_len)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ath_buf *bf;
+	int i, bsize, error;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers\n",
+	    __func__, name, nbuf);
+
+	dd->dd_name = name;
+	/*
+	 * This is (mostly) purely for show.  We're not allocating any actual
+	 * descriptors here as EDMA RX has the descriptor be part
+	 * of the RX buffer.
+	 *
+	 * However, dd_desc_len is used by ath_descdma_free() to determine
+	 * whether we have already freed this DMA mapping.
+	 */
+	dd->dd_desc_len = rx_status_len * nbuf;
+	dd->dd_descsize = rx_status_len;
+
+	/* allocate rx buffers */
+	bsize = sizeof(struct ath_buf) * nbuf;
+	bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO);
+	if (bf == NULL) {
+		if_printf(ifp, "malloc of %s buffers failed, size %u\n",
+			dd->dd_name, bsize);
+		error = ENOMEM;
+		goto fail3;
+	}
+	dd->dd_bufptr = bf;
+
+	TAILQ_INIT(head);
+	for (i = 0; i < nbuf; i++, bf++) {
+		bf->bf_desc = NULL;
+		bf->bf_daddr = 0;
+		bf->bf_lastds = NULL;	/* Just an initial value */
+
+		error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT,
+				&bf->bf_dmamap);
+		if (error != 0) {
+			if_printf(ifp, "unable to create dmamap for %s "
+				"buffer %u, error %u\n", dd->dd_name, i, error);
+			ath_descdma_cleanup(sc, dd, head);
+			return error;
+		}
+		TAILQ_INSERT_TAIL(head, bf, bf_list);
+	}
+	return 0;
+fail3:
+	memset(dd, 0, sizeof(*dd));
+	return error;
+}
+
+void
 ath_descdma_cleanup(struct ath_softc *sc,
 	struct ath_descdma *dd, ath_bufhead *head)
 {
 	struct ath_buf *bf;
 	struct ieee80211_node *ni;
 
-	bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
-	bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
-	bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
-	bus_dma_tag_destroy(dd->dd_dmat);
+	if (dd->dd_dmamap != 0) {
+		bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
+		bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+		bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
+		bus_dma_tag_destroy(dd->dd_dmat);
+	}
 
 	TAILQ_FOREACH(bf, head, bf_list) {
 		if (bf->bf_m) {
@@ -3690,23 +3009,32 @@
 {
 	int error;
 
-	error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
-			"rx", ath_rxbuf, 1);
-	if (error != 0)
-		return error;
-
 	error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
-			"tx", ath_txbuf, ATH_TXDESC);
+		    "tx", sc->sc_tx_desclen, ath_txbuf, ATH_TXDESC);
 	if (error != 0) {
-		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
 		return error;
 	}
-
-	error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
-			"beacon", ATH_BCBUF, 1);
+	sc->sc_txbuf_cnt = ath_txbuf;
+
+	error = ath_descdma_setup(sc, &sc->sc_txdma_mgmt, &sc->sc_txbuf_mgmt,
+		    "tx_mgmt", sc->sc_tx_desclen, ath_txbuf_mgmt,
+		    ATH_TXDESC);
 	if (error != 0) {
 		ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
-		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+		return error;
+	}
+
+	/*
+	 * XXX mark txbuf_mgmt frames with ATH_BUF_MGMT, so the
+	 * flag doesn't have to be set in ath_getbuf_locked().
+	 */
+
+	error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
+			"beacon", sc->sc_tx_desclen, ATH_BCBUF, 1);
+	if (error != 0) {
+		ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
+		ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt,
+		    &sc->sc_txbuf_mgmt);
 		return error;
 	}
 	return 0;
@@ -3720,8 +3048,9 @@
 		ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf);
 	if (sc->sc_txdma.dd_desc_len != 0)
 		ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
-	if (sc->sc_rxdma.dd_desc_len != 0)
-		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+	if (sc->sc_txdma_mgmt.dd_desc_len != 0)
+		ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt,
+		    &sc->sc_txbuf_mgmt);
 }
 
 static struct ieee80211_node *
@@ -3788,197 +3117,10 @@
 		*noise = -95;		/* nominally correct */
 }
 
-static int
-ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
-{
-	struct ath_hal *ah = sc->sc_ah;
-	int error;
-	struct mbuf *m;
-	struct ath_desc *ds;
-
-	m = bf->bf_m;
-	if (m == NULL) {
-		/*
-		 * NB: by assigning a page to the rx dma buffer we
-		 * implicitly satisfy the Atheros requirement that
-		 * this buffer be cache-line-aligned and sized to be
-		 * multiple of the cache line size.  Not doing this
-		 * causes weird stuff to happen (for the 5210 at least).
-		 */
-		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
-		if (m == NULL) {
-			DPRINTF(sc, ATH_DEBUG_ANY,
-				"%s: no mbuf/cluster\n", __func__);
-			sc->sc_stats.ast_rx_nombuf++;
-			return ENOMEM;
-		}
-		m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
-
-		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat,
-					     bf->bf_dmamap, m,
-					     bf->bf_segs, &bf->bf_nseg,
-					     BUS_DMA_NOWAIT);
-		if (error != 0) {
-			DPRINTF(sc, ATH_DEBUG_ANY,
-			    "%s: bus_dmamap_load_mbuf_sg failed; error %d\n",
-			    __func__, error);
-			sc->sc_stats.ast_rx_busdma++;
-			m_freem(m);
-			return error;
-		}
-		KASSERT(bf->bf_nseg == 1,
-			("multi-segment packet; nseg %u", bf->bf_nseg));
-		bf->bf_m = m;
-	}
-	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD);
-
-	/*
-	 * Setup descriptors.  For receive we always terminate
-	 * the descriptor list with a self-linked entry so we'll
-	 * not get overrun under high load (as can happen with a
-	 * 5212 when ANI processing enables PHY error frames).
-	 *
-	 * To insure the last descriptor is self-linked we create
-	 * each descriptor as self-linked and add it to the end.  As
-	 * each additional descriptor is added the previous self-linked
-	 * entry is ``fixed'' naturally.  This should be safe even
-	 * if DMA is happening.  When processing RX interrupts we
-	 * never remove/process the last, self-linked, entry on the
-	 * descriptor list.  This insures the hardware always has
-	 * someplace to write a new frame.
-	 */
-	/*
-	 * 11N: we can no longer afford to self link the last descriptor.
-	 * MAC acknowledges BA status as long as it copies frames to host
-	 * buffer (or rx fifo). This can incorrectly acknowledge packets
-	 * to a sender if last desc is self-linked.
-	 */
-	ds = bf->bf_desc;
-	if (sc->sc_rxslink)
-		ds->ds_link = bf->bf_daddr;	/* link to self */
-	else
-		ds->ds_link = 0;		/* terminate the list */
-	ds->ds_data = bf->bf_segs[0].ds_addr;
-	ath_hal_setuprxdesc(ah, ds
-		, m->m_len		/* buffer size */
-		, 0
-	);
-
-	if (sc->sc_rxlink != NULL)
-		*sc->sc_rxlink = bf->bf_daddr;
-	sc->sc_rxlink = &ds->ds_link;
-	return 0;
-}
-
-/*
- * Extend 15-bit time stamp from rx descriptor to
- * a full 64-bit TSF using the specified TSF.
- */
-static __inline u_int64_t
-ath_extend_tsf15(u_int32_t rstamp, u_int64_t tsf)
-{
-	if ((tsf & 0x7fff) < rstamp)
-		tsf -= 0x8000;
-
-	return ((tsf &~ 0x7fff) | rstamp);
-}
-
-/*
- * Extend 32-bit time stamp from rx descriptor to
- * a full 64-bit TSF using the specified TSF.
- */
-static __inline u_int64_t
-ath_extend_tsf32(u_int32_t rstamp, u_int64_t tsf)
-{
-	u_int32_t tsf_low = tsf & 0xffffffff;
-	u_int64_t tsf64 = (tsf & ~0xffffffffULL) | rstamp;
-
-	if (rstamp > tsf_low && (rstamp - tsf_low > 0x10000000))
-		tsf64 -= 0x100000000ULL;
-
-	if (rstamp < tsf_low && (tsf_low - rstamp > 0x10000000))
-		tsf64 += 0x100000000ULL;
-
-	return tsf64;
-}
-
-/*
- * Extend the TSF from the RX descriptor to a full 64 bit TSF.
- * Earlier hardware versions only wrote the low 15 bits of the
- * TSF into the RX descriptor; later versions (AR5416 and up)
- * include the 32 bit TSF value.
- */
-static __inline u_int64_t
-ath_extend_tsf(struct ath_softc *sc, u_int32_t rstamp, u_int64_t tsf)
-{
-	if (sc->sc_rxtsf32)
-		return ath_extend_tsf32(rstamp, tsf);
-	else
-		return ath_extend_tsf15(rstamp, tsf);
-}
-
-/*
- * Intercept management frames to collect beacon rssi data
- * and to do ibss merges.
- */
-static void
-ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
-	int subtype, int rssi, int nf)
-{
-	struct ieee80211vap *vap = ni->ni_vap;
-	struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
-
-	/*
-	 * Call up first so subsequent work can use information
-	 * potentially stored in the node (e.g. for ibss merge).
-	 */
-	ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, nf);
-	switch (subtype) {
-	case IEEE80211_FC0_SUBTYPE_BEACON:
-		/* update rssi statistics for use by the hal */
-		/* XXX unlocked check against vap->iv_bss? */
-		ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi);
-		if (sc->sc_syncbeacon &&
-		    ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) {
-			/*
-			 * Resync beacon timers using the tsf of the beacon
-			 * frame we just received.
-			 */
-			ath_beacon_config(sc, vap);
-		}
-		/* fall thru... */
-	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
-		if (vap->iv_opmode == IEEE80211_M_IBSS &&
-		    vap->iv_state == IEEE80211_S_RUN) {
-			uint32_t rstamp = sc->sc_lastrs->rs_tstamp;
-			uint64_t tsf = ath_extend_tsf(sc, rstamp,
-				ath_hal_gettsf64(sc->sc_ah));
-			/*
-			 * Handle ibss merge as needed; check the tsf on the
-			 * frame before attempting the merge.  The 802.11 spec
-			 * says the station should change it's bssid to match
-			 * the oldest station with the same ssid, where oldest
-			 * is determined by the tsf.  Note that hardware
-			 * reconfiguration happens through callback to
-			 * ath_newstate as the state machine will go from
-			 * RUN -> RUN when this happens.
-			 */
-			if (le64toh(ni->ni_tstamp.tsf) >= tsf) {
-				DPRINTF(sc, ATH_DEBUG_STATE,
-				    "ibss merge, rstamp %u tsf %ju "
-				    "tstamp %ju\n", rstamp, (uintmax_t)tsf,
-				    (uintmax_t)ni->ni_tstamp.tsf);
-				(void) ieee80211_ibss_merge(ni);
-			}
-		}
-		break;
-	}
-}
-
 /*
  * Set the default antenna.
  */
-static void
+void
 ath_setdefantenna(struct ath_softc *sc, u_int antenna)
 {
 	struct ath_hal *ah = sc->sc_ah;
@@ -3992,538 +3134,6 @@
 }
 
 static void
-ath_rx_tap(struct ifnet *ifp, struct mbuf *m,
-	const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf)
-{
-#define	CHAN_HT20	htole32(IEEE80211_CHAN_HT20)
-#define	CHAN_HT40U	htole32(IEEE80211_CHAN_HT40U)
-#define	CHAN_HT40D	htole32(IEEE80211_CHAN_HT40D)
-#define	CHAN_HT		(CHAN_HT20|CHAN_HT40U|CHAN_HT40D)
-	struct ath_softc *sc = ifp->if_softc;
-	const HAL_RATE_TABLE *rt;
-	uint8_t rix;
-
-	rt = sc->sc_currates;
-	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
-	rix = rt->rateCodeToIndex[rs->rs_rate];
-	sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate;
-	sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags;
-#ifdef AH_SUPPORT_AR5416
-	sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT;
-	if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) {	/* HT rate */
-		struct ieee80211com *ic = ifp->if_l2com;
-
-		if ((rs->rs_flags & HAL_RX_2040) == 0)
-			sc->sc_rx_th.wr_chan_flags |= CHAN_HT20;
-		else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
-			sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U;
-		else
-			sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D;
-		if ((rs->rs_flags & HAL_RX_GI) == 0)
-			sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI;
-	}
-#endif
-	sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf));
-	if (rs->rs_status & HAL_RXERR_CRC)
-		sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
-	/* XXX propagate other error flags from descriptor */
-	sc->sc_rx_th.wr_antnoise = nf;
-	sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi;
-	sc->sc_rx_th.wr_antenna = rs->rs_antenna;
-#undef CHAN_HT
-#undef CHAN_HT20
-#undef CHAN_HT40U
-#undef CHAN_HT40D
-}
-
-static void
-ath_handle_micerror(struct ieee80211com *ic,
-	struct ieee80211_frame *wh, int keyix)
-{
-	struct ieee80211_node *ni;
-
-	/* XXX recheck MIC to deal w/ chips that lie */
-	/* XXX discard MIC errors on !data frames */
-	ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh);
-	if (ni != NULL) {
-		ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix);
-		ieee80211_free_node(ni);
-	}
-}
-
-/*
- * Only run the RX proc if it's not already running.
- * Since this may get run as part of the reset/flush path,
- * the task can't clash with an existing, running tasklet.
- */
-static void
-ath_rx_tasklet(void *arg, int npending)
-{
-	struct ath_softc *sc = arg;
-
-	CTR1(ATH_KTR_INTR, "ath_rx_proc: pending=%d", npending);
-	DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
-	ATH_PCU_LOCK(sc);
-	if (sc->sc_inreset_cnt > 0) {
-		device_printf(sc->sc_dev,
-		    "%s: sc_inreset_cnt > 0; skipping\n", __func__);
-		ATH_PCU_UNLOCK(sc);
-		return;
-	}
-	ATH_PCU_UNLOCK(sc);
-	ath_rx_proc(sc, 1);
-}
-
-static void
-ath_rx_proc(struct ath_softc *sc, int resched)
-{
-#define	PA2DESC(_sc, _pa) \
-	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
-		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
-	struct ath_buf *bf;
-	struct ifnet *ifp = sc->sc_ifp;
-	struct ieee80211com *ic = ifp->if_l2com;
-	struct ath_hal *ah = sc->sc_ah;
-	struct ath_desc *ds;
-	struct ath_rx_status *rs;
-	struct mbuf *m;
-	struct ieee80211_node *ni;
-	int len, type, ngood;
-	HAL_STATUS status;
-	int16_t nf;
-	u_int64_t tsf, rstamp;
-	int npkts = 0;
-
-	/* XXX we must not hold the ATH_LOCK here */
-	ATH_UNLOCK_ASSERT(sc);
-	ATH_PCU_UNLOCK_ASSERT(sc);
-
-	ATH_PCU_LOCK(sc);
-	sc->sc_rxproc_cnt++;
-	ATH_PCU_UNLOCK(sc);
-
-	DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__);
-	ngood = 0;
-	nf = ath_hal_getchannoise(ah, sc->sc_curchan);
-	sc->sc_stats.ast_rx_noise = nf;
-	tsf = ath_hal_gettsf64(ah);
-	do {
-		bf = TAILQ_FIRST(&sc->sc_rxbuf);
-		if (sc->sc_rxslink && bf == NULL) {	/* NB: shouldn't happen */
-			if_printf(ifp, "%s: no buffer!\n", __func__);
-			break;
-		} else if (bf == NULL) {
-			/*
-			 * End of List:
-			 * this can happen for non-self-linked RX chains
-			 */
-			sc->sc_stats.ast_rx_hitqueueend++;
-			break;
-		}
-		m = bf->bf_m;
-		if (m == NULL) {		/* NB: shouldn't happen */
-			/*
-			 * If mbuf allocation failed previously there
-			 * will be no mbuf; try again to re-populate it.
-			 */
-			/* XXX make debug msg */
-			if_printf(ifp, "%s: no mbuf!\n", __func__);
-			TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list);
-			goto rx_next;
-		}
-		ds = bf->bf_desc;
-		if (ds->ds_link == bf->bf_daddr) {
-			/* NB: never process the self-linked entry at the end */
-			sc->sc_stats.ast_rx_hitqueueend++;
-			break;
-		}
-		/* XXX sync descriptor memory */
-		/*
-		 * Must provide the virtual address of the current
-		 * descriptor, the physical address, and the virtual
-		 * address of the next descriptor in the h/w chain.
-		 * This allows the HAL to look ahead to see if the
-		 * hardware is done with a descriptor by checking the
-		 * done bit in the following descriptor and the address
-		 * of the current descriptor the DMA engine is working
-		 * on.  All this is necessary because of our use of
-		 * a self-linked list to avoid rx overruns.
-		 */
-		rs = &bf->bf_status.ds_rxstat;
-		status = ath_hal_rxprocdesc(ah, ds,
-				bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs);
-#ifdef ATH_DEBUG
-		if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
-			ath_printrxbuf(sc, bf, 0, status == HAL_OK);
-#endif
-		if (status == HAL_EINPROGRESS)
-			break;
-
-		TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list);
-		npkts++;
-
-		/*
-		 * Calculate the correct 64 bit TSF given
-		 * the TSF64 register value and rs_tstamp.
-		 */
-		rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf);
-
-		/* These aren't specifically errors */
-#ifdef	AH_SUPPORT_AR5416
-		if (rs->rs_flags & HAL_RX_GI)
-			sc->sc_stats.ast_rx_halfgi++;
-		if (rs->rs_flags & HAL_RX_2040)
-			sc->sc_stats.ast_rx_2040++;
-		if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE)
-			sc->sc_stats.ast_rx_pre_crc_err++;
-		if (rs->rs_flags & HAL_RX_DELIM_CRC_POST)
-			sc->sc_stats.ast_rx_post_crc_err++;
-		if (rs->rs_flags & HAL_RX_DECRYPT_BUSY)
-			sc->sc_stats.ast_rx_decrypt_busy_err++;
-		if (rs->rs_flags & HAL_RX_HI_RX_CHAIN)
-			sc->sc_stats.ast_rx_hi_rx_chain++;
-#endif /* AH_SUPPORT_AR5416 */
-
-		if (rs->rs_status != 0) {
-			if (rs->rs_status & HAL_RXERR_CRC)
-				sc->sc_stats.ast_rx_crcerr++;
-			if (rs->rs_status & HAL_RXERR_FIFO)
-				sc->sc_stats.ast_rx_fifoerr++;
-			if (rs->rs_status & HAL_RXERR_PHY) {
-				sc->sc_stats.ast_rx_phyerr++;
-				/* Process DFS radar events */
-				if ((rs->rs_phyerr == HAL_PHYERR_RADAR) ||
-				    (rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) {
-					/* Since we're touching the frame data, sync it */
-					bus_dmamap_sync(sc->sc_dmat,
-					    bf->bf_dmamap,
-					    BUS_DMASYNC_POSTREAD);
-					/* Now pass it to the radar processing code */
-					ath_dfs_process_phy_err(sc, mtod(m, char *), rstamp, rs);
-				}
-
-				/* Be suitably paranoid about receiving phy errors out of the stats array bounds */
-				if (rs->rs_phyerr < 64)
-					sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++;
-				goto rx_error;	/* NB: don't count in ierrors */
-			}
-			if (rs->rs_status & HAL_RXERR_DECRYPT) {
-				/*
-				 * Decrypt error.  If the error occurred
-				 * because there was no hardware key, then
-				 * let the frame through so the upper layers
-				 * can process it.  This is necessary for 5210
-				 * parts which have no way to setup a ``clear''
-				 * key cache entry.
-				 *
-				 * XXX do key cache faulting
-				 */
-				if (rs->rs_keyix == HAL_RXKEYIX_INVALID)
-					goto rx_accept;
-				sc->sc_stats.ast_rx_badcrypt++;
-			}
-			if (rs->rs_status & HAL_RXERR_MIC) {
-				sc->sc_stats.ast_rx_badmic++;
-				/*
-				 * Do minimal work required to hand off
-				 * the 802.11 header for notification.
-				 */
-				/* XXX frag's and qos frames */
-				len = rs->rs_datalen;
-				if (len >= sizeof (struct ieee80211_frame)) {
-					bus_dmamap_sync(sc->sc_dmat,
-					    bf->bf_dmamap,
-					    BUS_DMASYNC_POSTREAD);
-					ath_handle_micerror(ic,
-					    mtod(m, struct ieee80211_frame *),
-					    sc->sc_splitmic ?
-						rs->rs_keyix-32 : rs->rs_keyix);
-				}
-			}
-			ifp->if_ierrors++;
-rx_error:
-			/*
-			 * Cleanup any pending partial frame.
-			 */
-			if (sc->sc_rxpending != NULL) {
-				m_freem(sc->sc_rxpending);
-				sc->sc_rxpending = NULL;
-			}
-			/*
-			 * When a tap is present pass error frames
-			 * that have been requested.  By default we
-			 * pass decrypt+mic errors but others may be
-			 * interesting (e.g. crc).
-			 */
-			if (ieee80211_radiotap_active(ic) &&
-			    (rs->rs_status & sc->sc_monpass)) {
-				bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
-				    BUS_DMASYNC_POSTREAD);
-				/* NB: bpf needs the mbuf length setup */
-				len = rs->rs_datalen;
-				m->m_pkthdr.len = m->m_len = len;
-				bf->bf_m = NULL;
-				ath_rx_tap(ifp, m, rs, rstamp, nf);
-				ieee80211_radiotap_rx_all(ic, m);
-				m_freem(m);
-			}
-			/* XXX pass MIC errors up for s/w reclaculation */
-			goto rx_next;
-		}
-rx_accept:
-		/*
-		 * Sync and unmap the frame.  At this point we're
-		 * committed to passing the mbuf somewhere so clear
-		 * bf_m; this means a new mbuf must be allocated
-		 * when the rx descriptor is setup again to receive
-		 * another frame.
-		 */
-		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
-		    BUS_DMASYNC_POSTREAD);
-		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-		bf->bf_m = NULL;
-
-		len = rs->rs_datalen;
-		m->m_len = len;
-
-		if (rs->rs_more) {
-			/*
-			 * Frame spans multiple descriptors; save
-			 * it for the next completed descriptor, it
-			 * will be used to construct a jumbogram.
-			 */
-			if (sc->sc_rxpending != NULL) {
-				/* NB: max frame size is currently 2 clusters */
-				sc->sc_stats.ast_rx_toobig++;
-				m_freem(sc->sc_rxpending);
-			}
-			m->m_pkthdr.rcvif = ifp;
-			m->m_pkthdr.len = len;
-			sc->sc_rxpending = m;
-			goto rx_next;
-		} else if (sc->sc_rxpending != NULL) {
-			/*
-			 * This is the second part of a jumbogram,
-			 * chain it to the first mbuf, adjust the
-			 * frame length, and clear the rxpending state.
-			 */
-			sc->sc_rxpending->m_next = m;
-			sc->sc_rxpending->m_pkthdr.len += len;
-			m = sc->sc_rxpending;
-			sc->sc_rxpending = NULL;
-		} else {
-			/*
-			 * Normal single-descriptor receive; setup
-			 * the rcvif and packet length.
-			 */
-			m->m_pkthdr.rcvif = ifp;
-			m->m_pkthdr.len = len;
-		}
-
-		/*
-		 * Validate rs->rs_antenna.
-		 *
-		 * Some users w/ AR9285 NICs have reported crashes
-		 * here because rs_antenna field is bogusly large.
-		 * Let's enforce the maximum antenna limit of 8
-		 * (and it shouldn't be hard coded, but that's a
-		 * separate problem) and if there's an issue, print
-		 * out an error and adjust rs_antenna to something
-		 * sensible.
-		 *
-		 * This code should be removed once the actual
-		 * root cause of the issue has been identified.
-		 * For example, it may be that the rs_antenna
-		 * field is only valid for the lsat frame of
-		 * an aggregate and it just happens that it is
-		 * "mostly" right. (This is a general statement -
-		 * the majority of the statistics are only valid
-		 * for the last frame in an aggregate.
-		 */
-		if (rs->rs_antenna > 7) {
-			device_printf(sc->sc_dev, "%s: rs_antenna > 7 (%d)\n",
-			    __func__, rs->rs_antenna);
-#ifdef	ATH_DEBUG
-			ath_printrxbuf(sc, bf, 0, status == HAL_OK);
-#endif /* ATH_DEBUG */
-			rs->rs_antenna = 0;	/* XXX better than nothing */
-		}
-
-		ifp->if_ipackets++;
-		sc->sc_stats.ast_ant_rx[rs->rs_antenna]++;
-
-		/*
-		 * Populate the rx status block.  When there are bpf
-		 * listeners we do the additional work to provide
-		 * complete status.  Otherwise we fill in only the
-		 * material required by ieee80211_input.  Note that
-		 * noise setting is filled in above.
-		 */
-		if (ieee80211_radiotap_active(ic))
-			ath_rx_tap(ifp, m, rs, rstamp, nf);
-
-		/*
-		 * From this point on we assume the frame is at least
-		 * as large as ieee80211_frame_min; verify that.
-		 */
-		if (len < IEEE80211_MIN_LEN) {
-			if (!ieee80211_radiotap_active(ic)) {
-				DPRINTF(sc, ATH_DEBUG_RECV,
-				    "%s: short packet %d\n", __func__, len);
-				sc->sc_stats.ast_rx_tooshort++;
-			} else {
-				/* NB: in particular this captures ack's */
-				ieee80211_radiotap_rx_all(ic, m);
-			}
-			m_freem(m);
-			goto rx_next;
-		}
-
-		if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
-			const HAL_RATE_TABLE *rt = sc->sc_currates;
-			uint8_t rix = rt->rateCodeToIndex[rs->rs_rate];
-
-			ieee80211_dump_pkt(ic, mtod(m, caddr_t), len,
-			    sc->sc_hwmap[rix].ieeerate, rs->rs_rssi);
-		}
-
-		m_adj(m, -IEEE80211_CRC_LEN);
-
-		/*
-		 * Locate the node for sender, track state, and then
-		 * pass the (referenced) node up to the 802.11 layer
-		 * for its use.
-		 */
-		ni = ieee80211_find_rxnode_withkey(ic,
-			mtod(m, const struct ieee80211_frame_min *),
-			rs->rs_keyix == HAL_RXKEYIX_INVALID ?
-				IEEE80211_KEYIX_NONE : rs->rs_keyix);
-		sc->sc_lastrs = rs;
-
-#ifdef	AH_SUPPORT_AR5416
-		if (rs->rs_isaggr)
-			sc->sc_stats.ast_rx_agg++;
-#endif /* AH_SUPPORT_AR5416 */
-
-		if (ni != NULL) {
-			/*
- 			 * Only punt packets for ampdu reorder processing for
-			 * 11n nodes; net80211 enforces that M_AMPDU is only
-			 * set for 11n nodes.
- 			 */
-			if (ni->ni_flags & IEEE80211_NODE_HT)
-				m->m_flags |= M_AMPDU;
-
-			/*
-			 * Sending station is known, dispatch directly.
-			 */
-			type = ieee80211_input(ni, m, rs->rs_rssi, nf);
-			ieee80211_free_node(ni);
-			/*
-			 * Arrange to update the last rx timestamp only for
-			 * frames from our ap when operating in station mode.
-			 * This assumes the rx key is always setup when
-			 * associated.
-			 */
-			if (ic->ic_opmode == IEEE80211_M_STA &&
-			    rs->rs_keyix != HAL_RXKEYIX_INVALID)
-				ngood++;
-		} else {
-			type = ieee80211_input_all(ic, m, rs->rs_rssi, nf);
-		}
-		/*
-		 * Track rx rssi and do any rx antenna management.
-		 */
-		ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi);
-		if (sc->sc_diversity) {
-			/*
-			 * When using fast diversity, change the default rx
-			 * antenna if diversity chooses the other antenna 3
-			 * times in a row.
-			 */
-			if (sc->sc_defant != rs->rs_antenna) {
-				if (++sc->sc_rxotherant >= 3)
-					ath_setdefantenna(sc, rs->rs_antenna);
-			} else
-				sc->sc_rxotherant = 0;
-		}
-
-		/* Newer school diversity - kite specific for now */
-		/* XXX perhaps migrate the normal diversity code to this? */
-		if ((ah)->ah_rxAntCombDiversity)
-			(*(ah)->ah_rxAntCombDiversity)(ah, rs, ticks, hz);
-
-		if (sc->sc_softled) {
-			/*
-			 * Blink for any data frame.  Otherwise do a
-			 * heartbeat-style blink when idle.  The latter
-			 * is mainly for station mode where we depend on
-			 * periodic beacon frames to trigger the poll event.
-			 */
-			if (type == IEEE80211_FC0_TYPE_DATA) {
-				const HAL_RATE_TABLE *rt = sc->sc_currates;
-				ath_led_event(sc,
-				    rt->rateCodeToIndex[rs->rs_rate]);
-			} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
-				ath_led_event(sc, 0);
-		}
-rx_next:
-		TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
-	} while (ath_rxbuf_init(sc, bf) == 0);
-
-	/* rx signal state monitoring */
-	ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan);
-	if (ngood)
-		sc->sc_lastrx = tsf;
-
-	CTR2(ATH_KTR_INTR, "ath_rx_proc: npkts=%d, ngood=%d", npkts, ngood);
-	/* Queue DFS tasklet if needed */
-	if (resched && ath_dfs_tasklet_needed(sc, sc->sc_curchan))
-		taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask);
-
-	/*
-	 * Now that all the RX frames were handled that
-	 * need to be handled, kick the PCU if there's
-	 * been an RXEOL condition.
-	 */
-	ATH_PCU_LOCK(sc);
-	if (resched && sc->sc_kickpcu) {
-		CTR0(ATH_KTR_ERR, "ath_rx_proc: kickpcu");
-		device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n",
-		    __func__, npkts);
-
-		/* XXX rxslink? */
-		/*
-		 * XXX can we hold the PCU lock here?
-		 * Are there any net80211 buffer calls involved?
-		 */
-		bf = TAILQ_FIRST(&sc->sc_rxbuf);
-		ath_hal_putrxbuf(ah, bf->bf_daddr);
-		ath_hal_rxena(ah);		/* enable recv descriptors */
-		ath_mode_init(sc);		/* set filters, etc. */
-		ath_hal_startpcurecv(ah);	/* re-enable PCU/DMA engine */
-
-		ath_hal_intrset(ah, sc->sc_imask);
-		sc->sc_kickpcu = 0;
-	}
-	ATH_PCU_UNLOCK(sc);
-
-	/* XXX check this inside of IF_LOCK? */
-	if (resched && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
-#ifdef IEEE80211_SUPPORT_SUPERG
-		ieee80211_ff_age_all(ic, 100);
-#endif
-		if (!IFQ_IS_EMPTY(&ifp->if_snd))
-			ath_start(ifp);
-	}
-#undef PA2DESC
-
-	ATH_PCU_LOCK(sc);
-	sc->sc_rxproc_cnt--;
-	ATH_PCU_UNLOCK(sc);
-}
-
-static void
 ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum)
 {
 	txq->axq_qnum = qnum;
@@ -4700,7 +3310,7 @@
 /*
  * Callback from the 802.11 layer to update WME parameters.
  */
-static int
+int
 ath_wme_update(struct ieee80211com *ic)
 {
 	struct ath_softc *sc = ic->ic_ifp->if_softc;
@@ -4887,12 +3497,14 @@
 	 * descriptor.
 	 */
 	ATH_TXBUF_LOCK_ASSERT(sc);
+	last = TAILQ_LAST(&sc->sc_txbuf_mgmt, ath_bufhead_s);
+	if (last != NULL)
+		last->bf_flags &= ~ATH_BUF_BUSY;
 	last = TAILQ_LAST(&sc->sc_txbuf, ath_bufhead_s);
 	if (last != NULL)
 		last->bf_flags &= ~ATH_BUF_BUSY;
 }
 
-
 /*
  * Process completed xmit descriptors from the specified queue.
  * Kick the packet scheduler if needed. This can occur from this
@@ -5063,7 +3675,7 @@
 	sc->sc_txproc_cnt--;
 	ATH_PCU_UNLOCK(sc);
 
-	ath_start(ifp);
+	ath_tx_kick(sc);
 }
 
 /*
@@ -5113,7 +3725,7 @@
 	sc->sc_txproc_cnt--;
 	ATH_PCU_UNLOCK(sc);
 
-	ath_start(ifp);
+	ath_tx_kick(sc);
 }
 
 /*
@@ -5156,7 +3768,7 @@
 	sc->sc_txproc_cnt--;
 	ATH_PCU_UNLOCK(sc);
 
-	ath_start(ifp);
+	ath_tx_kick(sc);
 }
 #undef	TXQACTIVE
 
@@ -5195,6 +3807,48 @@
 	ATH_PCU_UNLOCK(sc);
 }
 
+void
+ath_returnbuf_tail(struct ath_softc *sc, struct ath_buf *bf)
+{
+
+	ATH_TXBUF_LOCK_ASSERT(sc);
+
+	if (bf->bf_flags & ATH_BUF_MGMT)
+		TAILQ_INSERT_TAIL(&sc->sc_txbuf_mgmt, bf, bf_list);
+	else {
+		TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		sc->sc_txbuf_cnt++;
+		if (sc->sc_txbuf_cnt > ath_txbuf) {
+			device_printf(sc->sc_dev,
+			    "%s: sc_txbuf_cnt > %d?\n",
+			    __func__,
+			    ath_txbuf);
+			sc->sc_txbuf_cnt = ath_txbuf;
+		}
+	}
+}
+
+void
+ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf)
+{
+
+	ATH_TXBUF_LOCK_ASSERT(sc);
+
+	if (bf->bf_flags & ATH_BUF_MGMT)
+		TAILQ_INSERT_HEAD(&sc->sc_txbuf_mgmt, bf, bf_list);
+	else {
+		TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+		sc->sc_txbuf_cnt++;
+		if (sc->sc_txbuf_cnt > ATH_TXBUF) {
+			device_printf(sc->sc_dev,
+			    "%s: sc_txbuf_cnt > %d?\n",
+			    __func__,
+			    ATH_TXBUF);
+			sc->sc_txbuf_cnt = ATH_TXBUF;
+		}
+	}
+}
+
 /*
  * Return a buffer to the pool and update the 'busy' flag on the
  * previous 'tail' entry.
@@ -5217,7 +3871,7 @@
 
 	ATH_TXBUF_LOCK(sc);
 	ath_tx_update_busy(sc);
-	TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+	ath_returnbuf_tail(sc, bf);
 	ATH_TXBUF_UNLOCK(sc);
 }
 
@@ -5273,6 +3927,9 @@
 	bf = TAILQ_LAST(&sc->sc_txbuf, ath_bufhead_s);
 	if (bf != NULL)
 		bf->bf_flags &= ~ATH_BUF_BUSY;
+	bf = TAILQ_LAST(&sc->sc_txbuf_mgmt, ath_bufhead_s);
+	if (bf != NULL)
+		bf->bf_flags &= ~ATH_BUF_BUSY;
 	ATH_TXBUF_UNLOCK(sc);
 
 	for (ix = 0;; ix++) {
@@ -5404,81 +4061,6 @@
 }
 
 /*
- * Disable the receive h/w in preparation for a reset.
- */
-static void
-ath_stoprecv(struct ath_softc *sc, int dodelay)
-{
-#define	PA2DESC(_sc, _pa) \
-	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
-		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
-	struct ath_hal *ah = sc->sc_ah;
-
-	ath_hal_stoppcurecv(ah);	/* disable PCU */
-	ath_hal_setrxfilter(ah, 0);	/* clear recv filter */
-	ath_hal_stopdmarecv(ah);	/* disable DMA engine */
-	if (dodelay)
-		DELAY(3000);		/* 3ms is long enough for 1 frame */
-#ifdef ATH_DEBUG
-	if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) {
-		struct ath_buf *bf;
-		u_int ix;
-
-		device_printf(sc->sc_dev,
-		    "%s: rx queue %p, link %p\n",
-		    __func__,
-		    (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah),
-		    sc->sc_rxlink);
-		ix = 0;
-		TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
-			struct ath_desc *ds = bf->bf_desc;
-			struct ath_rx_status *rs = &bf->bf_status.ds_rxstat;
-			HAL_STATUS status = ath_hal_rxprocdesc(ah, ds,
-				bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs);
-			if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL))
-				ath_printrxbuf(sc, bf, ix, status == HAL_OK);
-			ix++;
-		}
-	}
-#endif
-	if (sc->sc_rxpending != NULL) {
-		m_freem(sc->sc_rxpending);
-		sc->sc_rxpending = NULL;
-	}
-	sc->sc_rxlink = NULL;		/* just in case */
-#undef PA2DESC
-}
-
-/*
- * Enable the receive h/w following a reset.
- */
-static int
-ath_startrecv(struct ath_softc *sc)
-{
-	struct ath_hal *ah = sc->sc_ah;
-	struct ath_buf *bf;
-
-	sc->sc_rxlink = NULL;
-	sc->sc_rxpending = NULL;
-	TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
-		int error = ath_rxbuf_init(sc, bf);
-		if (error != 0) {
-			DPRINTF(sc, ATH_DEBUG_RECV,
-				"%s: ath_rxbuf_init failed %d\n",
-				__func__, error);
-			return error;
-		}
-	}
-
-	bf = TAILQ_FIRST(&sc->sc_rxbuf);
-	ath_hal_putrxbuf(ah, bf->bf_daddr);
-	ath_hal_rxena(ah);		/* enable recv descriptors */
-	ath_mode_init(sc);		/* set filters, etc. */
-	ath_hal_startpcurecv(ah);	/* re-enable PCU/DMA engine */
-	return 0;
-}
-
-/*
  * Update internal state after a channel change.
  */
 static void
@@ -5544,7 +4126,7 @@
 		/*
 		 * First, handle completed TX/RX frames.
 		 */
-		ath_rx_proc(sc, 0);
+		ath_rx_flush(sc);
 		ath_draintxq(sc, ATH_RESET_NOLOSS);
 		/*
 		 * Next, flush the non-scheduled frames.
@@ -6360,6 +4942,43 @@
 	callout_schedule(&sc->sc_wd_ch, hz);
 }
 
+/*
+ * Fetch the rate control statistics for the given node.
+ */
+static int
+ath_ioctl_ratestats(struct ath_softc *sc, struct ath_rateioctl *rs)
+{
+	struct ath_node *an;
+	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+	struct ieee80211_node *ni;
+	int error = 0;
+
+	/* Perform a lookup on the given node */
+	ni = ieee80211_find_node(&ic->ic_sta, rs->is_u.macaddr);
+	if (ni == NULL) {
+		error = EINVAL;
+		goto bad;
+	}
+
+	/* Lock the ath_node */
+	an = ATH_NODE(ni);
+	ATH_NODE_LOCK(an);
+
+	/* Fetch the rate control stats for this node */
+	error = ath_rate_fetch_node_stats(sc, an, rs);
+
+	/* No matter what happens here, just drop through */
+
+	/* Unlock the ath_node */
+	ATH_NODE_UNLOCK(an);
+
+	/* Unref the node */
+	ieee80211_node_decref(ni);
+
+bad:
+	return (error);
+}
+
 #ifdef ATH_DIAGAPI
 /*
  * Diagnostic interface to the HAL.  This is used by various
@@ -6487,6 +5106,9 @@
 			sc->sc_stats.ast_tx_rate |= IEEE80211_RATE_MCS;
 		return copyout(&sc->sc_stats,
 		    ifr->ifr_data, sizeof (sc->sc_stats));
+	case SIOCGATHAGSTATS:
+		return copyout(&sc->sc_aggr_stats,
+		    ifr->ifr_data, sizeof (sc->sc_aggr_stats));
 	case SIOCZATHSTATS:
 		error = priv_check(curthread, PRIV_DRIVER);
 		if (error == 0) {
@@ -6505,6 +5127,9 @@
 		error = ath_ioctl_phyerr(sc,(struct ath_diag*) ifr);
 		break;
 #endif
+	case SIOCGATHNODERATESTATS:
+		error = ath_ioctl_ratestats(sc, (struct ath_rateioctl *) ifr);
+		break;
 	case SIOCGIFADDR:
 		error = ether_ioctl(ifp, cmd, data);
 		break;
@@ -6549,357 +5174,6 @@
 		if_printf(ifp, "using multicast key search\n");
 }
 
-#ifdef IEEE80211_SUPPORT_TDMA
-static void
-ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, u_int32_t bintval)
-{
-	struct ath_hal *ah = sc->sc_ah;
-	HAL_BEACON_TIMERS bt;
-
-	bt.bt_intval = bintval | HAL_BEACON_ENA;
-	bt.bt_nexttbtt = nexttbtt;
-	bt.bt_nextdba = (nexttbtt<<3) - sc->sc_tdmadbaprep;
-	bt.bt_nextswba = (nexttbtt<<3) - sc->sc_tdmaswbaprep;
-	bt.bt_nextatim = nexttbtt+1;
-	/* Enables TBTT, DBA, SWBA timers by default */
-	bt.bt_flags = 0;
-	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_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;
-		}
-	}
-	/* XXX should take a locked ref to iv_bss */
-	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, tp->ucastrate);
-	else
-		rix = ath_tx_findrix(sc, 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 */
-	if (sc->sc_setcca)
-		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, int changed)
-{
-#define	TSF_TO_TU(_h,_l) \
-	((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
-#define	TU_TO_TSF(_tu)	(((u_int64_t)(_tu)) << 10)
-	struct ieee80211vap *vap = ni->ni_vap;
-	struct ieee80211com *ic = ni->ni_ic;
-	struct ath_softc *sc = ic->ic_ifp->if_softc;
-	struct ath_hal *ah = sc->sc_ah;
-	const HAL_RATE_TABLE *rt = sc->sc_currates;
-	u_int64_t tsf, rstamp, nextslot, nexttbtt;
-	u_int32_t txtime, nextslottu;
-	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 (changed != 0) {
-		const struct ieee80211_tdma_state *ts = vap->iv_tdma;
-
-		ath_tdma_bintvalsetup(sc, ts);
-		if (changed & TDMA_UPDATE_SLOTLEN)
-			ath_wme_update(ic);
-
-		DPRINTF(sc, ATH_DEBUG_TDMA,
-		    "%s: adopt slot %u slotcnt %u slotlen %u us "
-		    "bintval %u TU\n", __func__,
-		    ts->tdma_slot, ts->tdma_slotcnt, ts->tdma_slotlen,
-		    sc->sc_tdmabintval);
-
-		/* XXX right? */
-		ath_hal_intrset(ah, sc->sc_imask);
-		/* NB: beacon timers programmed below */
-	}
-
-	/* extend rx timestamp to 64 bits */
-	rs = sc->sc_lastrs;
-	tsf = ath_hal_gettsf64(ah);
-	rstamp = ath_extend_tsf(sc, rs->rs_tstamp, 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.
-	 */
-	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;
-
-	/*
-	 * Retrieve the hardware NextTBTT in 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.
-	 */
-	nexttbtt = ath_hal_getnexttbtt(ah);
-	tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD + 1)) - nexttbtt);
-
-	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 - TSF_TO_TU(nexttbtt >> 32, nexttbtt);
-
-	/*
-	 * 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 nexttbtt %llu (%d)\n",
-	    (unsigned long long) tsf, (unsigned long long) nextslot,
-	    (int)(nextslot - tsf), tsfdelta, nextslottu, nexttbtt, 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 >= ath_bstuck_threshold)
-			taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask);
-		return;
-	}
-	if (sc->sc_bmisscount != 0) {
-		DPRINTF(sc, ATH_DEBUG_BEACON,
-			"%s: resume beacon xmit after %u misses\n",
-			__func__, sc->sc_bmisscount);
-		sc->sc_bmisscount = 0;
-	}
-
-	/*
-	 * 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.
-		 */
-		/* XXX should take a locked ref to iv_bss */
-		vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah);
-	}
-}
-#endif /* IEEE80211_SUPPORT_TDMA */
-
 static void
 ath_dfs_tasklet(void *p, int npending)
 {
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_ahb.c
--- a/head/sys/dev/ath/if_ath_ahb.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_ahb.c	Wed Jul 25 17:07:47 2012 +0300
@@ -29,12 +29,14 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_ahb.c 227328 2011-11-08 02:12:11Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_ahb.c 238709 2012-07-23 02:49:25Z adrian $");
 
 /*
  * AHB bus front-end for the Atheros Wireless LAN controller driver.
  */
 
+#include "opt_ath.h"
+
 #include <sys/param.h>
 #include <sys/systm.h> 
 #include <sys/module.h>
@@ -192,11 +194,15 @@
 
 	ATH_LOCK_INIT(sc);
 	ATH_PCU_LOCK_INIT(sc);
+	ATH_RX_LOCK_INIT(sc);
+	ATH_TXSTATUS_LOCK_INIT(sc);
 
 	error = ath_attach(AR9130_DEVID, sc);
 	if (error == 0)					/* success */
 		return 0;
 
+	ATH_TXSTATUS_LOCK_DESTROY(sc);
+	ATH_RX_LOCK_DESTROY(sc);
 	ATH_PCU_LOCK_DESTROY(sc);
 	ATH_LOCK_DESTROY(sc);
 	bus_dma_tag_destroy(sc->sc_dmat);
@@ -235,6 +241,8 @@
 	if (sc->sc_eepromdata)
 		free(sc->sc_eepromdata, M_TEMP);
 
+	ATH_TXSTATUS_LOCK_DESTROY(sc);
+	ATH_RX_LOCK_DESTROY(sc);
 	ATH_PCU_LOCK_DESTROY(sc);
 	ATH_LOCK_DESTROY(sc);
 
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_beacon.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_beacon.c	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,844 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_beacon.c 238609 2012-07-19 03:51:16Z adrian $");
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+
+#include "opt_inet.h"
+#include "opt_ath.h"
+/*
+ * This is needed for register operations which are performed
+ * by the driver - eg, calls to ath_hal_gettsf32().
+ *
+ * It's also required for any AH_DEBUG checks in here, eg the
+ * module dependencies.
+ */
+#include "opt_ah.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/callout.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/priv.h>
+#include <sys/module.h>
+#include <sys/ktr.h>
+#include <sys/smp.h>	/* for mp_ncpus */
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/ath/if_athvar.h>
+
+#include <dev/ath/if_ath_debug.h>
+#include <dev/ath/if_ath_misc.h>
+#include <dev/ath/if_ath_tx.h>
+#include <dev/ath/if_ath_beacon.h>
+
+#ifdef ATH_TX99_DIAG
+#include <dev/ath/ath_tx99/ath_tx99.h>
+#endif
+
+/*
+ * Setup a h/w transmit queue for beacons.
+ */
+int
+ath_beaconq_setup(struct ath_hal *ah)
+{
+	HAL_TXQ_INFO qi;
+
+	memset(&qi, 0, sizeof(qi));
+	qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
+	qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
+	qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
+	/* NB: for dynamic turbo, don't enable any other interrupts */
+	qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE;
+	return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi);
+}
+
+/*
+ * Setup the transmit queue parameters for the beacon queue.
+ */
+int
+ath_beaconq_config(struct ath_softc *sc)
+{
+#define	ATH_EXPONENT_TO_VALUE(v)	((1<<(v))-1)
+	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_TXQ_INFO qi;
+
+	ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi);
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+	    ic->ic_opmode == IEEE80211_M_MBSS) {
+		/*
+		 * Always burst out beacon and CAB traffic.
+		 */
+		qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT;
+		qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT;
+		qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT;
+	} else {
+		struct wmeParams *wmep =
+			&ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE];
+		/*
+		 * Adhoc mode; important thing is to use 2x cwmin.
+		 */
+		qi.tqi_aifs = wmep->wmep_aifsn;
+		qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
+		qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
+	}
+
+	if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) {
+		device_printf(sc->sc_dev, "unable to update parameters for "
+			"beacon hardware queue!\n");
+		return 0;
+	} else {
+		ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */
+		return 1;
+	}
+#undef ATH_EXPONENT_TO_VALUE
+}
+
+/*
+ * Allocate and setup an initial beacon frame.
+ */
+int
+ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ath_vap *avp = ATH_VAP(vap);
+	struct ath_buf *bf;
+	struct mbuf *m;
+	int error;
+
+	bf = avp->av_bcbuf;
+	DPRINTF(sc, ATH_DEBUG_NODE, "%s: bf_m=%p, bf_node=%p\n",
+	    __func__, bf->bf_m, bf->bf_node);
+	if (bf->bf_m != NULL) {
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		m_freem(bf->bf_m);
+		bf->bf_m = NULL;
+	}
+	if (bf->bf_node != NULL) {
+		ieee80211_free_node(bf->bf_node);
+		bf->bf_node = NULL;
+	}
+
+	/*
+	 * NB: the beacon data buffer must be 32-bit aligned;
+	 * we assume the mbuf routines will return us something
+	 * with this alignment (perhaps should assert).
+	 */
+	m = ieee80211_beacon_alloc(ni, &avp->av_boff);
+	if (m == NULL) {
+		device_printf(sc->sc_dev, "%s: cannot get mbuf\n", __func__);
+		sc->sc_stats.ast_be_nombuf++;
+		return ENOMEM;
+	}
+	error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
+				     bf->bf_segs, &bf->bf_nseg,
+				     BUS_DMA_NOWAIT);
+	if (error != 0) {
+		device_printf(sc->sc_dev,
+		    "%s: cannot map mbuf, bus_dmamap_load_mbuf_sg returns %d\n",
+		    __func__, error);
+		m_freem(m);
+		return error;
+	}
+
+	/*
+	 * Calculate a TSF adjustment factor required for staggered
+	 * beacons.  Note that we assume the format of the beacon
+	 * frame leaves the tstamp field immediately following the
+	 * header.
+	 */
+	if (sc->sc_stagbeacons && avp->av_bslot > 0) {
+		uint64_t tsfadjust;
+		struct ieee80211_frame *wh;
+
+		/*
+		 * The beacon interval is in TU's; the TSF is in usecs.
+		 * We figure out how many TU's to add to align the timestamp
+		 * then convert to TSF units and handle byte swapping before
+		 * inserting it in the frame.  The hardware will then add this
+		 * each time a beacon frame is sent.  Note that we align vap's
+		 * 1..N and leave vap 0 untouched.  This means vap 0 has a
+		 * timestamp in one beacon interval while the others get a
+		 * timstamp aligned to the next interval.
+		 */
+		tsfadjust = ni->ni_intval *
+		    (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF;
+		tsfadjust = htole64(tsfadjust << 10);	/* TU -> TSF */
+
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+		    "%s: %s beacons bslot %d intval %u tsfadjust %llu\n",
+		    __func__, sc->sc_stagbeacons ? "stagger" : "burst",
+		    avp->av_bslot, ni->ni_intval,
+		    (long long unsigned) le64toh(tsfadjust));
+
+		wh = mtod(m, struct ieee80211_frame *);
+		memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust));
+	}
+	bf->bf_m = m;
+	bf->bf_node = ieee80211_ref_node(ni);
+
+	return 0;
+}
+
+/*
+ * Setup the beacon frame for transmit.
+ */
+static void
+ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
+{
+#define	USE_SHPREAMBLE(_ic) \
+	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
+		== IEEE80211_F_SHPREAMBLE)
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m = bf->bf_m;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_desc *ds;
+	int flags, antenna;
+	const HAL_RATE_TABLE *rt;
+	u_int8_t rix, rate;
+
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n",
+		__func__, m, m->m_len);
+
+	/* setup descriptors */
+	ds = bf->bf_desc;
+	bf->bf_last = bf;
+	bf->bf_lastds = ds;
+
+	flags = HAL_TXDESC_NOACK;
+	if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) {
+		/* self-linked descriptor */
+		ath_hal_settxdesclink(sc->sc_ah, ds, bf->bf_daddr);
+		flags |= HAL_TXDESC_VEOL;
+		/*
+		 * Let hardware handle antenna switching.
+		 */
+		antenna = sc->sc_txantenna;
+	} else {
+		ath_hal_settxdesclink(sc->sc_ah, ds, 0);
+		/*
+		 * Switch antenna every 4 beacons.
+		 * XXX assumes two antenna
+		 */
+		if (sc->sc_txantenna != 0)
+			antenna = sc->sc_txantenna;
+		else if (sc->sc_stagbeacons && sc->sc_nbcnvaps != 0)
+			antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 4 ? 2 : 1);
+		else
+			antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
+	}
+
+	KASSERT(bf->bf_nseg == 1,
+		("multi-segment beacon frame; nseg %u", bf->bf_nseg));
+	ds->ds_data = bf->bf_segs[0].ds_addr;
+	/*
+	 * Calculate rate code.
+	 * XXX everything at min xmit rate
+	 */
+	rix = 0;
+	rt = sc->sc_currates;
+	rate = rt->info[rix].rateCode;
+	if (USE_SHPREAMBLE(ic))
+		rate |= rt->info[rix].shortPreamble;
+	ath_hal_setuptxdesc(ah, ds
+		, m->m_len + IEEE80211_CRC_LEN	/* frame length */
+		, sizeof(struct ieee80211_frame)/* header length */
+		, HAL_PKT_TYPE_BEACON		/* Atheros packet type */
+		, ni->ni_txpower		/* txpower XXX */
+		, rate, 1			/* series 0 rate/tries */
+		, HAL_TXKEYIX_INVALID		/* no encryption */
+		, antenna			/* antenna mode */
+		, flags				/* no ack, veol for beacons */
+		, 0				/* rts/cts rate */
+		, 0				/* rts/cts duration */
+	);
+	/* NB: beacon's BufLen must be a multiple of 4 bytes */
+	ath_hal_filltxdesc(ah, ds
+		, roundup(m->m_len, 4)		/* buffer length */
+		, AH_TRUE			/* first segment */
+		, AH_TRUE			/* last segment */
+		, ds				/* first descriptor */
+	);
+#if 0
+	ath_desc_swap(ds);
+#endif
+#undef USE_SHPREAMBLE
+}
+
+void
+ath_beacon_update(struct ieee80211vap *vap, int item)
+{
+	struct ieee80211_beacon_offsets *bo = &ATH_VAP(vap)->av_boff;
+
+	setbit(bo->bo_flags, item);
+}
+
+/*
+ * Transmit a beacon frame at SWBA.  Dynamic updates to the
+ * frame contents are done as needed and the slot time is
+ * also adjusted based on current state.
+ */
+void
+ath_beacon_proc(void *arg, int pending)
+{
+	struct ath_softc *sc = arg;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211vap *vap;
+	struct ath_buf *bf;
+	int slot, otherant;
+	uint32_t bfaddr;
+
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n",
+		__func__, pending);
+	/*
+	 * 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++;
+		sc->sc_stats.ast_be_missed++;
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+			"%s: missed %u consecutive beacons\n",
+			__func__, sc->sc_bmisscount);
+		if (sc->sc_bmisscount >= ath_bstuck_threshold)
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask);
+		return;
+	}
+	if (sc->sc_bmisscount != 0) {
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+			"%s: resume beacon xmit after %u misses\n",
+			__func__, sc->sc_bmisscount);
+		sc->sc_bmisscount = 0;
+	}
+
+	if (sc->sc_stagbeacons) {			/* staggered beacons */
+		struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+		uint32_t tsftu;
+
+		tsftu = ath_hal_gettsf32(ah) >> 10;
+		/* XXX lintval */
+		slot = ((tsftu % ic->ic_lintval) * ATH_BCBUF) / ic->ic_lintval;
+		vap = sc->sc_bslot[(slot+1) % ATH_BCBUF];
+		bfaddr = 0;
+		if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) {
+			bf = ath_beacon_generate(sc, vap);
+			if (bf != NULL)
+				bfaddr = bf->bf_daddr;
+		}
+	} else {					/* burst'd beacons */
+		uint32_t *bflink = &bfaddr;
+
+		for (slot = 0; slot < ATH_BCBUF; slot++) {
+			vap = sc->sc_bslot[slot];
+			if (vap != NULL && vap->iv_state >= IEEE80211_S_RUN) {
+				bf = ath_beacon_generate(sc, vap);
+				if (bf != NULL) {
+					/* XXX should do this using the ds */
+					*bflink = bf->bf_daddr;
+					ath_hal_gettxdesclinkptr(sc->sc_ah,
+					    bf->bf_desc, &bflink);
+				}
+			}
+		}
+		*bflink = 0;				/* terminate list */
+	}
+
+	/*
+	 * Handle slot time change when a non-ERP station joins/leaves
+	 * an 11g network.  The 802.11 layer notifies us via callback,
+	 * we mark updateslot, then wait one beacon before effecting
+	 * the change.  This gives associated stations at least one
+	 * beacon interval to note the state change.
+	 */
+	/* XXX locking */
+	if (sc->sc_updateslot == UPDATE) {
+		sc->sc_updateslot = COMMIT;	/* commit next beacon */
+		sc->sc_slotupdate = slot;
+	} else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot)
+		ath_setslottime(sc);		/* commit change to h/w */
+
+	/*
+	 * 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 && (!sc->sc_stagbeacons || slot == 0)) {
+		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;
+	}
+
+	if (bfaddr != 0) {
+		/*
+		 * 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: cabq traffic should already be queued and primed */
+		ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr);
+		ath_hal_txstart(ah, sc->sc_bhalq);
+
+		sc->sc_stats.ast_be_xmit++;
+	}
+}
+
+struct ath_buf *
+ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+	struct ath_vap *avp = ATH_VAP(vap);
+	struct ath_txq *cabq = sc->sc_cabq;
+	struct ath_buf *bf;
+	struct mbuf *m;
+	int nmcastq, error;
+
+	KASSERT(vap->iv_state >= IEEE80211_S_RUN,
+	    ("not running, state %d", vap->iv_state));
+	KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer"));
+
+	/*
+	 * Update dynamic beacon contents.  If this returns
+	 * non-zero then we need to remap the memory because
+	 * the beacon frame changed size (probably because
+	 * of the TIM bitmap).
+	 */
+	bf = avp->av_bcbuf;
+	m = bf->bf_m;
+	/* XXX lock mcastq? */
+	nmcastq = avp->av_mcastq.axq_depth;
+
+	if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, nmcastq)) {
+		/* XXX too conservative? */
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
+					     bf->bf_segs, &bf->bf_nseg,
+					     BUS_DMA_NOWAIT);
+		if (error != 0) {
+			if_printf(vap->iv_ifp,
+			    "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
+			    __func__, error);
+			return NULL;
+		}
+	}
+	if ((avp->av_boff.bo_tim[4] & 1) && cabq->axq_depth) {
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+		    "%s: cabq did not drain, mcastq %u cabq %u\n",
+		    __func__, nmcastq, cabq->axq_depth);
+		sc->sc_stats.ast_cabq_busy++;
+		if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) {
+			/*
+			 * CABQ traffic from a previous vap is still pending.
+			 * We must drain the q before this beacon frame goes
+			 * out as otherwise this vap's stations will get cab
+			 * frames from a different vap.
+			 * XXX could be slow causing us to miss DBA
+			 */
+			ath_tx_draintxq(sc, cabq);
+		}
+	}
+	ath_beacon_setup(sc, bf);
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+
+	/*
+	 * Enable the CAB queue before the beacon queue to
+	 * insure cab frames are triggered by this beacon.
+	 */
+	if (avp->av_boff.bo_tim[4] & 1) {
+		struct ath_hal *ah = sc->sc_ah;
+
+		/* NB: only at DTIM */
+		ATH_TXQ_LOCK(cabq);
+		ATH_TXQ_LOCK(&avp->av_mcastq);
+		if (nmcastq) {
+			struct ath_buf *bfm;
+
+			/*
+			 * Move frames from the s/w mcast q to the h/w cab q.
+			 * XXX MORE_DATA bit
+			 */
+			bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q);
+			if (cabq->axq_link != NULL) {
+				*cabq->axq_link = bfm->bf_daddr;
+			} else
+				ath_hal_puttxbuf(ah, cabq->axq_qnum,
+					bfm->bf_daddr);
+			ath_txqmove(cabq, &avp->av_mcastq);
+
+			sc->sc_stats.ast_cabq_xmit += nmcastq;
+		}
+		/* NB: gated by beacon so safe to start here */
+		if (! TAILQ_EMPTY(&(cabq->axq_q)))
+			ath_hal_txstart(ah, cabq->axq_qnum);
+		ATH_TXQ_UNLOCK(&avp->av_mcastq);
+		ATH_TXQ_UNLOCK(cabq);
+	}
+	return bf;
+}
+
+void
+ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+	struct ath_vap *avp = ATH_VAP(vap);
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_buf *bf;
+	struct mbuf *m;
+	int error;
+
+	KASSERT(avp->av_bcbuf != NULL, ("no beacon buffer"));
+
+	/*
+	 * Update dynamic beacon contents.  If this returns
+	 * non-zero then we need to remap the memory because
+	 * the beacon frame changed size (probably because
+	 * of the TIM bitmap).
+	 */
+	bf = avp->av_bcbuf;
+	m = bf->bf_m;
+	if (ieee80211_beacon_update(bf->bf_node, &avp->av_boff, m, 0)) {
+		/* XXX too conservative? */
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m,
+					     bf->bf_segs, &bf->bf_nseg,
+					     BUS_DMA_NOWAIT);
+		if (error != 0) {
+			if_printf(vap->iv_ifp,
+			    "%s: bus_dmamap_load_mbuf_sg failed, error %u\n",
+			    __func__, error);
+			return;
+		}
+	}
+	ath_beacon_setup(sc, bf);
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+
+	/* NB: caller is known to have already stopped tx dma */
+	ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
+	ath_hal_txstart(ah, sc->sc_bhalq);
+}
+
+/*
+ * Reclaim beacon resources and return buffer to the pool.
+ */
+void
+ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf)
+{
+
+	DPRINTF(sc, ATH_DEBUG_NODE, "%s: free bf=%p, bf_m=%p, bf_node=%p\n",
+	    __func__, bf, bf->bf_m, bf->bf_node);
+	if (bf->bf_m != NULL) {
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		m_freem(bf->bf_m);
+		bf->bf_m = NULL;
+	}
+	if (bf->bf_node != NULL) {
+		ieee80211_free_node(bf->bf_node);
+		bf->bf_node = NULL;
+	}
+	TAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list);
+}
+
+/*
+ * Reclaim beacon resources.
+ */
+void
+ath_beacon_free(struct ath_softc *sc)
+{
+	struct ath_buf *bf;
+
+	TAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) {
+		DPRINTF(sc, ATH_DEBUG_NODE,
+		    "%s: free bf=%p, bf_m=%p, bf_node=%p\n",
+		        __func__, bf, bf->bf_m, bf->bf_node);
+		if (bf->bf_m != NULL) {
+			bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+			m_freem(bf->bf_m);
+			bf->bf_m = NULL;
+		}
+		if (bf->bf_node != NULL) {
+			ieee80211_free_node(bf->bf_node);
+			bf->bf_node = NULL;
+		}
+	}
+}
+
+/*
+ * Configure the beacon and sleep timers.
+ *
+ * When operating as an AP this resets the TSF and sets
+ * up the hardware to notify us when we need to issue beacons.
+ *
+ * When operating in station mode this sets up the beacon
+ * timers according to the timestamp of the last received
+ * beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware
+ * will wakeup in time to receive beacons, and configures
+ * the beacon miss handling so we'll receive a BMISS
+ * interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+void
+ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+#define	TSF_TO_TU(_h,_l) \
+	((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
+#define	FUDGE	2
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+	struct ieee80211_node *ni;
+	u_int32_t nexttbtt, intval, tsftu;
+	u_int64_t tsf;
+
+	if (vap == NULL)
+		vap = TAILQ_FIRST(&ic->ic_vaps);	/* XXX */
+	ni = ieee80211_ref_node(vap->iv_bss);
+
+	/* extract tstamp from last beacon and convert to TU */
+	nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4),
+			     LE_READ_4(ni->ni_tstamp.data));
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+	    ic->ic_opmode == IEEE80211_M_MBSS) {
+		/*
+		 * For multi-bss ap/mesh support beacons are either staggered
+		 * evenly over N slots or burst together.  For the former
+		 * arrange for the SWBA to be delivered for each slot.
+		 * Slots that are not occupied will generate nothing.
+		 */
+		/* NB: the beacon interval is kept internally in TU's */
+		intval = ni->ni_intval & HAL_BEACON_PERIOD;
+		if (sc->sc_stagbeacons)
+			intval /= ATH_BCBUF;
+	} else {
+		/* NB: the beacon interval is kept internally in TU's */
+		intval = ni->ni_intval & HAL_BEACON_PERIOD;
+	}
+	if (nexttbtt == 0)		/* e.g. for ap mode */
+		nexttbtt = intval;
+	else if (intval)		/* NB: can be 0 for monitor mode */
+		nexttbtt = roundup(nexttbtt, intval);
+	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
+		__func__, nexttbtt, intval, ni->ni_intval);
+	if (ic->ic_opmode == IEEE80211_M_STA && !sc->sc_swbmiss) {
+		HAL_BEACON_STATE bs;
+		int dtimperiod, dtimcount;
+		int cfpperiod, cfpcount;
+
+		/*
+		 * Setup dtim and cfp parameters according to
+		 * last beacon we received (which may be none).
+		 */
+		dtimperiod = ni->ni_dtim_period;
+		if (dtimperiod <= 0)		/* NB: 0 if not known */
+			dtimperiod = 1;
+		dtimcount = ni->ni_dtim_count;
+		if (dtimcount >= dtimperiod)	/* NB: sanity check */
+			dtimcount = 0;		/* XXX? */
+		cfpperiod = 1;			/* NB: no PCF support yet */
+		cfpcount = 0;
+		/*
+		 * Pull nexttbtt forward to reflect the current
+		 * TSF and calculate dtim+cfp state for the result.
+		 */
+		tsf = ath_hal_gettsf64(ah);
+		tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
+		do {
+			nexttbtt += intval;
+			if (--dtimcount < 0) {
+				dtimcount = dtimperiod - 1;
+				if (--cfpcount < 0)
+					cfpcount = cfpperiod - 1;
+			}
+		} while (nexttbtt < tsftu);
+		memset(&bs, 0, sizeof(bs));
+		bs.bs_intval = intval;
+		bs.bs_nexttbtt = nexttbtt;
+		bs.bs_dtimperiod = dtimperiod*intval;
+		bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
+		bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
+		bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
+		bs.bs_cfpmaxduration = 0;
+#if 0
+		/*
+		 * The 802.11 layer records the offset to the DTIM
+		 * bitmap while receiving beacons; use it here to
+		 * enable h/w detection of our AID being marked in
+		 * the bitmap vector (to indicate frames for us are
+		 * pending at the AP).
+		 * XXX do DTIM handling in s/w to WAR old h/w bugs
+		 * XXX enable based on h/w rev for newer chips
+		 */
+		bs.bs_timoffset = ni->ni_timoff;
+#endif
+		/*
+		 * Calculate the number of consecutive beacons to miss
+		 * before taking a BMISS interrupt.
+		 * Note that we clamp the result to at most 10 beacons.
+		 */
+		bs.bs_bmissthreshold = vap->iv_bmissthreshold;
+		if (bs.bs_bmissthreshold > 10)
+			bs.bs_bmissthreshold = 10;
+		else if (bs.bs_bmissthreshold <= 0)
+			bs.bs_bmissthreshold = 1;
+
+		/*
+		 * Calculate sleep duration.  The configuration is
+		 * given in ms.  We insure a multiple of the beacon
+		 * period is used.  Also, if the sleep duration is
+		 * greater than the DTIM period then it makes senses
+		 * to make it a multiple of that.
+		 *
+		 * XXX fixed at 100ms
+		 */
+		bs.bs_sleepduration =
+			roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval);
+		if (bs.bs_sleepduration > bs.bs_dtimperiod)
+			bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
+
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+			"%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n"
+			, __func__
+			, tsf, tsftu
+			, bs.bs_intval
+			, bs.bs_nexttbtt
+			, bs.bs_dtimperiod
+			, bs.bs_nextdtim
+			, bs.bs_bmissthreshold
+			, bs.bs_sleepduration
+			, bs.bs_cfpperiod
+			, bs.bs_cfpmaxduration
+			, bs.bs_cfpnext
+			, bs.bs_timoffset
+		);
+		ath_hal_intrset(ah, 0);
+		ath_hal_beacontimers(ah, &bs);
+		sc->sc_imask |= HAL_INT_BMISS;
+		ath_hal_intrset(ah, sc->sc_imask);
+	} else {
+		ath_hal_intrset(ah, 0);
+		if (nexttbtt == intval)
+			intval |= HAL_BEACON_RESET_TSF;
+		if (ic->ic_opmode == IEEE80211_M_IBSS) {
+			/*
+			 * In IBSS mode enable the beacon timers but only
+			 * enable SWBA interrupts if we need to manually
+			 * prepare beacon frames.  Otherwise we use a
+			 * self-linked tx descriptor and let the hardware
+			 * deal with things.
+			 */
+			intval |= HAL_BEACON_ENA;
+			if (!sc->sc_hasveol)
+				sc->sc_imask |= HAL_INT_SWBA;
+			if ((intval & HAL_BEACON_RESET_TSF) == 0) {
+				/*
+				 * Pull nexttbtt forward to reflect
+				 * the current TSF.
+				 */
+				tsf = ath_hal_gettsf64(ah);
+				tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
+				do {
+					nexttbtt += intval;
+				} while (nexttbtt < tsftu);
+			}
+			ath_beaconq_config(sc);
+		} else if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+		    ic->ic_opmode == IEEE80211_M_MBSS) {
+			/*
+			 * In AP/mesh mode we enable the beacon timers
+			 * and SWBA interrupts to prepare beacon frames.
+			 */
+			intval |= HAL_BEACON_ENA;
+			sc->sc_imask |= HAL_INT_SWBA;	/* beacon prepare */
+			ath_beaconq_config(sc);
+		}
+		ath_hal_beaconinit(ah, nexttbtt, intval);
+		sc->sc_bmisscount = 0;
+		ath_hal_intrset(ah, sc->sc_imask);
+		/*
+		 * When using a self-linked beacon descriptor in
+		 * ibss mode load it once here.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol)
+			ath_beacon_start_adhoc(sc, vap);
+	}
+	sc->sc_syncbeacon = 0;
+	ieee80211_free_node(ni);
+#undef FUDGE
+#undef TSF_TO_TU
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_beacon.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_beacon.h	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: head/sys/dev/ath/if_ath_beacon.h 235680 2012-05-20 04:14:29Z adrian $
+ */
+#ifndef	__IF_ATH_BEACON_H__
+#define	__IF_ATH_BEACON_H__
+
+extern	int ath_bstuck_threshold;
+
+extern	int ath_beaconq_setup(struct ath_hal *ah);
+extern	int ath_beaconq_config(struct ath_softc *sc);
+extern	void ath_beacon_config(struct ath_softc *sc,
+	    struct ieee80211vap *vap);
+extern	struct ath_buf * ath_beacon_generate(struct ath_softc *sc,
+	    struct ieee80211vap *vap);
+extern	int ath_wme_update(struct ieee80211com *ic);
+extern	void ath_beacon_update(struct ieee80211vap *vap, int item);
+extern	void ath_beacon_start_adhoc(struct ath_softc *sc,
+	    struct ieee80211vap *vap);
+extern	int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni);
+extern	void ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf);
+extern	void ath_beacon_free(struct ath_softc *sc);
+extern	void ath_beacon_proc(void *arg, int pending);
+
+#endif
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_debug.c
--- a/head/sys/dev/ath/if_ath_debug.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_debug.c	Wed Jul 25 17:07:47 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_debug.c 233966 2012-04-07 02:01:26Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_debug.c 238275 2012-07-09 06:39:46Z adrian $");
 
 #include "opt_inet.h"
 #include "opt_ath.h"
@@ -89,12 +89,12 @@
 #ifdef ATH_DEBUG
 #include <dev/ath/if_ath_debug.h>
 
-int ath_debug = 0;
+uint64_t ath_debug = 0;
 
 SYSCTL_DECL(_hw_ath);
-SYSCTL_INT(_hw_ath, OID_AUTO, debug, CTLFLAG_RW, &ath_debug,
+SYSCTL_QUAD(_hw_ath, OID_AUTO, debug, CTLFLAG_RW, &ath_debug,
 	    0, "control debugging printfs");
-TUNABLE_INT("hw.ath.debug", &ath_debug);
+TUNABLE_QUAD("hw.ath.debug", &ath_debug);
 
 void
 ath_printrxbuf(struct ath_softc *sc, const struct ath_buf *bf,
@@ -118,6 +118,16 @@
 			    ds->ds_hw[2], ds->ds_hw[3], ds->ds_hw[4],
 			    ds->ds_hw[5], ds->ds_hw[6], ds->ds_hw[7],
 			    ds->ds_hw[8]);
+		} else if (ah->ah_magic == 0x19741014) {
+			printf("        %08x %08x %08x %08x %08x %08x %08x\n",
+			    ds->ds_hw[2], ds->ds_hw[3], ds->ds_hw[4],
+			    ds->ds_hw[5], ds->ds_hw[6], ds->ds_hw[7],
+			    ds->ds_hw[8]);
+
+			printf("        %08x %08x %08x %08x %08x %08x %08x\n",
+			    ds->ds_hw[9], ds->ds_hw[10], ds->ds_hw[11],
+			    ds->ds_hw[12], ds->ds_hw[13], ds->ds_hw[14],
+			    ds->ds_hw[15]);
 		}
 	}
 }
@@ -144,9 +154,6 @@
 			    bf->bf_state.bfs_retries,
 			    bf->bf_state.bfs_addedbaw,
 			    bf->bf_state.bfs_dobaw);
-			printf("        SEQNO_ASSIGNED: %d, NEED_SEQNO: %d\n",
-			    bf->bf_state.bfs_seqno_assigned,
-			    bf->bf_state.bfs_need_seqno);
 			printf("        %08x %08x %08x %08x %08x %08x\n",
 			    ds->ds_ctl0, ds->ds_ctl1,
 			    ds->ds_hw[0], ds->ds_hw[1],
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_debug.h
--- a/head/sys/dev/ath/if_ath_debug.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_debug.h	Wed Jul 25 17:07:47 2012 +0300
@@ -26,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/if_ath_debug.h 226900 2011-10-29 07:17:47Z adrian $
+ * $FreeBSD: head/sys/dev/ath/if_ath_debug.h 238364 2012-07-11 12:10:13Z jhb $
  */
 #ifndef	__IF_ATH_DEBUG_H__
 #define	__IF_ATH_DEBUG_H__
@@ -34,39 +34,45 @@
 #ifdef	ATH_DEBUG
 
 enum { 
-	ATH_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
-	ATH_DEBUG_XMIT_DESC	= 0x00000002,	/* xmit descriptors */
-	ATH_DEBUG_RECV		= 0x00000004,	/* basic recv operation */
-	ATH_DEBUG_RECV_DESC	= 0x00000008,	/* recv descriptors */
-	ATH_DEBUG_RATE		= 0x00000010,	/* rate control */
-	ATH_DEBUG_RESET		= 0x00000020,	/* reset processing */
-	ATH_DEBUG_MODE		= 0x00000040,	/* mode init/setup */
-	ATH_DEBUG_BEACON	= 0x00000080,	/* beacon handling */
-	ATH_DEBUG_WATCHDOG	= 0x00000100,	/* watchdog timeout */
-	ATH_DEBUG_INTR		= 0x00001000,	/* ISR */
-	ATH_DEBUG_TX_PROC	= 0x00002000,	/* tx ISR proc */
-	ATH_DEBUG_RX_PROC	= 0x00004000,	/* rx ISR proc */
-	ATH_DEBUG_BEACON_PROC	= 0x00008000,	/* beacon ISR proc */
-	ATH_DEBUG_CALIBRATE	= 0x00010000,	/* periodic calibration */
-	ATH_DEBUG_KEYCACHE	= 0x00020000,	/* key cache management */
-	ATH_DEBUG_STATE		= 0x00040000,	/* 802.11 state transitions */
-	ATH_DEBUG_NODE		= 0x00080000,	/* node management */
-	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_SW_TX		= 0x04000000,	/* per-packet software TX */
-	ATH_DEBUG_SW_TX_BAW	= 0x08000000,	/* BAW handling */
-	ATH_DEBUG_SW_TX_CTRL	= 0x10000000,	/* queue control */
-	ATH_DEBUG_SW_TX_AGGR	= 0x20000000,	/* aggregate TX */
-	ATH_DEBUG_SW_TX_RETRIES	= 0x40000000,	/* software TX retries */
-	ATH_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
-	ATH_DEBUG_ANY		= 0xffffffff
+	ATH_DEBUG_XMIT		= 0x000000001ULL,	/* basic xmit operation */
+	ATH_DEBUG_XMIT_DESC	= 0x000000002ULL,	/* xmit descriptors */
+	ATH_DEBUG_RECV		= 0x000000004ULL,	/* basic recv operation */
+	ATH_DEBUG_RECV_DESC	= 0x000000008ULL,	/* recv descriptors */
+	ATH_DEBUG_RATE		= 0x000000010ULL,	/* rate control */
+	ATH_DEBUG_RESET		= 0x000000020ULL,	/* reset processing */
+	ATH_DEBUG_MODE		= 0x000000040ULL,	/* mode init/setup */
+	ATH_DEBUG_BEACON	= 0x000000080ULL,	/* beacon handling */
+	ATH_DEBUG_WATCHDOG	= 0x000000100ULL,	/* watchdog timeout */
+	ATH_DEBUG_INTR		= 0x000001000ULL,	/* ISR */
+	ATH_DEBUG_TX_PROC	= 0x000002000ULL,	/* tx ISR proc */
+	ATH_DEBUG_RX_PROC	= 0x000004000ULL,	/* rx ISR proc */
+	ATH_DEBUG_BEACON_PROC	= 0x000008000ULL,	/* beacon ISR proc */
+	ATH_DEBUG_CALIBRATE	= 0x000010000ULL,	/* periodic calibration */
+	ATH_DEBUG_KEYCACHE	= 0x000020000ULL,	/* key cache management */
+	ATH_DEBUG_STATE		= 0x000040000ULL,	/* 802.11 state transitions */
+	ATH_DEBUG_NODE		= 0x000080000ULL,	/* node management */
+	ATH_DEBUG_LED		= 0x000100000ULL,	/* led management */
+	ATH_DEBUG_FF		= 0x000200000ULL,	/* fast frames */
+	ATH_DEBUG_DFS		= 0x000400000ULL,	/* DFS processing */
+	ATH_DEBUG_TDMA		= 0x000800000ULL,	/* TDMA processing */
+	ATH_DEBUG_TDMA_TIMER	= 0x001000000ULL,	/* TDMA timer processing */
+	ATH_DEBUG_REGDOMAIN	= 0x002000000ULL,	/* regulatory processing */
+	ATH_DEBUG_SW_TX		= 0x004000000ULL,	/* per-packet software TX */
+	ATH_DEBUG_SW_TX_BAW	= 0x008000000ULL,	/* BAW handling */
+	ATH_DEBUG_SW_TX_CTRL	= 0x010000000ULL,	/* queue control */
+	ATH_DEBUG_SW_TX_AGGR	= 0x020000000ULL,	/* aggregate TX */
+	ATH_DEBUG_SW_TX_RETRIES	= 0x040000000ULL,	/* software TX retries */
+	ATH_DEBUG_FATAL		= 0x080000000ULL,	/* fatal errors */
+	ATH_DEBUG_SW_TX_BAR	= 0x100000000ULL,	/* BAR TX */
+	ATH_DEBUG_EDMA_RX	= 0x200000000ULL,	/* RX EDMA state */
+
+	ATH_DEBUG_ANY		= 0xffffffffffffffffULL
 };
 
-extern int	ath_debug;
+#define	ATH_KTR_INTR	KTR_SPARE4
+#define	ATH_KTR_ERR	KTR_SPARE3
+
+extern uint64_t ath_debug;
 
 #define	IFF_DUMPPKTS(sc, m) \
 	((sc->sc_debug & (m)) || \
@@ -85,6 +91,9 @@
 extern	void ath_printtxbuf(struct ath_softc *, const struct ath_buf *bf,
 	u_int qnum, u_int ix, int done);
 #else	/* ATH_DEBUG */
+#define	ATH_KTR_INTR	0
+#define	ATH_KTR_ERR	0
+
 #define	IFF_DUMPPKTS(sc, m) \
 	((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
 #define	DPRINTF(sc, m, fmt, ...) do {				\
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_led.c
--- a/head/sys/dev/ath/if_ath_led.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_led.c	Wed Jul 25 17:07:47 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_led.c 228892 2011-12-26 07:48:29Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_led.c 237611 2012-06-26 22:16:53Z adrian $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -125,7 +125,7 @@
 	/* Software LED blinking - GPIO controlled LED */
 	if (sc->sc_softled) {
 		ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin,
-		    HAL_GPIO_MUX_OUTPUT);
+		    HAL_GPIO_OUTPUT_MUX_AS_OUTPUT);
 		ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
 	}
 
@@ -139,10 +139,10 @@
 		 */
 		if (sc->sc_led_pwr_pin > 0)
 			ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_led_pwr_pin,
-			    HAL_GPIO_MUX_MAC_POWER_LED);
+			    HAL_GPIO_OUTPUT_MUX_MAC_POWER_LED);
 		if (sc->sc_led_net_pin > 0)
 			ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_led_net_pin,
-			    HAL_GPIO_MUX_MAC_NETWORK_LED);
+			    HAL_GPIO_OUTPUT_MUX_MAC_NETWORK_LED);
 	}
 }
 
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_misc.h
--- a/head/sys/dev/ath/if_ath_misc.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_misc.h	Wed Jul 25 17:07:47 2012 +0300
@@ -26,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/if_ath_misc.h 227364 2011-11-08 22:43:13Z adrian $
+ * $FreeBSD: head/sys/dev/ath/if_ath_misc.h 238729 2012-07-23 23:40:13Z adrian $
  */
 #ifndef	__IF_ATH_MISC_H__
 #define	__IF_ATH_MISC_H__
@@ -48,13 +48,22 @@
 	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8) |	\
 	  (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
 
+extern int ath_rxbuf;
+extern int ath_txbuf;
+extern int ath_txbuf_mgmt;
+
 extern int ath_tx_findrix(const struct ath_softc *sc, uint8_t rate);
 
-extern struct ath_buf * ath_getbuf(struct ath_softc *sc);
-extern struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc);
+extern struct ath_buf * ath_getbuf(struct ath_softc *sc,
+	    ath_buf_type_t btype);
+extern struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc,
+	    ath_buf_type_t btype);
 extern struct ath_buf * ath_buf_clone(struct ath_softc *sc,
 	    const struct ath_buf *bf);
+/* XXX change this to NULL the buffer pointer? */
 extern void ath_freebuf(struct ath_softc *sc, struct ath_buf *bf);
+extern void ath_returnbuf_head(struct ath_softc *sc, struct ath_buf *bf);
+extern void ath_returnbuf_tail(struct ath_softc *sc, struct ath_buf *bf);
 
 extern int ath_reset(struct ifnet *, ATH_RESET_TYPE);
 extern void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq);
@@ -67,4 +76,37 @@
 extern void ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf,
     int status);
 
+extern void ath_txqmove(struct ath_txq *dst, struct ath_txq *src);
+
+extern void ath_mode_init(struct ath_softc *sc);
+
+extern void ath_setdefantenna(struct ath_softc *sc, u_int antenna);
+
+extern void ath_setslottime(struct ath_softc *sc);
+
+extern	int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
+	    ath_bufhead *head, const char *name, int ds_size, int nbuf,
+	    int ndesc);
+extern	int ath_descdma_setup_rx_edma(struct ath_softc *sc,
+	    struct ath_descdma *dd, ath_bufhead *head, const char *name,
+	    int nbuf, int desclen);
+extern	void ath_descdma_cleanup(struct ath_softc *sc,
+	    struct ath_descdma *dd, ath_bufhead *head);
+
+/*
+ * This is only here so that the RX proc function can call it.
+ * It's very likely that the "start TX after RX" call should be
+ * done via something in if_ath.c, moving "rx tasklet" into
+ * if_ath.c and do the ath_start() call there.  Once that's done,
+ * we can kill this.
+ */
+extern void ath_start(struct ifnet *ifp);
+
+static inline void
+ath_tx_kick(struct ath_softc *sc)
+{
+
+	ath_start(sc->sc_ifp);
+}
+
 #endif
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_pci.c
--- a/head/sys/dev/ath/if_ath_pci.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_pci.c	Wed Jul 25 17:07:47 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_pci.c 234304 2012-04-15 00:04:23Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_pci.c 238709 2012-07-23 02:49:25Z adrian $");
 
 /*
  * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver.
@@ -249,12 +249,16 @@
 
 	ATH_LOCK_INIT(sc);
 	ATH_PCU_LOCK_INIT(sc);
+	ATH_RX_LOCK_INIT(sc);
+	ATH_TXSTATUS_LOCK_INIT(sc);
 
 	error = ath_attach(pci_get_device(dev), sc);
 	if (error == 0)					/* success */
 		return 0;
 
+	ATH_TXSTATUS_LOCK_DESTROY(sc);
 	ATH_PCU_LOCK_DESTROY(sc);
+	ATH_RX_LOCK_DESTROY(sc);
 	ATH_LOCK_DESTROY(sc);
 	bus_dma_tag_destroy(sc->sc_dmat);
 bad3:
@@ -293,7 +297,9 @@
 	if (sc->sc_eepromdata)
 		free(sc->sc_eepromdata, M_TEMP);
 
+	ATH_TXSTATUS_LOCK_DESTROY(sc);
 	ATH_PCU_LOCK_DESTROY(sc);
+	ATH_RX_LOCK_DESTROY(sc);
 	ATH_LOCK_DESTROY(sc);
 
 	return (0);
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_rx.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_rx.c	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,1113 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_rx.c 238729 2012-07-23 23:40:13Z adrian $");
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+
+#include "opt_inet.h"
+#include "opt_ath.h"
+/*
+ * This is needed for register operations which are performed
+ * by the driver - eg, calls to ath_hal_gettsf32().
+ *
+ * It's also required for any AH_DEBUG checks in here, eg the
+ * module dependencies.
+ */
+#include "opt_ah.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/callout.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/priv.h>
+#include <sys/module.h>
+#include <sys/ktr.h>
+#include <sys/smp.h>	/* for mp_ncpus */
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
+#ifdef IEEE80211_SUPPORT_TDMA
+#include <net80211/ieee80211_tdma.h>
+#endif
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/ath/if_athvar.h>
+#include <dev/ath/ath_hal/ah_devid.h>		/* XXX for softled */
+#include <dev/ath/ath_hal/ah_diagcodes.h>
+
+#include <dev/ath/if_ath_debug.h>
+#include <dev/ath/if_ath_misc.h>
+#include <dev/ath/if_ath_tsf.h>
+#include <dev/ath/if_ath_tx.h>
+#include <dev/ath/if_ath_sysctl.h>
+#include <dev/ath/if_ath_led.h>
+#include <dev/ath/if_ath_keycache.h>
+#include <dev/ath/if_ath_rx.h>
+#include <dev/ath/if_ath_beacon.h>
+#include <dev/ath/if_athdfs.h>
+
+#ifdef ATH_TX99_DIAG
+#include <dev/ath/ath_tx99/ath_tx99.h>
+#endif
+
+/*
+ * Calculate the receive filter according to the
+ * operating mode and state:
+ *
+ * o always accept unicast, broadcast, and multicast traffic
+ * o accept PHY error frames when hardware doesn't have MIB support
+ *   to count and we need them for ANI (sta mode only until recently)
+ *   and we are not scanning (ANI is disabled)
+ *   NB: older hal's add rx filter bits out of sight and we need to
+ *	 blindly preserve them
+ * o probe request frames are accepted only when operating in
+ *   hostap, adhoc, mesh, or monitor modes
+ * o enable promiscuous mode
+ *   - when in monitor mode
+ *   - if interface marked PROMISC (assumes bridge setting is filtered)
+ * o accept beacons:
+ *   - when operating in station mode for collecting rssi data when
+ *     the station is otherwise quiet, or
+ *   - when operating in adhoc mode so the 802.11 layer creates
+ *     node table entries for peers,
+ *   - when scanning
+ *   - when doing s/w beacon miss (e.g. for ap+sta)
+ *   - when operating in ap mode in 11g to detect overlapping bss that
+ *     require protection
+ *   - when operating in mesh mode to detect neighbors
+ * o accept control frames:
+ *   - when in monitor mode
+ * XXX HT protection for 11n
+ */
+u_int32_t
+ath_calcrxfilter(struct ath_softc *sc)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ieee80211com *ic = ifp->if_l2com;
+	u_int32_t rfilt;
+
+	rfilt = HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
+	if (!sc->sc_needmib && !sc->sc_scanning)
+		rfilt |= HAL_RX_FILTER_PHYERR;
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		rfilt |= HAL_RX_FILTER_PROBEREQ;
+	/* XXX ic->ic_monvaps != 0? */
+	if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC))
+		rfilt |= HAL_RX_FILTER_PROM;
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_IBSS ||
+	    sc->sc_swbmiss || sc->sc_scanning)
+		rfilt |= HAL_RX_FILTER_BEACON;
+	/*
+	 * NB: We don't recalculate the rx filter when
+	 * ic_protmode changes; otherwise we could do
+	 * this only when ic_protmode != NONE.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	    IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
+		rfilt |= HAL_RX_FILTER_BEACON;
+
+	/*
+	 * Enable hardware PS-POLL RX only for hostap mode;
+	 * STA mode sends PS-POLL frames but never
+	 * receives them.
+	 */
+	if (ath_hal_getcapability(sc->sc_ah, HAL_CAP_PSPOLL,
+	    0, NULL) == HAL_OK &&
+	    ic->ic_opmode == IEEE80211_M_HOSTAP)
+		rfilt |= HAL_RX_FILTER_PSPOLL;
+
+	if (sc->sc_nmeshvaps) {
+		rfilt |= HAL_RX_FILTER_BEACON;
+		if (sc->sc_hasbmatch)
+			rfilt |= HAL_RX_FILTER_BSSID;
+		else
+			rfilt |= HAL_RX_FILTER_PROM;
+	}
+	if (ic->ic_opmode == IEEE80211_M_MONITOR)
+		rfilt |= HAL_RX_FILTER_CONTROL;
+
+	/*
+	 * Enable RX of compressed BAR frames only when doing
+	 * 802.11n. Required for A-MPDU.
+	 */
+	if (IEEE80211_IS_CHAN_HT(ic->ic_curchan))
+		rfilt |= HAL_RX_FILTER_COMPBAR;
+
+	/*
+	 * Enable radar PHY errors if requested by the
+	 * DFS module.
+	 */
+	if (sc->sc_dodfs)
+		rfilt |= HAL_RX_FILTER_PHYRADAR;
+
+	DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, %s if_flags 0x%x\n",
+	    __func__, rfilt, ieee80211_opmode_name[ic->ic_opmode], ifp->if_flags);
+	return rfilt;
+}
+
+static int
+ath_legacy_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	int error;
+	struct mbuf *m;
+	struct ath_desc *ds;
+
+	m = bf->bf_m;
+	if (m == NULL) {
+		/*
+		 * NB: by assigning a page to the rx dma buffer we
+		 * implicitly satisfy the Atheros requirement that
+		 * this buffer be cache-line-aligned and sized to be
+		 * multiple of the cache line size.  Not doing this
+		 * causes weird stuff to happen (for the 5210 at least).
+		 */
+		m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+		if (m == NULL) {
+			DPRINTF(sc, ATH_DEBUG_ANY,
+				"%s: no mbuf/cluster\n", __func__);
+			sc->sc_stats.ast_rx_nombuf++;
+			return ENOMEM;
+		}
+		m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+
+		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat,
+					     bf->bf_dmamap, m,
+					     bf->bf_segs, &bf->bf_nseg,
+					     BUS_DMA_NOWAIT);
+		if (error != 0) {
+			DPRINTF(sc, ATH_DEBUG_ANY,
+			    "%s: bus_dmamap_load_mbuf_sg failed; error %d\n",
+			    __func__, error);
+			sc->sc_stats.ast_rx_busdma++;
+			m_freem(m);
+			return error;
+		}
+		KASSERT(bf->bf_nseg == 1,
+			("multi-segment packet; nseg %u", bf->bf_nseg));
+		bf->bf_m = m;
+	}
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD);
+
+	/*
+	 * Setup descriptors.  For receive we always terminate
+	 * the descriptor list with a self-linked entry so we'll
+	 * not get overrun under high load (as can happen with a
+	 * 5212 when ANI processing enables PHY error frames).
+	 *
+	 * To insure the last descriptor is self-linked we create
+	 * each descriptor as self-linked and add it to the end.  As
+	 * each additional descriptor is added the previous self-linked
+	 * entry is ``fixed'' naturally.  This should be safe even
+	 * if DMA is happening.  When processing RX interrupts we
+	 * never remove/process the last, self-linked, entry on the
+	 * descriptor list.  This insures the hardware always has
+	 * someplace to write a new frame.
+	 */
+	/*
+	 * 11N: we can no longer afford to self link the last descriptor.
+	 * MAC acknowledges BA status as long as it copies frames to host
+	 * buffer (or rx fifo). This can incorrectly acknowledge packets
+	 * to a sender if last desc is self-linked.
+	 */
+	ds = bf->bf_desc;
+	if (sc->sc_rxslink)
+		ds->ds_link = bf->bf_daddr;	/* link to self */
+	else
+		ds->ds_link = 0;		/* terminate the list */
+	ds->ds_data = bf->bf_segs[0].ds_addr;
+	ath_hal_setuprxdesc(ah, ds
+		, m->m_len		/* buffer size */
+		, 0
+	);
+
+	if (sc->sc_rxlink != NULL)
+		*sc->sc_rxlink = bf->bf_daddr;
+	sc->sc_rxlink = &ds->ds_link;
+	return 0;
+}
+
+/*
+ * Intercept management frames to collect beacon rssi data
+ * and to do ibss merges.
+ */
+void
+ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
+	int subtype, int rssi, int nf)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
+
+	/*
+	 * Call up first so subsequent work can use information
+	 * potentially stored in the node (e.g. for ibss merge).
+	 */
+	ATH_VAP(vap)->av_recv_mgmt(ni, m, subtype, rssi, nf);
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_BEACON:
+		/* update rssi statistics for use by the hal */
+		/* XXX unlocked check against vap->iv_bss? */
+		ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi);
+		if (sc->sc_syncbeacon &&
+		    ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) {
+			/*
+			 * Resync beacon timers using the tsf of the beacon
+			 * frame we just received.
+			 */
+			ath_beacon_config(sc, vap);
+		}
+		/* fall thru... */
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+		if (vap->iv_opmode == IEEE80211_M_IBSS &&
+		    vap->iv_state == IEEE80211_S_RUN) {
+			uint32_t rstamp = sc->sc_lastrs->rs_tstamp;
+			uint64_t tsf = ath_extend_tsf(sc, rstamp,
+				ath_hal_gettsf64(sc->sc_ah));
+			/*
+			 * Handle ibss merge as needed; check the tsf on the
+			 * frame before attempting the merge.  The 802.11 spec
+			 * says the station should change it's bssid to match
+			 * the oldest station with the same ssid, where oldest
+			 * is determined by the tsf.  Note that hardware
+			 * reconfiguration happens through callback to
+			 * ath_newstate as the state machine will go from
+			 * RUN -> RUN when this happens.
+			 */
+			if (le64toh(ni->ni_tstamp.tsf) >= tsf) {
+				DPRINTF(sc, ATH_DEBUG_STATE,
+				    "ibss merge, rstamp %u tsf %ju "
+				    "tstamp %ju\n", rstamp, (uintmax_t)tsf,
+				    (uintmax_t)ni->ni_tstamp.tsf);
+				(void) ieee80211_ibss_merge(ni);
+			}
+		}
+		break;
+	}
+}
+
+#ifdef	ATH_ENABLE_RADIOTAP_VENDOR_EXT
+static void
+ath_rx_tap_vendor(struct ifnet *ifp, struct mbuf *m,
+    const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf)
+{
+	struct ath_softc *sc = ifp->if_softc;
+
+	/* Fill in the extension bitmap */
+	sc->sc_rx_th.wr_ext_bitmap = htole32(1 << ATH_RADIOTAP_VENDOR_HEADER);
+
+	/* Fill in the vendor header */
+	sc->sc_rx_th.wr_vh.vh_oui[0] = 0x7f;
+	sc->sc_rx_th.wr_vh.vh_oui[1] = 0x03;
+	sc->sc_rx_th.wr_vh.vh_oui[2] = 0x00;
+
+	/* XXX what should this be? */
+	sc->sc_rx_th.wr_vh.vh_sub_ns = 0;
+	sc->sc_rx_th.wr_vh.vh_skip_len =
+	    htole16(sizeof(struct ath_radiotap_vendor_hdr));
+
+	/* General version info */
+	sc->sc_rx_th.wr_v.vh_version = 1;
+
+	sc->sc_rx_th.wr_v.vh_rx_chainmask = sc->sc_rxchainmask;
+
+	/* rssi */
+	sc->sc_rx_th.wr_v.rssi_ctl[0] = rs->rs_rssi_ctl[0];
+	sc->sc_rx_th.wr_v.rssi_ctl[1] = rs->rs_rssi_ctl[1];
+	sc->sc_rx_th.wr_v.rssi_ctl[2] = rs->rs_rssi_ctl[2];
+	sc->sc_rx_th.wr_v.rssi_ext[0] = rs->rs_rssi_ext[0];
+	sc->sc_rx_th.wr_v.rssi_ext[1] = rs->rs_rssi_ext[1];
+	sc->sc_rx_th.wr_v.rssi_ext[2] = rs->rs_rssi_ext[2];
+
+	/* evm */
+	sc->sc_rx_th.wr_v.evm[0] = rs->rs_evm0;
+	sc->sc_rx_th.wr_v.evm[1] = rs->rs_evm1;
+	sc->sc_rx_th.wr_v.evm[2] = rs->rs_evm2;
+	/* XXX TODO: extend this to include 3-stream EVM */
+
+	/* phyerr info */
+	if (rs->rs_status & HAL_RXERR_PHY)
+		sc->sc_rx_th.wr_v.vh_phyerr_code = rs->rs_phyerr;
+	else
+		sc->sc_rx_th.wr_v.vh_phyerr_code = 0xff;
+	sc->sc_rx_th.wr_v.vh_rs_status = rs->rs_status;
+	sc->sc_rx_th.wr_v.vh_rssi = rs->rs_rssi;
+}
+#endif	/* ATH_ENABLE_RADIOTAP_VENDOR_EXT */
+
+static void
+ath_rx_tap(struct ifnet *ifp, struct mbuf *m,
+	const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf)
+{
+#define	CHAN_HT20	htole32(IEEE80211_CHAN_HT20)
+#define	CHAN_HT40U	htole32(IEEE80211_CHAN_HT40U)
+#define	CHAN_HT40D	htole32(IEEE80211_CHAN_HT40D)
+#define	CHAN_HT		(CHAN_HT20|CHAN_HT40U|CHAN_HT40D)
+	struct ath_softc *sc = ifp->if_softc;
+	const HAL_RATE_TABLE *rt;
+	uint8_t rix;
+
+	rt = sc->sc_currates;
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+	rix = rt->rateCodeToIndex[rs->rs_rate];
+	sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate;
+	sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags;
+#ifdef AH_SUPPORT_AR5416
+	sc->sc_rx_th.wr_chan_flags &= ~CHAN_HT;
+	if (sc->sc_rx_th.wr_rate & IEEE80211_RATE_MCS) {	/* HT rate */
+		struct ieee80211com *ic = ifp->if_l2com;
+
+		if ((rs->rs_flags & HAL_RX_2040) == 0)
+			sc->sc_rx_th.wr_chan_flags |= CHAN_HT20;
+		else if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
+			sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U;
+		else
+			sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D;
+		if ((rs->rs_flags & HAL_RX_GI) == 0)
+			sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI;
+	}
+#endif
+	sc->sc_rx_th.wr_tsf = htole64(ath_extend_tsf(sc, rs->rs_tstamp, tsf));
+	if (rs->rs_status & HAL_RXERR_CRC)
+		sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+	/* XXX propagate other error flags from descriptor */
+	sc->sc_rx_th.wr_antnoise = nf;
+	sc->sc_rx_th.wr_antsignal = nf + rs->rs_rssi;
+	sc->sc_rx_th.wr_antenna = rs->rs_antenna;
+#undef CHAN_HT
+#undef CHAN_HT20
+#undef CHAN_HT40U
+#undef CHAN_HT40D
+}
+
+static void
+ath_handle_micerror(struct ieee80211com *ic,
+	struct ieee80211_frame *wh, int keyix)
+{
+	struct ieee80211_node *ni;
+
+	/* XXX recheck MIC to deal w/ chips that lie */
+	/* XXX discard MIC errors on !data frames */
+	ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) wh);
+	if (ni != NULL) {
+		ieee80211_notify_michael_failure(ni->ni_vap, wh, keyix);
+		ieee80211_free_node(ni);
+	}
+}
+
+int
+ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status,
+    uint64_t tsf, int nf, HAL_RX_QUEUE qtype, struct ath_buf *bf)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct mbuf *m = bf->bf_m;
+	uint64_t rstamp;
+	int len, type;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ieee80211com *ic = ifp->if_l2com;
+	struct ieee80211_node *ni;
+	int is_good = 0;
+	struct ath_rx_edma *re = &sc->sc_rxedma[qtype];
+
+	/*
+	 * Calculate the correct 64 bit TSF given
+	 * the TSF64 register value and rs_tstamp.
+	 */
+	rstamp = ath_extend_tsf(sc, rs->rs_tstamp, tsf);
+
+	/* These aren't specifically errors */
+#ifdef	AH_SUPPORT_AR5416
+	if (rs->rs_flags & HAL_RX_GI)
+		sc->sc_stats.ast_rx_halfgi++;
+	if (rs->rs_flags & HAL_RX_2040)
+		sc->sc_stats.ast_rx_2040++;
+	if (rs->rs_flags & HAL_RX_DELIM_CRC_PRE)
+		sc->sc_stats.ast_rx_pre_crc_err++;
+	if (rs->rs_flags & HAL_RX_DELIM_CRC_POST)
+		sc->sc_stats.ast_rx_post_crc_err++;
+	if (rs->rs_flags & HAL_RX_DECRYPT_BUSY)
+		sc->sc_stats.ast_rx_decrypt_busy_err++;
+	if (rs->rs_flags & HAL_RX_HI_RX_CHAIN)
+		sc->sc_stats.ast_rx_hi_rx_chain++;
+#endif /* AH_SUPPORT_AR5416 */
+
+	if (rs->rs_status != 0) {
+		if (rs->rs_status & HAL_RXERR_CRC)
+			sc->sc_stats.ast_rx_crcerr++;
+		if (rs->rs_status & HAL_RXERR_FIFO)
+			sc->sc_stats.ast_rx_fifoerr++;
+		if (rs->rs_status & HAL_RXERR_PHY) {
+			sc->sc_stats.ast_rx_phyerr++;
+			/* Process DFS radar events */
+			if ((rs->rs_phyerr == HAL_PHYERR_RADAR) ||
+			    (rs->rs_phyerr == HAL_PHYERR_FALSE_RADAR_EXT)) {
+				/* Since we're touching the frame data, sync it */
+				bus_dmamap_sync(sc->sc_dmat,
+				    bf->bf_dmamap,
+				    BUS_DMASYNC_POSTREAD);
+				/* Now pass it to the radar processing code */
+				ath_dfs_process_phy_err(sc, m, rstamp, rs);
+			}
+
+			/* Be suitably paranoid about receiving phy errors out of the stats array bounds */
+			if (rs->rs_phyerr < 64)
+				sc->sc_stats.ast_rx_phy[rs->rs_phyerr]++;
+			goto rx_error;	/* NB: don't count in ierrors */
+		}
+		if (rs->rs_status & HAL_RXERR_DECRYPT) {
+			/*
+			 * Decrypt error.  If the error occurred
+			 * because there was no hardware key, then
+			 * let the frame through so the upper layers
+			 * can process it.  This is necessary for 5210
+			 * parts which have no way to setup a ``clear''
+			 * key cache entry.
+			 *
+			 * XXX do key cache faulting
+			 */
+			if (rs->rs_keyix == HAL_RXKEYIX_INVALID)
+				goto rx_accept;
+			sc->sc_stats.ast_rx_badcrypt++;
+		}
+		/*
+		 * Similar as above - if the failure was a keymiss
+		 * just punt it up to the upper layers for now.
+		 */
+		if (rs->rs_status & HAL_RXERR_KEYMISS) {
+			sc->sc_stats.ast_rx_keymiss++;
+			goto rx_accept;
+		}
+		if (rs->rs_status & HAL_RXERR_MIC) {
+			sc->sc_stats.ast_rx_badmic++;
+			/*
+			 * Do minimal work required to hand off
+			 * the 802.11 header for notification.
+			 */
+			/* XXX frag's and qos frames */
+			len = rs->rs_datalen;
+			if (len >= sizeof (struct ieee80211_frame)) {
+				bus_dmamap_sync(sc->sc_dmat,
+				    bf->bf_dmamap,
+				    BUS_DMASYNC_POSTREAD);
+				ath_handle_micerror(ic,
+				    mtod(m, struct ieee80211_frame *),
+				    sc->sc_splitmic ?
+					rs->rs_keyix-32 : rs->rs_keyix);
+			}
+		}
+		ifp->if_ierrors++;
+rx_error:
+		/*
+		 * Cleanup any pending partial frame.
+		 */
+		if (re->m_rxpending != NULL) {
+			m_freem(re->m_rxpending);
+			re->m_rxpending = NULL;
+		}
+		/*
+		 * When a tap is present pass error frames
+		 * that have been requested.  By default we
+		 * pass decrypt+mic errors but others may be
+		 * interesting (e.g. crc).
+		 */
+		if (ieee80211_radiotap_active(ic) &&
+		    (rs->rs_status & sc->sc_monpass)) {
+			bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+			    BUS_DMASYNC_POSTREAD);
+			/* NB: bpf needs the mbuf length setup */
+			len = rs->rs_datalen;
+			m->m_pkthdr.len = m->m_len = len;
+			bf->bf_m = NULL;
+			ath_rx_tap(ifp, m, rs, rstamp, nf);
+#ifdef	ATH_ENABLE_RADIOTAP_VENDOR_EXT
+			ath_rx_tap_vendor(ifp, m, rs, rstamp, nf);
+#endif	/* ATH_ENABLE_RADIOTAP_VENDOR_EXT */
+			ieee80211_radiotap_rx_all(ic, m);
+			m_freem(m);
+		}
+		/* XXX pass MIC errors up for s/w reclaculation */
+		goto rx_next;
+	}
+rx_accept:
+	/*
+	 * Sync and unmap the frame.  At this point we're
+	 * committed to passing the mbuf somewhere so clear
+	 * bf_m; this means a new mbuf must be allocated
+	 * when the rx descriptor is setup again to receive
+	 * another frame.
+	 */
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTREAD);
+	bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+	bf->bf_m = NULL;
+
+	len = rs->rs_datalen;
+	m->m_len = len;
+
+	if (rs->rs_more) {
+		/*
+		 * Frame spans multiple descriptors; save
+		 * it for the next completed descriptor, it
+		 * will be used to construct a jumbogram.
+		 */
+		if (re->m_rxpending != NULL) {
+			/* NB: max frame size is currently 2 clusters */
+			sc->sc_stats.ast_rx_toobig++;
+			m_freem(re->m_rxpending);
+		}
+		m->m_pkthdr.rcvif = ifp;
+		m->m_pkthdr.len = len;
+		re->m_rxpending = m;
+		goto rx_next;
+	} else if (re->m_rxpending != NULL) {
+		/*
+		 * This is the second part of a jumbogram,
+		 * chain it to the first mbuf, adjust the
+		 * frame length, and clear the rxpending state.
+		 */
+		re->m_rxpending->m_next = m;
+		re->m_rxpending->m_pkthdr.len += len;
+		m = re->m_rxpending;
+		re->m_rxpending = NULL;
+	} else {
+		/*
+		 * Normal single-descriptor receive; setup
+		 * the rcvif and packet length.
+		 */
+		m->m_pkthdr.rcvif = ifp;
+		m->m_pkthdr.len = len;
+	}
+
+	/*
+	 * Validate rs->rs_antenna.
+	 *
+	 * Some users w/ AR9285 NICs have reported crashes
+	 * here because rs_antenna field is bogusly large.
+	 * Let's enforce the maximum antenna limit of 8
+	 * (and it shouldn't be hard coded, but that's a
+	 * separate problem) and if there's an issue, print
+	 * out an error and adjust rs_antenna to something
+	 * sensible.
+	 *
+	 * This code should be removed once the actual
+	 * root cause of the issue has been identified.
+	 * For example, it may be that the rs_antenna
+	 * field is only valid for the lsat frame of
+	 * an aggregate and it just happens that it is
+	 * "mostly" right. (This is a general statement -
+	 * the majority of the statistics are only valid
+	 * for the last frame in an aggregate.
+	 */
+	if (rs->rs_antenna > 7) {
+		device_printf(sc->sc_dev, "%s: rs_antenna > 7 (%d)\n",
+		    __func__, rs->rs_antenna);
+#ifdef	ATH_DEBUG
+		ath_printrxbuf(sc, bf, 0, status == HAL_OK);
+#endif /* ATH_DEBUG */
+		rs->rs_antenna = 0;	/* XXX better than nothing */
+	}
+
+	ifp->if_ipackets++;
+	sc->sc_stats.ast_ant_rx[rs->rs_antenna]++;
+
+	/*
+	 * Populate the rx status block.  When there are bpf
+	 * listeners we do the additional work to provide
+	 * complete status.  Otherwise we fill in only the
+	 * material required by ieee80211_input.  Note that
+	 * noise setting is filled in above.
+	 */
+	if (ieee80211_radiotap_active(ic)) {
+		ath_rx_tap(ifp, m, rs, rstamp, nf);
+#ifdef	ATH_ENABLE_RADIOTAP_VENDOR_EXT
+		ath_rx_tap_vendor(ifp, m, rs, rstamp, nf);
+#endif	/* ATH_ENABLE_RADIOTAP_VENDOR_EXT */
+	}
+
+	/*
+	 * From this point on we assume the frame is at least
+	 * as large as ieee80211_frame_min; verify that.
+	 */
+	if (len < IEEE80211_MIN_LEN) {
+		if (!ieee80211_radiotap_active(ic)) {
+			DPRINTF(sc, ATH_DEBUG_RECV,
+			    "%s: short packet %d\n", __func__, len);
+			sc->sc_stats.ast_rx_tooshort++;
+		} else {
+			/* NB: in particular this captures ack's */
+			ieee80211_radiotap_rx_all(ic, m);
+		}
+		m_freem(m);
+		goto rx_next;
+	}
+
+	if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
+		const HAL_RATE_TABLE *rt = sc->sc_currates;
+		uint8_t rix = rt->rateCodeToIndex[rs->rs_rate];
+
+		ieee80211_dump_pkt(ic, mtod(m, caddr_t), len,
+		    sc->sc_hwmap[rix].ieeerate, rs->rs_rssi);
+	}
+
+	m_adj(m, -IEEE80211_CRC_LEN);
+
+	/*
+	 * Locate the node for sender, track state, and then
+	 * pass the (referenced) node up to the 802.11 layer
+	 * for its use.
+	 */
+	ni = ieee80211_find_rxnode_withkey(ic,
+		mtod(m, const struct ieee80211_frame_min *),
+		rs->rs_keyix == HAL_RXKEYIX_INVALID ?
+			IEEE80211_KEYIX_NONE : rs->rs_keyix);
+	sc->sc_lastrs = rs;
+
+#ifdef	AH_SUPPORT_AR5416
+	if (rs->rs_isaggr)
+		sc->sc_stats.ast_rx_agg++;
+#endif /* AH_SUPPORT_AR5416 */
+
+	if (ni != NULL) {
+		/*
+		 * Only punt packets for ampdu reorder processing for
+		 * 11n nodes; net80211 enforces that M_AMPDU is only
+		 * set for 11n nodes.
+		 */
+		if (ni->ni_flags & IEEE80211_NODE_HT)
+			m->m_flags |= M_AMPDU;
+
+		/*
+		 * Sending station is known, dispatch directly.
+		 */
+		type = ieee80211_input(ni, m, rs->rs_rssi, nf);
+		ieee80211_free_node(ni);
+		/*
+		 * Arrange to update the last rx timestamp only for
+		 * frames from our ap when operating in station mode.
+		 * This assumes the rx key is always setup when
+		 * associated.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_STA &&
+		    rs->rs_keyix != HAL_RXKEYIX_INVALID)
+			is_good = 1;
+	} else {
+		type = ieee80211_input_all(ic, m, rs->rs_rssi, nf);
+	}
+	/*
+	 * Track rx rssi and do any rx antenna management.
+	 */
+	ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi);
+	if (sc->sc_diversity) {
+		/*
+		 * When using fast diversity, change the default rx
+		 * antenna if diversity chooses the other antenna 3
+		 * times in a row.
+		 */
+		if (sc->sc_defant != rs->rs_antenna) {
+			if (++sc->sc_rxotherant >= 3)
+				ath_setdefantenna(sc, rs->rs_antenna);
+		} else
+			sc->sc_rxotherant = 0;
+	}
+
+	/* Newer school diversity - kite specific for now */
+	/* XXX perhaps migrate the normal diversity code to this? */
+	if ((ah)->ah_rxAntCombDiversity)
+		(*(ah)->ah_rxAntCombDiversity)(ah, rs, ticks, hz);
+
+	if (sc->sc_softled) {
+		/*
+		 * Blink for any data frame.  Otherwise do a
+		 * heartbeat-style blink when idle.  The latter
+		 * is mainly for station mode where we depend on
+		 * periodic beacon frames to trigger the poll event.
+		 */
+		if (type == IEEE80211_FC0_TYPE_DATA) {
+			const HAL_RATE_TABLE *rt = sc->sc_currates;
+			ath_led_event(sc,
+			    rt->rateCodeToIndex[rs->rs_rate]);
+		} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
+			ath_led_event(sc, 0);
+		}
+rx_next:
+	return (is_good);
+}
+
+static void
+ath_rx_proc(struct ath_softc *sc, int resched)
+{
+#define	PA2DESC(_sc, _pa) \
+	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+	struct ath_buf *bf;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ath_hal *ah = sc->sc_ah;
+#ifdef IEEE80211_SUPPORT_SUPERG
+	struct ieee80211com *ic = ifp->if_l2com;
+#endif
+	struct ath_desc *ds;
+	struct ath_rx_status *rs;
+	struct mbuf *m;
+	int ngood;
+	HAL_STATUS status;
+	int16_t nf;
+	u_int64_t tsf;
+	int npkts = 0;
+
+	/* XXX we must not hold the ATH_LOCK here */
+	ATH_UNLOCK_ASSERT(sc);
+	ATH_PCU_UNLOCK_ASSERT(sc);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_rxproc_cnt++;
+	ATH_PCU_UNLOCK(sc);
+
+	DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__);
+	ngood = 0;
+	nf = ath_hal_getchannoise(ah, sc->sc_curchan);
+	sc->sc_stats.ast_rx_noise = nf;
+	tsf = ath_hal_gettsf64(ah);
+	do {
+		bf = TAILQ_FIRST(&sc->sc_rxbuf);
+		if (sc->sc_rxslink && bf == NULL) {	/* NB: shouldn't happen */
+			if_printf(ifp, "%s: no buffer!\n", __func__);
+			break;
+		} else if (bf == NULL) {
+			/*
+			 * End of List:
+			 * this can happen for non-self-linked RX chains
+			 */
+			sc->sc_stats.ast_rx_hitqueueend++;
+			break;
+		}
+		m = bf->bf_m;
+		if (m == NULL) {		/* NB: shouldn't happen */
+			/*
+			 * If mbuf allocation failed previously there
+			 * will be no mbuf; try again to re-populate it.
+			 */
+			/* XXX make debug msg */
+			if_printf(ifp, "%s: no mbuf!\n", __func__);
+			TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list);
+			goto rx_proc_next;
+		}
+		ds = bf->bf_desc;
+		if (ds->ds_link == bf->bf_daddr) {
+			/* NB: never process the self-linked entry at the end */
+			sc->sc_stats.ast_rx_hitqueueend++;
+			break;
+		}
+		/* XXX sync descriptor memory */
+		/*
+		 * Must provide the virtual address of the current
+		 * descriptor, the physical address, and the virtual
+		 * address of the next descriptor in the h/w chain.
+		 * This allows the HAL to look ahead to see if the
+		 * hardware is done with a descriptor by checking the
+		 * done bit in the following descriptor and the address
+		 * of the current descriptor the DMA engine is working
+		 * on.  All this is necessary because of our use of
+		 * a self-linked list to avoid rx overruns.
+		 */
+		rs = &bf->bf_status.ds_rxstat;
+		status = ath_hal_rxprocdesc(ah, ds,
+				bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs);
+#ifdef ATH_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
+			ath_printrxbuf(sc, bf, 0, status == HAL_OK);
+#endif
+		if (status == HAL_EINPROGRESS)
+			break;
+
+		TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list);
+		npkts++;
+
+		/*
+		 * Process a single frame.
+		 */
+		if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf))
+			ngood++;
+rx_proc_next:
+		TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+	} while (ath_rxbuf_init(sc, bf) == 0);
+
+	/* rx signal state monitoring */
+	ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan);
+	if (ngood)
+		sc->sc_lastrx = tsf;
+
+	CTR2(ATH_KTR_INTR, "ath_rx_proc: npkts=%d, ngood=%d", npkts, ngood);
+	/* Queue DFS tasklet if needed */
+	if (resched && ath_dfs_tasklet_needed(sc, sc->sc_curchan))
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask);
+
+	/*
+	 * Now that all the RX frames were handled that
+	 * need to be handled, kick the PCU if there's
+	 * been an RXEOL condition.
+	 */
+	ATH_PCU_LOCK(sc);
+	if (resched && sc->sc_kickpcu) {
+		CTR0(ATH_KTR_ERR, "ath_rx_proc: kickpcu");
+		device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n",
+		    __func__, npkts);
+
+		/* XXX rxslink? */
+		/*
+		 * XXX can we hold the PCU lock here?
+		 * Are there any net80211 buffer calls involved?
+		 */
+		bf = TAILQ_FIRST(&sc->sc_rxbuf);
+		ath_hal_putrxbuf(ah, bf->bf_daddr, HAL_RX_QUEUE_HP);
+		ath_hal_rxena(ah);		/* enable recv descriptors */
+		ath_mode_init(sc);		/* set filters, etc. */
+		ath_hal_startpcurecv(ah);	/* re-enable PCU/DMA engine */
+
+		ath_hal_intrset(ah, sc->sc_imask);
+		sc->sc_kickpcu = 0;
+	}
+	ATH_PCU_UNLOCK(sc);
+
+	/* XXX check this inside of IF_LOCK? */
+	if (resched && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
+#ifdef IEEE80211_SUPPORT_SUPERG
+		ieee80211_ff_age_all(ic, 100);
+#endif
+		if (!IFQ_IS_EMPTY(&ifp->if_snd))
+			ath_tx_kick(sc);
+	}
+#undef PA2DESC
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_rxproc_cnt--;
+	ATH_PCU_UNLOCK(sc);
+}
+
+/*
+ * Only run the RX proc if it's not already running.
+ * Since this may get run as part of the reset/flush path,
+ * the task can't clash with an existing, running tasklet.
+ */
+static void
+ath_legacy_rx_tasklet(void *arg, int npending)
+{
+	struct ath_softc *sc = arg;
+
+	CTR1(ATH_KTR_INTR, "ath_rx_proc: pending=%d", npending);
+	DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
+	ATH_PCU_LOCK(sc);
+	if (sc->sc_inreset_cnt > 0) {
+		device_printf(sc->sc_dev,
+		    "%s: sc_inreset_cnt > 0; skipping\n", __func__);
+		ATH_PCU_UNLOCK(sc);
+		return;
+	}
+	ATH_PCU_UNLOCK(sc);
+
+	ath_rx_proc(sc, 1);
+}
+
+static void
+ath_legacy_flushrecv(struct ath_softc *sc)
+{
+
+	ath_rx_proc(sc, 0);
+}
+
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void
+ath_legacy_stoprecv(struct ath_softc *sc, int dodelay)
+{
+#define	PA2DESC(_sc, _pa) \
+	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+	struct ath_hal *ah = sc->sc_ah;
+
+	ath_hal_stoppcurecv(ah);	/* disable PCU */
+	ath_hal_setrxfilter(ah, 0);	/* clear recv filter */
+	ath_hal_stopdmarecv(ah);	/* disable DMA engine */
+	/*
+	 * TODO: see if this particular DELAY() is required; it may be
+	 * masking some missing FIFO flush or DMA sync.
+	 */
+#if 0
+	if (dodelay)
+#endif
+		DELAY(3000);		/* 3ms is long enough for 1 frame */
+#ifdef ATH_DEBUG
+	if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) {
+		struct ath_buf *bf;
+		u_int ix;
+
+		device_printf(sc->sc_dev,
+		    "%s: rx queue %p, link %p\n",
+		    __func__,
+		    (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah, HAL_RX_QUEUE_HP),
+		    sc->sc_rxlink);
+		ix = 0;
+		TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+			struct ath_desc *ds = bf->bf_desc;
+			struct ath_rx_status *rs = &bf->bf_status.ds_rxstat;
+			HAL_STATUS status = ath_hal_rxprocdesc(ah, ds,
+				bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs);
+			if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL))
+				ath_printrxbuf(sc, bf, ix, status == HAL_OK);
+			ix++;
+		}
+	}
+#endif
+	/*
+	 * Free both high/low RX pending, just in case.
+	 */
+	if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) {
+		m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending);
+		sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
+	}
+	if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) {
+		m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending);
+		sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
+	}
+	sc->sc_rxlink = NULL;		/* just in case */
+#undef PA2DESC
+}
+
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int
+ath_legacy_startrecv(struct ath_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_buf *bf;
+
+	sc->sc_rxlink = NULL;
+	sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
+	sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
+	TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+		int error = ath_rxbuf_init(sc, bf);
+		if (error != 0) {
+			DPRINTF(sc, ATH_DEBUG_RECV,
+				"%s: ath_rxbuf_init failed %d\n",
+				__func__, error);
+			return error;
+		}
+	}
+
+	bf = TAILQ_FIRST(&sc->sc_rxbuf);
+	ath_hal_putrxbuf(ah, bf->bf_daddr, HAL_RX_QUEUE_HP);
+	ath_hal_rxena(ah);		/* enable recv descriptors */
+	ath_mode_init(sc);		/* set filters, etc. */
+	ath_hal_startpcurecv(ah);	/* re-enable PCU/DMA engine */
+	return 0;
+}
+
+static int
+ath_legacy_dma_rxsetup(struct ath_softc *sc)
+{
+	int error;
+
+	device_printf(sc->sc_dev, "%s: called\n", __func__);
+
+	error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
+	    "rx", sizeof(struct ath_desc), ath_rxbuf, 1);
+	if (error != 0)
+		return (error);
+
+	return (0);
+}
+
+static int
+ath_legacy_dma_rxteardown(struct ath_softc *sc)
+{
+
+	device_printf(sc->sc_dev, "%s: called\n", __func__);
+	
+	if (sc->sc_rxdma.dd_desc_len != 0)
+		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+	return (0);
+}
+
+void
+ath_recv_setup_legacy(struct ath_softc *sc)
+{
+
+	device_printf(sc->sc_dev, "DMA setup: legacy\n");
+
+	/* Sensible legacy defaults */
+	sc->sc_rx_statuslen = 0;
+
+	sc->sc_rx.recv_start = ath_legacy_startrecv;
+	sc->sc_rx.recv_stop = ath_legacy_stoprecv;
+	sc->sc_rx.recv_flush = ath_legacy_flushrecv;
+	sc->sc_rx.recv_tasklet = ath_legacy_rx_tasklet;
+	sc->sc_rx.recv_rxbuf_init = ath_legacy_rxbuf_init;
+
+	sc->sc_rx.recv_setup = ath_legacy_dma_rxsetup;
+	sc->sc_rx.recv_teardown = ath_legacy_dma_rxteardown;
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_rx.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_rx.h	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: head/sys/dev/ath/if_ath_rx.h 238316 2012-07-10 00:02:19Z adrian $
+ */
+#ifndef	__IF_ATH_RX_H__
+#define	__IF_ATH_RX_H__
+
+extern	u_int32_t ath_calcrxfilter(struct ath_softc *sc);
+extern	void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
+	    int subtype, int rssi, int nf);
+
+#define	ath_stoprecv(_sc, _dodelay)		\
+	    (_sc)->sc_rx.recv_stop((_sc), (_dodelay))
+#define	ath_startrecv(_sc)			\
+	    (_sc)->sc_rx.recv_start((_sc))
+#define	ath_rx_flush(_sc)			\
+	    (_sc)->sc_rx.recv_flush((_sc))
+#define	ath_rxbuf_init(_sc, _bf)		\
+	    (_sc)->sc_rx.recv_rxbuf_init((_sc), (_bf))
+#define	ath_rxdma_setup(_sc)			\
+	    (_sc)->sc_rx.recv_setup(_sc)
+#define	ath_rxdma_teardown(_sc)			\
+	    (_sc)->sc_rx.recv_teardown(_sc)
+
+#if 0
+extern	int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf);
+extern	void ath_rx_tasklet(void *arg, int npending);
+extern	void ath_rx_proc(struct ath_softc *sc, int resched);
+extern	void ath_stoprecv(struct ath_softc *sc, int dodelay);
+extern	int ath_startrecv(struct ath_softc *sc);
+#endif
+
+extern	int ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs,
+	    HAL_STATUS status, uint64_t tsf, int nf, HAL_RX_QUEUE qtype,
+	    struct ath_buf *bf);
+
+extern	void ath_recv_setup_legacy(struct ath_softc *sc);
+
+#endif
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_rx_edma.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_rx_edma.c	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,849 @@
+/*-
+ * Copyright (c) 2012 Adrian Chadd <adrian at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_rx_edma.c 238710 2012-07-23 03:52:18Z adrian $");
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+
+#include "opt_inet.h"
+#include "opt_ath.h"
+/*
+ * This is needed for register operations which are performed
+ * by the driver - eg, calls to ath_hal_gettsf32().
+ *
+ * It's also required for any AH_DEBUG checks in here, eg the
+ * module dependencies.
+ */
+#include "opt_ah.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/callout.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/priv.h>
+#include <sys/module.h>
+#include <sys/ktr.h>
+#include <sys/smp.h>	/* for mp_ncpus */
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
+#ifdef IEEE80211_SUPPORT_TDMA
+#include <net80211/ieee80211_tdma.h>
+#endif
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/ath/if_athvar.h>
+#include <dev/ath/ath_hal/ah_devid.h>		/* XXX for softled */
+#include <dev/ath/ath_hal/ah_diagcodes.h>
+
+#include <dev/ath/if_ath_debug.h>
+#include <dev/ath/if_ath_misc.h>
+#include <dev/ath/if_ath_tsf.h>
+#include <dev/ath/if_ath_tx.h>
+#include <dev/ath/if_ath_sysctl.h>
+#include <dev/ath/if_ath_led.h>
+#include <dev/ath/if_ath_keycache.h>
+#include <dev/ath/if_ath_rx.h>
+#include <dev/ath/if_ath_beacon.h>
+#include <dev/ath/if_athdfs.h>
+
+#ifdef ATH_TX99_DIAG
+#include <dev/ath/ath_tx99/ath_tx99.h>
+#endif
+
+#include <dev/ath/if_ath_rx_edma.h>
+
+/*
+ * some general macros
+  */
+#define	INCR(_l, _sz)		(_l) ++; (_l) &= ((_sz) - 1)
+#define	DECR(_l, _sz)		(_l) --; (_l) &= ((_sz) - 1)
+
+MALLOC_DECLARE(M_ATHDEV);
+
+/*
+ * XXX TODO:
+ *
+ * + Add an RX lock, just to ensure we don't have things clash;
+ * + Make sure the FIFO is correctly flushed and reinitialised
+ *   through a reset;
+ * + Handle the "kickpcu" state where the FIFO overflows.
+ * + Implement a "flush" routine, which doesn't push any
+ *   new frames into the FIFO.
+ * + Verify multi-descriptor frames work!
+ * + There's a "memory use after free" which needs to be tracked down
+ *   and fixed ASAP.  I've seen this in the legacy path too, so it
+ *   may be a generic RX path issue.
+ */
+
+/*
+ * XXX shuffle the function orders so these pre-declarations aren't
+ * required!
+ */
+static	int ath_edma_rxfifo_alloc(struct ath_softc *sc, HAL_RX_QUEUE qtype,
+	    int nbufs);
+static	int ath_edma_rxfifo_flush(struct ath_softc *sc, HAL_RX_QUEUE qtype);
+static	void ath_edma_rxbuf_free(struct ath_softc *sc, struct ath_buf *bf);
+static	int ath_edma_recv_proc_queue(struct ath_softc *sc,
+	    HAL_RX_QUEUE qtype, int dosched);
+
+static void
+ath_edma_stoprecv(struct ath_softc *sc, int dodelay)
+{
+	struct ath_hal *ah = sc->sc_ah;
+
+	ATH_RX_LOCK(sc);
+	ath_hal_stoppcurecv(ah);
+	ath_hal_setrxfilter(ah, 0);
+	ath_hal_stopdmarecv(ah);
+
+	DELAY(3000);
+
+	/* Flush RX pending for each queue */
+	/* XXX should generic-ify this */
+	if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending) {
+		m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending);
+		sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL;
+	}
+
+	if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending) {
+		m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending);
+		sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL;
+	}
+	ATH_RX_UNLOCK(sc);
+}
+
+/*
+ * Re-initialise the FIFO given the current buffer contents.
+ * Specifically, walk from head -> tail, pushing the FIFO contents
+ * back into the FIFO.
+ */
+static void
+ath_edma_reinit_fifo(struct ath_softc *sc, HAL_RX_QUEUE qtype)
+{
+	struct ath_rx_edma *re = &sc->sc_rxedma[qtype];
+	struct ath_buf *bf;
+	int i, j;
+
+	ATH_RX_LOCK_ASSERT(sc);
+
+	i = re->m_fifo_head;
+	for (j = 0; j < re->m_fifo_depth; j++) {
+		bf = re->m_fifo[i];
+		DPRINTF(sc, ATH_DEBUG_EDMA_RX,
+		    "%s: Q%d: pos=%i, addr=0x%jx\n",
+		    __func__,
+		    qtype,
+		    i,
+		    (uintmax_t)bf->bf_daddr);
+		ath_hal_putrxbuf(sc->sc_ah, bf->bf_daddr, qtype);
+		INCR(i, re->m_fifolen);
+	}
+
+	/* Ensure this worked out right */
+	if (i != re->m_fifo_tail) {
+		device_printf(sc->sc_dev, "%s: i (%d) != tail! (%d)\n",
+		    __func__,
+		    i,
+		    re->m_fifo_tail);
+	}
+}
+
+/*
+ * Start receive.
+ *
+ * XXX TODO: this needs to reallocate the FIFO entries when a reset
+ * occurs, in case the FIFO is filled up and no new descriptors get
+ * thrown into the FIFO.
+ */
+static int
+ath_edma_startrecv(struct ath_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_ah;
+
+	ATH_RX_LOCK(sc);
+
+	/* Enable RX FIFO */
+	ath_hal_rxena(ah);
+
+	/*
+	 * Entries should only be written out if the
+	 * FIFO is empty.
+	 *
+	 * XXX This isn't correct. I should be looking
+	 * at the value of AR_RXDP_SIZE (0x0070) to determine
+	 * how many entries are in here.
+	 *
+	 * A warm reset will clear the registers but not the FIFO.
+	 *
+	 * And I believe this is actually the address of the last
+	 * handled buffer rather than the current FIFO pointer.
+	 * So if no frames have been (yet) seen, we'll reinit the
+	 * FIFO.
+	 *
+	 * I'll chase that up at some point.
+	 */
+	if (ath_hal_getrxbuf(sc->sc_ah, HAL_RX_QUEUE_HP) == 0) {
+		DPRINTF(sc, ATH_DEBUG_EDMA_RX,
+		    "%s: Re-initing HP FIFO\n", __func__);
+		ath_edma_reinit_fifo(sc, HAL_RX_QUEUE_HP);
+	}
+	if (ath_hal_getrxbuf(sc->sc_ah, HAL_RX_QUEUE_LP) == 0) {
+		DPRINTF(sc, ATH_DEBUG_EDMA_RX,
+		    "%s: Re-initing LP FIFO\n", __func__);
+		ath_edma_reinit_fifo(sc, HAL_RX_QUEUE_LP);
+	}
+
+	/* Add up to m_fifolen entries in each queue */
+	/*
+	 * These must occur after the above write so the FIFO buffers
+	 * are pushed/tracked in the same order as the hardware will
+	 * process them.
+	 */
+	ath_edma_rxfifo_alloc(sc, HAL_RX_QUEUE_HP,
+	    sc->sc_rxedma[HAL_RX_QUEUE_HP].m_fifolen);
+
+	ath_edma_rxfifo_alloc(sc, HAL_RX_QUEUE_LP,
+	    sc->sc_rxedma[HAL_RX_QUEUE_LP].m_fifolen);
+
+	ath_mode_init(sc);
+	ath_hal_startpcurecv(ah);
+
+	ATH_RX_UNLOCK(sc);
+
+	return (0);
+}
+
+static void
+ath_edma_recv_flush(struct ath_softc *sc)
+{
+
+	device_printf(sc->sc_dev, "%s: called\n", __func__);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_rxproc_cnt++;
+	ATH_PCU_UNLOCK(sc);
+
+	ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 0);
+	ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 0);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_rxproc_cnt--;
+	ATH_PCU_UNLOCK(sc);
+}
+
+/*
+ * Process frames from the current queue.
+ *
+ * TODO:
+ *
+ * + Add a "dosched" flag, so we don't reschedule any FIFO frames
+ *   to the hardware or re-kick the PCU after 'kickpcu' is set.
+ *
+ * + Perhaps split "check FIFO contents" and "handle frames", so
+ *   we can run the "check FIFO contents" in ath_intr(), but
+ *   "handle frames" in the RX tasklet.
+ */
+static int
+ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype,
+    int dosched)
+{
+	struct ath_rx_edma *re = &sc->sc_rxedma[qtype];
+	struct ath_rx_status *rs;
+	struct ath_desc *ds;
+	struct ath_buf *bf;
+	struct mbuf *m;
+	struct ath_hal *ah = sc->sc_ah;
+	uint64_t tsf;
+	int16_t nf;
+	int ngood = 0, npkts = 0;
+	ath_bufhead rxlist;
+	struct ath_buf *next;
+
+	TAILQ_INIT(&rxlist);
+
+	tsf = ath_hal_gettsf64(ah);
+	nf = ath_hal_getchannoise(ah, sc->sc_curchan);
+	sc->sc_stats.ast_rx_noise = nf;
+
+	ATH_RX_LOCK(sc);
+
+	do {
+		bf = re->m_fifo[re->m_fifo_head];
+		/* This shouldn't occur! */
+		if (bf == NULL) {
+			device_printf(sc->sc_dev, "%s: Q%d: NULL bf?\n",
+			    __func__,
+			    qtype);
+			break;
+		}
+		m = bf->bf_m;
+		ds = bf->bf_desc;
+
+		/*
+		 * Sync descriptor memory - this also syncs the buffer for us.
+		 *
+		 * EDMA descriptors are in cached memory.
+		 */
+		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+		    BUS_DMASYNC_POSTREAD);
+		rs = &bf->bf_status.ds_rxstat;
+		bf->bf_rxstatus = ath_hal_rxprocdesc(ah, ds, bf->bf_daddr,
+		    NULL, rs);
+#ifdef	ATH_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
+			ath_printrxbuf(sc, bf, 0, bf->bf_rxstatus == HAL_OK);
+#endif
+		if (bf->bf_rxstatus == HAL_EINPROGRESS)
+			break;
+
+		/*
+		 * Completed descriptor.
+		 *
+		 * In the future we'll call ath_rx_pkt(), but it first
+		 * has to be taught about EDMA RX queues (so it can
+		 * access sc_rxpending correctly.)
+		 */
+		DPRINTF(sc, ATH_DEBUG_EDMA_RX,
+		    "%s: Q%d: completed!\n", __func__, qtype);
+		npkts++;
+
+		/*
+		 * Remove the FIFO entry and place it on the completion
+		 * queue.
+		 */
+		re->m_fifo[re->m_fifo_head] = NULL;
+		TAILQ_INSERT_TAIL(&rxlist, bf, bf_list);
+
+		/* Bump the descriptor FIFO stats */
+		INCR(re->m_fifo_head, re->m_fifolen);
+		re->m_fifo_depth--;
+		/* XXX check it doesn't fall below 0 */
+	} while (re->m_fifo_depth > 0);
+
+	/* Append some more fresh frames to the FIFO */
+	if (dosched)
+		ath_edma_rxfifo_alloc(sc, qtype, re->m_fifolen);
+
+	ATH_RX_UNLOCK(sc);
+
+	/* Handle the completed descriptors */
+	TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) {
+		/*
+		 * Skip the RX descriptor status - start at the data offset
+		 */
+		m_adj(bf->bf_m, sc->sc_rx_statuslen);
+
+		/* Handle the frame */
+		/*
+		 * Note: this may or may not free bf->bf_m and sync/unmap
+		 * the frame.
+		 */
+		rs = &bf->bf_status.ds_rxstat;
+		if (ath_rx_pkt(sc, rs, bf->bf_rxstatus, tsf, nf, qtype, bf))
+			ngood++;
+	}
+
+	/* Free in one set, inside the lock */
+	ATH_RX_LOCK(sc);
+	TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) {
+		/* Free the buffer/mbuf */
+		ath_edma_rxbuf_free(sc, bf);
+	}
+	ATH_RX_UNLOCK(sc);
+
+	/* rx signal state monitoring */
+	ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan);
+	if (ngood)
+		sc->sc_lastrx = tsf;
+
+	CTR2(ATH_KTR_INTR, "ath edma rx proc: npkts=%d, ngood=%d",
+	    npkts, ngood);
+
+	/* Handle resched and kickpcu appropriately */
+	ATH_PCU_LOCK(sc);
+	if (dosched && sc->sc_kickpcu) {
+		CTR0(ATH_KTR_ERR, "ath_edma_recv_proc_queue(): kickpcu");
+		device_printf(sc->sc_dev,
+		    "%s: handled npkts %d ngood %d\n",
+		    __func__, npkts, ngood);
+
+		/*
+		 * XXX TODO: what should occur here? Just re-poke and
+		 * re-enable the RX FIFO?
+		 */
+		sc->sc_kickpcu = 0;
+	}
+	ATH_PCU_UNLOCK(sc);
+
+	return (ngood);
+}
+
+static void
+ath_edma_recv_tasklet(void *arg, int npending)
+{
+	struct ath_softc *sc = (struct ath_softc *) arg;
+	struct ifnet *ifp = sc->sc_ifp;
+#ifdef	IEEE80211_SUPPORT_SUPERG
+	struct ieee80211com *ic = ifp->if_l2com;
+#endif
+
+	DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: called; npending=%d\n",
+	    __func__,
+	    npending);
+
+	ATH_PCU_LOCK(sc);
+	if (sc->sc_inreset_cnt > 0) {
+		device_printf(sc->sc_dev, "%s: sc_inreset_cnt > 0; skipping\n",
+		    __func__);
+		ATH_PCU_UNLOCK(sc);
+		return;
+	}
+	sc->sc_rxproc_cnt++;
+	ATH_PCU_UNLOCK(sc);
+
+	ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 1);
+	ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 1);
+
+	/* XXX inside IF_LOCK ? */
+	if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
+#ifdef	IEEE80211_SUPPORT_SUPERG
+		ieee80211_ff_age_all(ic, 100);
+#endif
+		if (! IFQ_IS_EMPTY(&ifp->if_snd))
+			ath_tx_kick(sc);
+	}
+	if (ath_dfs_tasklet_needed(sc, sc->sc_curchan))
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_dfstask);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_rxproc_cnt--;
+	ATH_PCU_UNLOCK(sc);
+}
+
+/*
+ * Allocate an RX mbuf for the given ath_buf and initialise
+ * it for EDMA.
+ *
+ * + Allocate a 4KB mbuf;
+ * + Setup the DMA map for the given buffer;
+ * + Keep a pointer to the start of the mbuf - that's where the
+ *   descriptor lies;
+ * + Take a pointer to the start of the RX buffer, set the
+ *   mbuf "start" to be there;
+ * + Return that.
+ */
+static int
+ath_edma_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
+{
+
+	struct mbuf *m;
+	int error;
+	int len;
+
+	ATH_RX_LOCK_ASSERT(sc);
+
+//	device_printf(sc->sc_dev, "%s: called; bf=%p\n", __func__, bf);
+
+	m = m_getm(NULL, sc->sc_edma_bufsize, M_DONTWAIT, MT_DATA);
+	if (! m)
+		return (ENOBUFS);		/* XXX ?*/
+
+	/* XXX warn/enforce alignment */
+
+	len = m->m_ext.ext_size;
+#if 0
+	device_printf(sc->sc_dev, "%s: called: m=%p, size=%d, mtod=%p\n",
+	    __func__,
+	    m,
+	    len,
+	    mtod(m, char *));
+#endif
+
+	m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+
+	/*
+	 * Create DMA mapping.
+	 */
+	error = bus_dmamap_load_mbuf_sg(sc->sc_dmat,
+	    bf->bf_dmamap, m, bf->bf_segs, &bf->bf_nseg, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		device_printf(sc->sc_dev, "%s: failed; error=%d\n",
+		    __func__,
+		    error);
+		m_freem(m);
+		return (error);
+	}
+
+	/*
+	 * Populate ath_buf fields.
+	 */
+
+	bf->bf_desc = mtod(m, struct ath_desc *);
+	bf->bf_daddr = bf->bf_segs[0].ds_addr;
+	bf->bf_lastds = bf->bf_desc;	/* XXX only really for TX? */
+	bf->bf_m = m;
+
+	/* Zero the descriptor */
+	memset(bf->bf_desc, '\0', sc->sc_rx_statuslen);
+
+#if 0
+	/*
+	 * Adjust mbuf header and length/size to compensate for the
+	 * descriptor size.
+	 */
+	m_adj(m, sc->sc_rx_statuslen);
+#endif
+
+	/* Finish! */
+
+	return (0);
+}
+
+static struct ath_buf *
+ath_edma_rxbuf_alloc(struct ath_softc *sc)
+{
+	struct ath_buf *bf;
+	int error;
+
+	ATH_RX_LOCK_ASSERT(sc);
+
+	/* Allocate buffer */
+	bf = TAILQ_FIRST(&sc->sc_rxbuf);
+	/* XXX shouldn't happen upon startup? */
+	if (bf == NULL)
+		return (NULL);
+
+	/* Remove it from the free list */
+	TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list);
+
+	/* Assign RX mbuf to it */
+	error = ath_edma_rxbuf_init(sc, bf);
+	if (error != 0) {
+		device_printf(sc->sc_dev,
+		    "%s: bf=%p, rxbuf alloc failed! error=%d\n",
+		    __func__,
+		    bf,
+		    error);
+		TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+		return (NULL);
+	}
+
+	return (bf);
+}
+
+static void
+ath_edma_rxbuf_free(struct ath_softc *sc, struct ath_buf *bf)
+{
+
+	ATH_RX_LOCK_ASSERT(sc);
+
+	/* We're doing this multiple times? */
+	bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+
+	if (bf->bf_m) {
+		m_freem(bf->bf_m);
+		bf->bf_m = NULL;
+	}
+
+	/* XXX lock? */
+	TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+}
+
+/*
+ * Allocate up to 'n' entries and push them onto the hardware FIFO.
+ *
+ * Return how many entries were successfully pushed onto the
+ * FIFO.
+ */
+static int
+ath_edma_rxfifo_alloc(struct ath_softc *sc, HAL_RX_QUEUE qtype, int nbufs)
+{
+	struct ath_rx_edma *re = &sc->sc_rxedma[qtype];
+	struct ath_buf *bf;
+	int i;
+
+	ATH_RX_LOCK_ASSERT(sc);
+
+	/*
+	 * Allocate buffers until the FIFO is full or nbufs is reached.
+	 */
+	for (i = 0; i < nbufs && re->m_fifo_depth < re->m_fifolen; i++) {
+		/* Ensure the FIFO is already blank, complain loudly! */
+		if (re->m_fifo[re->m_fifo_tail] != NULL) {
+			device_printf(sc->sc_dev,
+			    "%s: Q%d: fifo[%d] != NULL (%p)\n",
+			    __func__,
+			    qtype,
+			    re->m_fifo_tail,
+			    re->m_fifo[re->m_fifo_tail]);
+
+			/* Free the slot */
+			ath_edma_rxbuf_free(sc, re->m_fifo[re->m_fifo_tail]);
+			re->m_fifo_depth--;
+			/* XXX check it's not < 0 */
+			re->m_fifo[re->m_fifo_tail] = NULL;
+		}
+
+		bf = ath_edma_rxbuf_alloc(sc);
+		/* XXX should ensure the FIFO is not NULL? */
+		if (bf == NULL) {
+			device_printf(sc->sc_dev, "%s: Q%d: alloc failed?\n",
+			    __func__,
+			    qtype);
+			break;
+		}
+
+		re->m_fifo[re->m_fifo_tail] = bf;
+
+		/*
+		 * Flush the descriptor contents before it's handed to the
+		 * hardware.
+		 */
+		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+		    BUS_DMASYNC_PREREAD);
+
+		/* Write to the RX FIFO */
+		DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: Q%d: putrxbuf=%p\n",
+		    __func__,
+		    qtype,
+		    bf->bf_desc);
+		ath_hal_putrxbuf(sc->sc_ah, bf->bf_daddr, qtype);
+
+		re->m_fifo_depth++;
+		INCR(re->m_fifo_tail, re->m_fifolen);
+	}
+
+	/*
+	 * Return how many were allocated.
+	 */
+	DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: Q%d: nbufs=%d, nalloced=%d\n",
+	    __func__,
+	    qtype,
+	    nbufs,
+	    i);
+	return (i);
+}
+
+static int
+ath_edma_rxfifo_flush(struct ath_softc *sc, HAL_RX_QUEUE qtype)
+{
+	struct ath_rx_edma *re = &sc->sc_rxedma[qtype];
+	int i;
+
+	ATH_RX_LOCK_ASSERT(sc);
+
+	for (i = 0; i < re->m_fifolen; i++) {
+		if (re->m_fifo[i] != NULL) {
+#ifdef	ATH_DEBUG
+			struct ath_buf *bf = re->m_fifo[i];
+
+			if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
+				ath_printrxbuf(sc, bf, 0, HAL_OK);
+#endif
+			ath_edma_rxbuf_free(sc, re->m_fifo[i]);
+			re->m_fifo[i] = NULL;
+			re->m_fifo_depth--;
+		}
+	}
+
+	if (re->m_rxpending != NULL) {
+		m_freem(re->m_rxpending);
+		re->m_rxpending = NULL;
+	}
+	re->m_fifo_head = re->m_fifo_tail = re->m_fifo_depth = 0;
+
+	return (0);
+}
+
+/*
+ * Setup the initial RX FIFO structure.
+ */
+static int
+ath_edma_setup_rxfifo(struct ath_softc *sc, HAL_RX_QUEUE qtype)
+{
+	struct ath_rx_edma *re = &sc->sc_rxedma[qtype];
+
+	ATH_RX_LOCK_ASSERT(sc);
+
+	if (! ath_hal_getrxfifodepth(sc->sc_ah, qtype, &re->m_fifolen)) {
+		device_printf(sc->sc_dev, "%s: qtype=%d, failed\n",
+		    __func__,
+		    qtype);
+		return (-EINVAL);
+	}
+	device_printf(sc->sc_dev, "%s: type=%d, FIFO depth = %d entries\n",
+	    __func__,
+	    qtype,
+	    re->m_fifolen);
+
+	/* Allocate ath_buf FIFO array, pre-zero'ed */
+	re->m_fifo = malloc(sizeof(struct ath_buf *) * re->m_fifolen,
+	    M_ATHDEV,
+	    M_NOWAIT | M_ZERO);
+	if (re->m_fifo == NULL) {
+		device_printf(sc->sc_dev, "%s: malloc failed\n",
+		    __func__);
+		return (-ENOMEM);
+	}
+
+	/*
+	 * Set initial "empty" state.
+	 */
+	re->m_rxpending = NULL;
+	re->m_fifo_head = re->m_fifo_tail = re->m_fifo_depth = 0;
+
+	return (0);
+}
+
+static int
+ath_edma_rxfifo_free(struct ath_softc *sc, HAL_RX_QUEUE qtype)
+{
+	struct ath_rx_edma *re = &sc->sc_rxedma[qtype];
+
+	device_printf(sc->sc_dev, "%s: called; qtype=%d\n",
+	    __func__,
+	    qtype);
+	
+	free(re->m_fifo, M_ATHDEV);
+
+	return (0);
+}
+
+static int
+ath_edma_dma_rxsetup(struct ath_softc *sc)
+{
+	int error;
+
+	/*
+	 * Create RX DMA tag and buffers.
+	 */
+	error = ath_descdma_setup_rx_edma(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
+	    "rx", ath_rxbuf, sc->sc_rx_statuslen);
+	if (error != 0)
+		return error;
+
+	ATH_RX_LOCK(sc);
+	(void) ath_edma_setup_rxfifo(sc, HAL_RX_QUEUE_HP);
+	(void) ath_edma_setup_rxfifo(sc, HAL_RX_QUEUE_LP);
+	ATH_RX_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+ath_edma_dma_rxteardown(struct ath_softc *sc)
+{
+
+	device_printf(sc->sc_dev, "%s: called\n", __func__);
+
+	ATH_RX_LOCK(sc);
+	ath_edma_rxfifo_flush(sc, HAL_RX_QUEUE_HP);
+	ath_edma_rxfifo_free(sc, HAL_RX_QUEUE_HP);
+
+	ath_edma_rxfifo_flush(sc, HAL_RX_QUEUE_LP);
+	ath_edma_rxfifo_free(sc, HAL_RX_QUEUE_LP);
+	ATH_RX_UNLOCK(sc);
+
+	/* Free RX ath_buf */
+	/* Free RX DMA tag */
+	if (sc->sc_rxdma.dd_desc_len != 0)
+		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+
+	return (0);
+}
+
+void
+ath_recv_setup_edma(struct ath_softc *sc)
+{
+
+	device_printf(sc->sc_dev, "DMA setup: EDMA\n");
+
+	/* Set buffer size to 4k */
+	sc->sc_edma_bufsize = 4096;
+
+	/* Fetch EDMA field and buffer sizes */
+	(void) ath_hal_getrxstatuslen(sc->sc_ah, &sc->sc_rx_statuslen);
+
+	/* Configure the hardware with the RX buffer size */
+	(void) ath_hal_setrxbufsize(sc->sc_ah, sc->sc_edma_bufsize -
+	    sc->sc_rx_statuslen);
+
+	device_printf(sc->sc_dev, "RX status length: %d\n",
+	    sc->sc_rx_statuslen);
+	device_printf(sc->sc_dev, "RX buffer size: %d\n",
+	    sc->sc_edma_bufsize);
+
+	sc->sc_rx.recv_stop = ath_edma_stoprecv;
+	sc->sc_rx.recv_start = ath_edma_startrecv;
+	sc->sc_rx.recv_flush = ath_edma_recv_flush;
+	sc->sc_rx.recv_tasklet = ath_edma_recv_tasklet;
+	sc->sc_rx.recv_rxbuf_init = ath_edma_rxbuf_init;
+
+	sc->sc_rx.recv_setup = ath_edma_dma_rxsetup;
+	sc->sc_rx.recv_teardown = ath_edma_dma_rxteardown;
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_rx_edma.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_rx_edma.h	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2012 Adrian Chadd <adrian at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: head/sys/dev/ath/if_ath_rx_edma.h 238055 2012-07-03 06:59:12Z adrian $
+ */
+#ifndef	__IF_ATH_RX_EDMA_H__
+#define	__IF_ATH_RX_EDMA_H__
+
+extern	void ath_recv_setup_edma(struct ath_softc *sc);
+
+#endif
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_sysctl.c
--- a/head/sys/dev/ath/if_ath_sysctl.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_sysctl.c	Wed Jul 25 17:07:47 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_sysctl.c 234090 2012-04-10 07:23:37Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_sysctl.c 238507 2012-07-15 20:51:41Z adrian $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -374,7 +374,20 @@
 		t++;
 	}
 	ATH_TXBUF_UNLOCK(sc);
-	printf("Total TX buffers: %d; Total TX buffers busy: %d\n",
+	printf("Total TX buffers: %d; Total TX buffers busy: %d (%d)\n",
+	    t, i, sc->sc_txbuf_cnt);
+
+	i = t = 0;
+	ATH_TXBUF_LOCK(sc);
+	TAILQ_FOREACH(bf, &sc->sc_txbuf_mgmt, bf_list) {
+		if (bf->bf_flags & ATH_BUF_BUSY) {
+			printf("Busy: %d\n", t);
+			i++;
+		}
+		t++;
+	}
+	ATH_TXBUF_UNLOCK(sc);
+	printf("Total mgmt TX buffers: %d; Total mgmt TX buffers busy: %d\n",
 	    t, i);
 
 	return 0;
@@ -501,8 +514,8 @@
 		"regdomain", CTLFLAG_RD, &sc->sc_eerd, 0,
 		"EEPROM regdomain code");
 #ifdef	ATH_DEBUG
-	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
-		"debug", CTLFLAG_RW, &sc->sc_debug, 0,
+	SYSCTL_ADD_QUAD(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"debug", CTLFLAG_RW, &sc->sc_debug,
 		"control debugging printfs");
 #endif
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
@@ -607,12 +620,11 @@
 		"tid_hwq_hi", CTLFLAG_RW, &sc->sc_tid_hwq_hi, 0,
 		"");
 
-#if 0
 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"txq_data_minfree", CTLFLAG_RW, &sc->sc_txq_data_minfree,
 		0, "Minimum free buffers before adding a data frame"
 		" to the TX queue");
-#endif
+
 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"txq_mcastq_maxdepth", CTLFLAG_RW,
 		&sc->sc_txq_mcastq_maxdepth, 0,
@@ -922,6 +934,8 @@
 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_mcastq_overflow",
 	    CTLFLAG_RD, &sc->sc_stats.ast_tx_mcastq_overflow, 0,
 	    "Number of multicast frames exceeding maximum mcast queue depth");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_keymiss", CTLFLAG_RD,
+	    &sc->sc_stats.ast_rx_keymiss, 0, "");
 	
 	/* Attach the RX phy error array */
 	ath_sysctl_stats_attach_rxphyerr(sc, child);
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_tdma.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_tdma.c	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,476 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_tdma.c 235679 2012-05-20 02:49:42Z adrian $");
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+
+#include "opt_inet.h"
+#include "opt_ath.h"
+/*
+ * This is needed for register operations which are performed
+ * by the driver - eg, calls to ath_hal_gettsf32().
+ *
+ * It's also required for any AH_DEBUG checks in here, eg the
+ * module dependencies.
+ */
+#include "opt_ah.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/callout.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/priv.h>
+#include <sys/module.h>
+#include <sys/ktr.h>
+#include <sys/smp.h>	/* for mp_ncpus */
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
+#ifdef IEEE80211_SUPPORT_TDMA
+#include <net80211/ieee80211_tdma.h>
+#endif
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/ath/if_athvar.h>
+#include <dev/ath/ath_hal/ah_devid.h>		/* XXX for softled */
+#include <dev/ath/ath_hal/ah_diagcodes.h>
+
+#include <dev/ath/if_ath_debug.h>
+#include <dev/ath/if_ath_misc.h>
+#include <dev/ath/if_ath_tsf.h>
+#include <dev/ath/if_ath_tx.h>
+#include <dev/ath/if_ath_sysctl.h>
+#include <dev/ath/if_ath_led.h>
+#include <dev/ath/if_ath_keycache.h>
+#include <dev/ath/if_ath_rx.h>
+#include <dev/ath/if_ath_beacon.h>
+#include <dev/ath/if_athdfs.h>
+
+#ifdef ATH_TX99_DIAG
+#include <dev/ath/ath_tx99/ath_tx99.h>
+#endif
+
+#ifdef IEEE80211_SUPPORT_TDMA
+#include <dev/ath/if_ath_tdma.h>
+
+static void	ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt,
+		    u_int32_t bintval);
+static void	ath_tdma_bintvalsetup(struct ath_softc *sc,
+		    const struct ieee80211_tdma_state *tdma);
+#endif /* IEEE80211_SUPPORT_TDMA */
+
+#ifdef IEEE80211_SUPPORT_TDMA
+static void
+ath_tdma_settimers(struct ath_softc *sc, u_int32_t nexttbtt, u_int32_t bintval)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_BEACON_TIMERS bt;
+
+	bt.bt_intval = bintval | HAL_BEACON_ENA;
+	bt.bt_nexttbtt = nexttbtt;
+	bt.bt_nextdba = (nexttbtt<<3) - sc->sc_tdmadbaprep;
+	bt.bt_nextswba = (nexttbtt<<3) - sc->sc_tdmaswbaprep;
+	bt.bt_nextatim = nexttbtt+1;
+	/* Enables TBTT, DBA, SWBA timers by default */
+	bt.bt_flags = 0;
+	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_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.
+ */
+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;
+		}
+	}
+	/* XXX should take a locked ref to iv_bss */
+	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, tp->ucastrate);
+	else
+		rix = ath_tx_findrix(sc, 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 */
+	if (sc->sc_setcca)
+		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.
+ */
+void
+ath_tdma_update(struct ieee80211_node *ni,
+	const struct ieee80211_tdma_param *tdma, int changed)
+{
+#define	TSF_TO_TU(_h,_l) \
+	((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
+#define	TU_TO_TSF(_tu)	(((u_int64_t)(_tu)) << 10)
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+	struct ath_hal *ah = sc->sc_ah;
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	u_int64_t tsf, rstamp, nextslot, nexttbtt;
+	u_int32_t txtime, nextslottu;
+	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 (changed != 0) {
+		const struct ieee80211_tdma_state *ts = vap->iv_tdma;
+
+		ath_tdma_bintvalsetup(sc, ts);
+		if (changed & TDMA_UPDATE_SLOTLEN)
+			ath_wme_update(ic);
+
+		DPRINTF(sc, ATH_DEBUG_TDMA,
+		    "%s: adopt slot %u slotcnt %u slotlen %u us "
+		    "bintval %u TU\n", __func__,
+		    ts->tdma_slot, ts->tdma_slotcnt, ts->tdma_slotlen,
+		    sc->sc_tdmabintval);
+
+		/* XXX right? */
+		ath_hal_intrset(ah, sc->sc_imask);
+		/* NB: beacon timers programmed below */
+	}
+
+	/* extend rx timestamp to 64 bits */
+	rs = sc->sc_lastrs;
+	tsf = ath_hal_gettsf64(ah);
+	rstamp = ath_extend_tsf(sc, rs->rs_tstamp, 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.
+	 */
+	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;
+
+	/*
+	 * Retrieve the hardware NextTBTT in 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.
+	 */
+	nexttbtt = ath_hal_getnexttbtt(ah);
+	tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD + 1)) - nexttbtt);
+
+	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 - TSF_TO_TU(nexttbtt >> 32, nexttbtt);
+
+	/*
+	 * 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 nexttbtt %llu (%d)\n",
+	    (unsigned long long) tsf, (unsigned long long) nextslot,
+	    (int)(nextslot - tsf), tsfdelta, nextslottu, nexttbtt, 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.
+ */
+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 >= ath_bstuck_threshold)
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_bstucktask);
+		return;
+	}
+	if (sc->sc_bmisscount != 0) {
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+			"%s: resume beacon xmit after %u misses\n",
+			__func__, sc->sc_bmisscount);
+		sc->sc_bmisscount = 0;
+	}
+
+	/*
+	 * 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.
+		 */
+		/* XXX should take a locked ref to iv_bss */
+		vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah);
+	}
+}
+#endif /* IEEE80211_SUPPORT_TDMA */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_tdma.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_tdma.h	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: head/sys/dev/ath/if_ath_tdma.h 235679 2012-05-20 02:49:42Z adrian $
+ */
+#ifndef	__IF_ATH_TDMA_H__
+#define	__IF_ATH_TDMA_H__
+
+#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)
+
+extern	void ath_tdma_config(struct ath_softc *sc, struct ieee80211vap *vap);
+extern	void ath_tdma_update(struct ieee80211_node *ni,
+	    const struct ieee80211_tdma_param *tdma, int changed);
+extern	void ath_tdma_beacon_send(struct ath_softc *sc,
+	    struct ieee80211vap *vap);
+
+#endif
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_tsf.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_tsf.h	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: head/sys/dev/ath/if_ath_tsf.h 235676 2012-05-20 02:05:10Z adrian $
+ */
+#ifndef	__IF_ATH_TSF_H__
+#define	__IF_ATH_TSF_H__
+
+/*
+ * Extend 15-bit time stamp from rx descriptor to
+ * a full 64-bit TSF using the specified TSF.
+ */
+static __inline u_int64_t
+ath_extend_tsf15(u_int32_t rstamp, u_int64_t tsf)
+{
+	if ((tsf & 0x7fff) < rstamp)
+		tsf -= 0x8000;
+
+	return ((tsf &~ 0x7fff) | rstamp);
+}
+
+/*
+ * Extend 32-bit time stamp from rx descriptor to
+ * a full 64-bit TSF using the specified TSF.
+ */
+static __inline u_int64_t
+ath_extend_tsf32(u_int32_t rstamp, u_int64_t tsf)
+{
+	u_int32_t tsf_low = tsf & 0xffffffff;
+	u_int64_t tsf64 = (tsf & ~0xffffffffULL) | rstamp;
+
+	if (rstamp > tsf_low && (rstamp - tsf_low > 0x10000000))
+		tsf64 -= 0x100000000ULL;
+
+	if (rstamp < tsf_low && (tsf_low - rstamp > 0x10000000))
+		tsf64 += 0x100000000ULL;
+
+	return tsf64;
+}
+
+/*
+ * Extend the TSF from the RX descriptor to a full 64 bit TSF.
+ * Earlier hardware versions only wrote the low 15 bits of the
+ * TSF into the RX descriptor; later versions (AR5416 and up)
+ * include the 32 bit TSF value.
+ */
+static __inline u_int64_t
+ath_extend_tsf(struct ath_softc *sc, u_int32_t rstamp, u_int64_t tsf)
+{
+	if (sc->sc_rxtsf32)
+		return ath_extend_tsf32(rstamp, tsf);
+	else
+		return ath_extend_tsf15(rstamp, tsf);
+}
+
+#endif
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_tx.c
--- a/head/sys/dev/ath/if_ath_tx.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_tx.c	Wed Jul 25 17:07:47 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_tx.c 234324 2012-04-15 20:29:39Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_tx.c 238729 2012-07-23 23:40:13Z adrian $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -109,10 +109,10 @@
     int tid);
 static int ath_tx_ampdu_running(struct ath_softc *sc, struct ath_node *an,
     int tid);
+static ieee80211_seq ath_tx_tid_seqno_assign(struct ath_softc *sc,
+    struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0);
 static int ath_tx_action_frame_override_queue(struct ath_softc *sc,
     struct ieee80211_node *ni, struct mbuf *m0, int *tid);
-static int ath_tx_seqno_required(struct ath_softc *sc,
-    struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0);
 
 /*
  * Whether to use the 11n rate scenario functions or not
@@ -184,7 +184,7 @@
 	TAILQ_FOREACH_SAFE(bf, frags, bf_list, next) {
 		/* NB: bf assumed clean */
 		TAILQ_REMOVE(frags, bf, bf_list);
-		TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+		ath_returnbuf_head(sc, bf);
 		ieee80211_node_decref(ni);
 	}
 }
@@ -203,7 +203,8 @@
 
 	ATH_TXBUF_LOCK(sc);
 	for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) {
-		bf = _ath_getbuf_locked(sc);
+		/* XXX non-management? */
+		bf = _ath_getbuf_locked(sc, ATH_BUFTYPE_NORMAL);
 		if (bf == NULL) {	/* out of buffers, cleanup */
 			device_printf(sc->sc_dev, "%s: no buffer?\n",
 			    __func__);
@@ -301,6 +302,11 @@
 	struct ath_hal *ah = sc->sc_ah;
 	struct ath_desc *ds, *ds0;
 	int i;
+	/*
+	 * XXX There's txdma and txdma_mgmt; the descriptor
+	 * sizes must match.
+	 */
+	struct ath_descdma *dd = &sc->sc_txdma;
 
 	/*
 	 * Fillin the remainder of the descriptor info.
@@ -309,9 +315,10 @@
 	for (i = 0; i < bf->bf_nseg; i++, ds++) {
 		ds->ds_data = bf->bf_segs[i].ds_addr;
 		if (i == bf->bf_nseg - 1)
-			ds->ds_link = 0;
+			ath_hal_settxdesclink(ah, ds, 0);
 		else
-			ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
+			ath_hal_settxdesclink(ah, ds,
+			    bf->bf_daddr + dd->dd_descsize * (i + 1));
 		ath_hal_filltxdesc(ah, ds
 			, bf->bf_segs[i].ds_len	/* segment length */
 			, i == 0		/* first segment */
@@ -339,6 +346,11 @@
 	struct ath_hal *ah = sc->sc_ah;
 	struct ath_desc *ds, *ds0;
 	int i;
+	/*
+	 * XXX There's txdma and txdma_mgmt; the descriptor
+	 * sizes must match.
+	 */
+	struct ath_descdma *dd = &sc->sc_txdma;
 
 	ds0 = ds = bf->bf_desc;
 
@@ -349,9 +361,10 @@
 	for (i = 0; i < bf->bf_nseg; i++, ds++) {
 		ds->ds_data = bf->bf_segs[i].ds_addr;
 		if (i == bf->bf_nseg - 1)
-			ds->ds_link = 0;
+			ath_hal_settxdesclink(ah, ds, 0);
 		else
-			ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
+			ath_hal_settxdesclink(ah, ds,
+			    bf->bf_daddr + dd->dd_descsize * (i + 1));
 
 		/*
 		 * This performs the setup for an aggregate frame.
@@ -413,7 +426,8 @@
 		 * to the beginning descriptor of this frame.
 		 */
 		if (bf_prev != NULL)
-			bf_prev->bf_lastds->ds_link = bf->bf_daddr;
+			ath_hal_settxdesclink(sc->sc_ah, bf_prev->bf_lastds,
+			    bf->bf_daddr);
 
 		/* Save a copy so we can link the next descriptor in */
 		bf_prev = bf;
@@ -481,7 +495,7 @@
 		*txq->axq_link = bf->bf_daddr;
 	}
 	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
-	txq->axq_link = &bf->bf_lastds->ds_link;
+	ath_hal_gettxdesclinkptr(sc->sc_ah, bf->bf_lastds, &txq->axq_link);
 }
 
 /*
@@ -615,7 +629,7 @@
 #endif /* IEEE80211_SUPPORT_TDMA */
 		if (bf->bf_state.bfs_aggr)
 			txq->axq_aggr_depth++;
-		txq->axq_link = &bf->bf_lastds->ds_link;
+		ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link);
 		ath_hal_txstart(ah, txq->axq_qnum);
 	}
 }
@@ -644,7 +658,7 @@
 		return;
 
 	ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
-	txq->axq_link = &bf_last->bf_lastds->ds_link;
+	ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link);
 	ath_hal_txstart(ah, txq->axq_qnum);
 }
 
@@ -1171,6 +1185,15 @@
 	struct ath_node *an;
 	u_int pri;
 
+	/*
+	 * To ensure that both sequence numbers and the CCMP PN handling
+	 * is "correct", make sure that the relevant TID queue is locked.
+	 * Otherwise the CCMP PN and seqno may appear out of order, causing
+	 * re-ordered frames to have out of order CCMP PN's, resulting
+	 * in many, many frame drops.
+	 */
+	ATH_TXQ_LOCK_ASSERT(txq);
+
 	wh = mtod(m0, struct ieee80211_frame *);
 	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
 	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
@@ -1299,6 +1322,40 @@
 	}
 
 	/*
+	 * There are two known scenarios where the frame AC doesn't match
+	 * what the destination TXQ is.
+	 *
+	 * + non-QoS frames (eg management?) that the net80211 stack has
+	 *   assigned a higher AC to, but since it's a non-QoS TID, it's
+	 *   being thrown into TID 16.  TID 16 gets the AC_BE queue.
+	 *   It's quite possible that management frames should just be
+	 *   direct dispatched to hardware rather than go via the software
+	 *   queue; that should be investigated in the future.  There are
+	 *   some specific scenarios where this doesn't make sense, mostly
+	 *   surrounding ADDBA request/response - hence why that is special
+	 *   cased.
+	 *
+	 * + Multicast frames going into the VAP mcast queue.  That shows up
+	 *   as "TXQ 11".
+	 *
+	 * This driver should eventually support separate TID and TXQ locking,
+	 * allowing for arbitrary AC frames to appear on arbitrary software
+	 * queues, being queued to the "correct" hardware queue when needed.
+	 */
+#if 0
+	if (txq != sc->sc_ac2q[pri]) {
+		device_printf(sc->sc_dev,
+		    "%s: txq=%p (%d), pri=%d, pri txq=%p (%d)\n",
+		    __func__,
+		    txq,
+		    txq->axq_qnum,
+		    pri,
+		    sc->sc_ac2q[pri],
+		    sc->sc_ac2q[pri]->axq_qnum);
+	}
+#endif
+
+	/*
 	 * Calculate miscellaneous flags.
 	 */
 	if (ismcast) {
@@ -1424,7 +1481,7 @@
 	int ismcast;
 	const struct ieee80211_frame *wh;
 	int is_ampdu, is_ampdu_tx, is_ampdu_pending;
-	//ieee80211_seq seqno;
+	ieee80211_seq seqno;
 	uint8_t type, subtype;
 
 	/*
@@ -1476,9 +1533,8 @@
 	is_ampdu_pending = ath_tx_ampdu_pending(sc, ATH_NODE(ni), tid);
 	is_ampdu = is_ampdu_tx | is_ampdu_pending;
 
-	DPRINTF(sc, ATH_DEBUG_SW_TX,
-	    "%s: bf=%p, tid=%d, ac=%d, is_ampdu=%d\n",
-	    __func__, bf, tid, pri, is_ampdu);
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ac=%d, is_ampdu=%d\n",
+	    __func__, tid, pri, is_ampdu);
 
 	/*
 	 * When servicing one or more stations in power-save mode
@@ -1494,34 +1550,33 @@
 	/* Do the generic frame setup */
 	/* XXX should just bzero the bf_state? */
 	bf->bf_state.bfs_dobaw = 0;
-	bf->bf_state.bfs_seqno_assigned = 0;
-	bf->bf_state.bfs_need_seqno = 0;
-	bf->bf_state.bfs_seqno = -1;	/* XXX debugging */
+
+	/*
+	 * Acquire the TXQ lock early, so both the encap and seqno
+	 * are allocated together.
+	 */
+	ATH_TXQ_LOCK(txq);
 
 	/* A-MPDU TX? Manually set sequence number */
-	/* Don't do it whilst pending; the net80211 layer still assigns them */
-	/* XXX do we need locking here? */
+	/*
+	 * Don't do it whilst pending; the net80211 layer still
+	 * assigns them.
+	 */
 	if (is_ampdu_tx) {
-		ATH_TXQ_LOCK(txq);
 		/*
 		 * Always call; this function will
 		 * handle making sure that null data frames
 		 * don't get a sequence number from the current
 		 * TID and thus mess with the BAW.
 		 */
-		//seqno = ath_tx_tid_seqno_assign(sc, ni, bf, m0);
-		if (ath_tx_seqno_required(sc, ni, bf, m0)) {
+		seqno = ath_tx_tid_seqno_assign(sc, ni, bf, m0);
+
+		/*
+		 * Don't add QoS NULL frames to the BAW.
+		 */
+		if (IEEE80211_QOS_HAS_SEQ(wh) &&
+		    subtype != IEEE80211_FC0_SUBTYPE_QOS_NULL) {
 			bf->bf_state.bfs_dobaw = 1;
-			bf->bf_state.bfs_need_seqno = 1;
-		}
-		ATH_TXQ_UNLOCK(txq);
-	} else {
-		/* No AMPDU TX, we've been assigned a sequence number. */
-		if (IEEE80211_QOS_HAS_SEQ(wh)) {
-			bf->bf_state.bfs_seqno_assigned = 1;
-			/* XXX we should store the frag+seqno in bfs_seqno */
-			bf->bf_state.bfs_seqno =
-			    M_SEQNO_GET(m0) << IEEE80211_SEQ_SEQ_SHIFT;
 		}
 	}
 
@@ -1529,7 +1584,7 @@
 	 * If needed, the sequence number has been assigned.
 	 * Squirrel it away somewhere easy to get to.
 	 */
-	//bf->bf_state.bfs_seqno = M_SEQNO_GET(m0) << IEEE80211_SEQ_SEQ_SHIFT;
+	bf->bf_state.bfs_seqno = M_SEQNO_GET(m0) << IEEE80211_SEQ_SEQ_SHIFT;
 
 	/* Is ampdu pending? fetch the seqno and print it out */
 	if (is_ampdu_pending)
@@ -1541,15 +1596,11 @@
 	r = ath_tx_normal_setup(sc, ni, bf, m0, txq);
 
 	if (r != 0)
-		return r;
+		goto done;
 
 	/* At this point m0 could have changed! */
 	m0 = bf->bf_m;
 
-	DPRINTF(sc, ATH_DEBUG_SW_TX,
-	    "%s: DONE: bf=%p, tid=%d, ac=%d, is_ampdu=%d, dobaw=%d, seqno=%d\n",
-	    __func__, bf, tid, pri, is_ampdu, bf->bf_state.bfs_dobaw, M_SEQNO_GET(m0));
-
 #if 1
 	/*
 	 * If it's a multicast frame, do a direct-dispatch to the
@@ -1568,21 +1619,17 @@
 	 * reached.)
 	 */
 	if (txq == &avp->av_mcastq) {
-		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		DPRINTF(sc, ATH_DEBUG_SW_TX,
 		    "%s: bf=%p: mcastq: TX'ing\n", __func__, bf);
-		ATH_TXQ_LOCK(txq);
 		ath_tx_xmit_normal(sc, txq, bf);
-		ATH_TXQ_UNLOCK(txq);
 	} else if (type == IEEE80211_FC0_TYPE_CTL &&
 		    subtype == IEEE80211_FC0_SUBTYPE_BAR) {
-		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		DPRINTF(sc, ATH_DEBUG_SW_TX,
 		    "%s: BAR: TX'ing direct\n", __func__);
-		ATH_TXQ_LOCK(txq);
 		ath_tx_xmit_normal(sc, txq, bf);
-		ATH_TXQ_UNLOCK(txq);
 	} else {
 		/* add to software queue */
-		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		DPRINTF(sc, ATH_DEBUG_SW_TX,
 		    "%s: bf=%p: swq: TX'ing\n", __func__, bf);
 		ath_tx_swq(sc, ni, txq, bf);
 	}
@@ -1591,10 +1638,10 @@
 	 * For now, since there's no software queue,
 	 * direct-dispatch to the hardware.
 	 */
-	ATH_TXQ_LOCK(txq);
 	ath_tx_xmit_normal(sc, txq, bf);
+#endif
+done:
 	ATH_TXQ_UNLOCK(txq);
-#endif
 
 	return 0;
 }
@@ -1630,10 +1677,29 @@
 	/* XXX honor IEEE80211_BPF_DATAPAD */
 	pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN;
 
-
 	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: ismcast=%d\n",
 	    __func__, ismcast);
 
+	pri = params->ibp_pri & 3;
+	/* Override pri if the frame isn't a QoS one */
+	if (! IEEE80211_QOS_HAS_SEQ(wh))
+		pri = ath_tx_getac(sc, m0);
+
+	/* XXX If it's an ADDBA, override the correct queue */
+	do_override = ath_tx_action_frame_override_queue(sc, ni, m0, &o_tid);
+
+	/* Map ADDBA to the correct priority */
+	if (do_override) {
+#if 0
+		device_printf(sc->sc_dev,
+		    "%s: overriding tid %d pri %d -> %d\n",
+		    __func__, o_tid, pri, TID_TO_WME_AC(o_tid));
+#endif
+		pri = TID_TO_WME_AC(o_tid);
+	}
+
+	ATH_TXQ_LOCK(sc->sc_ac2q[pri]);
+
 	/* Handle encryption twiddling if needed */
 	if (! ath_tx_tag_crypto(sc, ni,
 	    m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0,
@@ -1688,11 +1754,6 @@
 	if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA))
 		bf->bf_state.bfs_ctsrate0 = params->ibp_ctsrate;
 
-	pri = params->ibp_pri & 3;
-	/* Override pri if the frame isn't a QoS one */
-	if (! IEEE80211_QOS_HAS_SEQ(wh))
-		pri = ath_tx_getac(sc, m0);
-
 	/*
 	 * NB: we mark all packets as type PSPOLL so the h/w won't
 	 * set the sequence number, duration, etc.
@@ -1774,19 +1835,6 @@
 
 	/* NB: no buffered multicast in power save support */
 
-	/* XXX If it's an ADDBA, override the correct queue */
-	do_override = ath_tx_action_frame_override_queue(sc, ni, m0, &o_tid);
-
-	/* Map ADDBA to the correct priority */
-	if (do_override) {
-#if 0
-		device_printf(sc->sc_dev,
-		    "%s: overriding tid %d pri %d -> %d\n",
-		    __func__, o_tid, pri, TID_TO_WME_AC(o_tid));
-#endif
-		pri = TID_TO_WME_AC(o_tid);
-	}
-
 	/*
 	 * If we're overiding the ADDBA destination, dump directly
 	 * into the hardware queue, right after any pending
@@ -1796,13 +1844,12 @@
 	    __func__, do_override);
 
 	if (do_override) {
-		ATH_TXQ_LOCK(sc->sc_ac2q[pri]);
 		ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
-		ATH_TXQ_UNLOCK(sc->sc_ac2q[pri]);
 	} else {
 		/* Queue to software queue */
 		ath_tx_swq(sc, ni, sc->sc_ac2q[pri], bf);
 	}
+	ATH_TXQ_UNLOCK(sc->sc_ac2q[pri]);
 
 	return 0;
 }
@@ -1867,7 +1914,7 @@
 	/*
 	 * Grab a TX buffer and associated resources.
 	 */
-	bf = ath_getbuf(sc);
+	bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT);
 	if (bf == NULL) {
 		sc->sc_stats.ast_tx_nobuf++;
 		m_freem(m);
@@ -1905,7 +1952,7 @@
 	return 0;
 bad2:
 	ATH_TXBUF_LOCK(sc);
-	TAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+	ath_returnbuf_head(sc, bf);
 	ATH_TXBUF_UNLOCK(sc);
 bad:
 	ATH_PCU_LOCK(sc);
@@ -2027,27 +2074,27 @@
 	struct ieee80211_tx_ampdu *tap;
 
 	ATH_TXQ_LOCK_ASSERT(sc->sc_ac2q[tid->ac]);
+	ATH_TID_LOCK_ASSERT(sc, tid);
 
 	if (bf->bf_state.bfs_isretried)
 		return;
 
-	/*
-	 * If this occurs we're in a lot of trouble.  We should try to
-	 * recover from this without the session hanging?
-	 */
-	if (! bf->bf_state.bfs_seqno_assigned) {
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+
+	if (! bf->bf_state.bfs_dobaw) {
 		device_printf(sc->sc_dev,
-		    "%s: bf=%p, seqno_assigned is 0?!\n", __func__, bf);
-		return;
+		    "%s: dobaw=0, seqno=%d, window %d:%d\n",
+		    __func__,
+		    SEQNO(bf->bf_state.bfs_seqno),
+		    tap->txa_start,
+		    tap->txa_wnd);
 	}
 
-	tap = ath_tx_get_tx_tid(an, tid->tid);
-
 	if (bf->bf_state.bfs_addedbaw)
 		device_printf(sc->sc_dev,
-		    "%s: re-added? bf=%p, tid=%d, seqno %d; window %d:%d; "
+		    "%s: re-added? tid=%d, seqno %d; window %d:%d; "
 		    "baw head=%d tail=%d\n",
-		    __func__, bf, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
+		    __func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
 		    tap->txa_start, tap->txa_wnd, tid->baw_head,
 		    tid->baw_tail);
 
@@ -2063,7 +2110,6 @@
 		    __func__, bf, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
 		    tap->txa_start, tap->txa_wnd, tid->baw_head,
 		    tid->baw_tail);
-
 	}
 
 	/*
@@ -2073,9 +2119,9 @@
 	index  = ATH_BA_INDEX(tap->txa_start, SEQNO(bf->bf_state.bfs_seqno));
 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
 	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
-	    "%s: bf=%p, tid=%d, seqno %d; window %d:%d; index=%d cindex=%d "
+	    "%s: tid=%d, seqno %d; window %d:%d; index=%d cindex=%d "
 	    "baw head=%d tail=%d\n",
-	    __func__, bf, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
+	    __func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
 	    tap->txa_start, tap->txa_wnd, index, cindex, tid->baw_head,
 	    tid->baw_tail);
 
@@ -2124,6 +2170,7 @@
 	int seqno = SEQNO(old_bf->bf_state.bfs_seqno);
 
 	ATH_TXQ_LOCK_ASSERT(sc->sc_ac2q[tid->ac]);
+	ATH_TID_LOCK_ASSERT(sc, tid);
 
 	tap = ath_tx_get_tx_tid(an, tid->tid);
 	index  = ATH_BA_INDEX(tap->txa_start, seqno);
@@ -2172,15 +2219,16 @@
 	int seqno = SEQNO(bf->bf_state.bfs_seqno);
 
 	ATH_TXQ_LOCK_ASSERT(sc->sc_ac2q[tid->ac]);
+	ATH_TID_LOCK_ASSERT(sc, tid);
 
 	tap = ath_tx_get_tx_tid(an, tid->tid);
 	index  = ATH_BA_INDEX(tap->txa_start, seqno);
 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
 
 	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
-	    "%s: bf=%p: tid=%d, baw=%d:%d, seqno=%d, index=%d, cindex=%d, "
+	    "%s: tid=%d, baw=%d:%d, seqno=%d, index=%d, cindex=%d, "
 	    "baw head=%d, tail=%d\n",
-	    __func__, bf, tid->tid, tap->txa_start, tap->txa_wnd, seqno, index,
+	    __func__, tid->tid, tap->txa_start, tap->txa_wnd, seqno, index,
 	    cindex, tid->baw_head, tid->baw_tail);
 
 	/*
@@ -2261,51 +2309,11 @@
 }
 
 /*
- * Return whether a sequence number is actually required.
- *
- * A sequence number must only be allocated at the time that a frame
- * is considered for addition to the BAW/aggregate and being TXed.
- * The sequence number must not be allocated before the frame
- * is added to the BAW (protected by the same lock instance)
- * otherwise a the multi-entrant TX path may result in a later seqno
- * being added to the BAW first.  The subsequent addition of the
- * earlier seqno would then not go into the BAW as it's now outside
- * of said BAW.
- *
- * This routine is used by ath_tx_start() to mark whether the frame
- * should get a sequence number before adding it to the BAW.
- *
- * Then the actual aggregate TX routines will check whether this
- * flag is set and if the frame needs to go into the BAW, it'll
- * have a sequence number allocated for it.
- */
-static int
-ath_tx_seqno_required(struct ath_softc *sc, struct ieee80211_node *ni,
-    struct ath_buf *bf, struct mbuf *m0)
-{
-	const struct ieee80211_frame *wh;
-	uint8_t subtype;
-
-	wh = mtod(m0, const struct ieee80211_frame *);
-	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
-
-	/* XXX assert txq lock */
-	/* XXX assert ampdu is set */
-
-	return ((IEEE80211_QOS_HAS_SEQ(wh) &&
-	    subtype != IEEE80211_FC0_SUBTYPE_QOS_NULL));
-}
-
-/*
  * Assign a sequence number manually to the given frame.
  *
  * This should only be called for A-MPDU TX frames.
- *
- * If this is called after the initial frame setup, make sure you've flushed
- * the DMA map or you'll risk sending stale data to the NIC.  This routine
- * updates the actual frame contents with the relevant seqno.
  */
-int
+static ieee80211_seq
 ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni,
     struct ath_buf *bf, struct mbuf *m0)
 {
@@ -2318,22 +2326,8 @@
 	wh = mtod(m0, struct ieee80211_frame *);
 	pri = M_WME_GETAC(m0);			/* honor classification */
 	tid = WME_AC_TO_TID(pri);
-	DPRINTF(sc, ATH_DEBUG_SW_TX,
-	    "%s: bf=%p, pri=%d, tid=%d, qos has seq=%d\n",
-	    __func__, bf, pri, tid, IEEE80211_QOS_HAS_SEQ(wh));
-
-	if (! bf->bf_state.bfs_need_seqno) {
-		device_printf(sc->sc_dev, "%s: bf=%p: need_seqno not set?!\n",
-		    __func__, bf);
-		return -1;
-	}
-	/* XXX check for bfs_need_seqno? */
-	if (bf->bf_state.bfs_seqno_assigned) {
-		device_printf(sc->sc_dev,
-		    "%s: bf=%p: seqno already assigned (%d)?!\n",
-		    __func__, bf, SEQNO(bf->bf_state.bfs_seqno));
-		return bf->bf_state.bfs_seqno >> IEEE80211_SEQ_SEQ_SHIFT;
-	}
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pri=%d, tid=%d, qos has seq=%d\n",
+	    __func__, pri, tid, IEEE80211_QOS_HAS_SEQ(wh));
 
 	/* XXX Is it a control frame? Ignore */
 
@@ -2341,6 +2335,8 @@
 	if (! IEEE80211_QOS_HAS_SEQ(wh))
 		return -1;
 
+	ATH_TID_LOCK_ASSERT(sc, &(ATH_NODE(ni)->an_tid[tid]));
+
 	/*
 	 * Is it a QOS NULL Data frame? Give it a sequence number from
 	 * the default TID (IEEE80211_NONQOS_TID.)
@@ -2352,6 +2348,7 @@
 	 */
 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
 	if (subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) {
+		/* XXX no locking for this TID? This is a bit of a problem. */
 		seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID];
 		INCR(ni->ni_txseqs[IEEE80211_NONQOS_TID], IEEE80211_SEQ_RANGE);
 	} else {
@@ -2361,14 +2358,9 @@
 	}
 	*(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
 	M_SEQNO_SET(m0, seqno);
-	bf->bf_state.bfs_seqno = seqno << IEEE80211_SEQ_SEQ_SHIFT;
-	bf->bf_state.bfs_seqno_assigned = 1;
 
 	/* Return so caller can do something with it if needed */
-	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p:  -> seqno=%d\n",
-	    __func__,
-	    bf,
-	    seqno);
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s:  -> seqno=%d\n", __func__, seqno);
 	return seqno;
 }
 
@@ -2380,98 +2372,27 @@
 static void
 ath_tx_xmit_aggr(struct ath_softc *sc, struct ath_node *an, struct ath_buf *bf)
 {
-	struct ieee80211_node *ni = &an->an_node;
 	struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid];
 	struct ath_txq *txq = bf->bf_state.bfs_txq;
 	struct ieee80211_tx_ampdu *tap;
 
 	ATH_TXQ_LOCK_ASSERT(txq);
+	ATH_TID_LOCK_ASSERT(sc, tid);
 
 	tap = ath_tx_get_tx_tid(an, tid->tid);
 
 	/* paused? queue */
 	if (tid->paused) {
-		ATH_TXQ_INSERT_TAIL(tid, bf, bf_list);
+		ATH_TXQ_INSERT_HEAD(tid, bf, bf_list);
 		/* XXX don't sched - we're paused! */
 		return;
 	}
 
-	/*
-	 * TODO: If it's _before_ the BAW left edge, complain very loudly.
-	 * This means something (else) has slid the left edge along
-	 * before we got a chance to be TXed.
-	 */
-
-	/*
-	 * Is there space in this BAW for another frame?
-	 * If not, don't bother trying to schedule it; just
-	 * throw it back on the queue.
-	 *
-	 * If we allocate the sequence number before we add
-	 * it to the BAW, we risk racing with another TX
-	 * thread that gets in a frame into the BAW with
-	 * seqno greater than ours.  We'd then fail the
-	 * below check and throw the frame on the tail of
-	 * the queue.  The sender would then have a hole.
-	 *
-	 * XXX again, we're protecting ni->ni_txseqs[tid]
-	 * behind this hardware TXQ lock, like the rest of
-	 * the TIDs that map to it.  Ugh.
-	 */
-	if (bf->bf_state.bfs_dobaw) {
-		ieee80211_seq seqno;
-
-		/*
-		 * If the sequence number is allocated, use it.
-		 * Otherwise, use the sequence number we WOULD
-		 * allocate.
-		 */
-		if (bf->bf_state.bfs_seqno_assigned)
-			seqno = SEQNO(bf->bf_state.bfs_seqno);
-		else
-			seqno = ni->ni_txseqs[bf->bf_state.bfs_tid];
-
-		/*
-		 * Check whether either the currently allocated
-		 * sequence number _OR_ the to-be allocated
-		 * sequence number is inside the BAW.
-		 */
-		if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd, seqno)) {
-			ATH_TXQ_INSERT_TAIL(tid, bf, bf_list);
-			ath_tx_tid_sched(sc, tid);
-			return;
-		}
-		if (! bf->bf_state.bfs_seqno_assigned) {
-			int seqno;
-
-			seqno = ath_tx_tid_seqno_assign(sc, ni, bf, bf->bf_m);
-			if (seqno < 0) {
-				device_printf(sc->sc_dev,
-				    "%s: bf=%p, huh, seqno=-1?\n",
-				    __func__,
-				    bf);
-				/* XXX what can we even do here? */
-			}
-			/* Flush seqno update to RAM */
-			/*
-			 * XXX This is required because the dmasetup
-			 * XXX is done early rather than at dispatch
-			 * XXX time. Ew, we should fix this!
-			 */
-			bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
-			    BUS_DMASYNC_PREWRITE);
-		}
-	}
-
 	/* outside baw? queue */
 	if (bf->bf_state.bfs_dobaw &&
 	    (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
 	    SEQNO(bf->bf_state.bfs_seqno)))) {
-		device_printf(sc->sc_dev,
-		    "%s: bf=%p, shouldn't be outside BAW now?!\n",
-		    __func__,
-		    bf);
-		ATH_TXQ_INSERT_TAIL(tid, bf, bf_list);
+		ATH_TXQ_INSERT_HEAD(tid, bf, bf_list);
 		ath_tx_tid_sched(sc, tid);
 		return;
 	}
@@ -2521,14 +2442,18 @@
 	int pri, tid;
 	struct mbuf *m0 = bf->bf_m;
 
+	ATH_TXQ_LOCK_ASSERT(txq);
+
 	/* Fetch the TID - non-QoS frames get assigned to TID 16 */
 	wh = mtod(m0, struct ieee80211_frame *);
 	pri = ath_tx_getac(sc, m0);
 	tid = ath_tx_gettid(sc, m0);
 	atid = &an->an_tid[tid];
 
-	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p, pri=%d, tid=%d, qos=%d, seqno=%d\n",
-	    __func__, bf, pri, tid, IEEE80211_QOS_HAS_SEQ(wh), SEQNO(bf->bf_state.bfs_seqno));
+	ATH_TID_LOCK_ASSERT(sc, atid);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p, pri=%d, tid=%d, qos=%d\n",
+	    __func__, bf, pri, tid, IEEE80211_QOS_HAS_SEQ(wh));
 
 	/* Set local packet state, used to queue packets to hardware */
 	bf->bf_state.bfs_tid = tid;
@@ -2541,41 +2466,53 @@
 	 * If the TID is paused or the traffic it outside BAW, software
 	 * queue it.
 	 */
-	ATH_TXQ_LOCK(txq);
 	if (atid->paused) {
 		/* TID is paused, queue */
-		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: paused\n", __func__, bf);
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: paused\n", __func__);
 		ATH_TXQ_INSERT_TAIL(atid, bf, bf_list);
 	} else if (ath_tx_ampdu_pending(sc, an, tid)) {
 		/* AMPDU pending; queue */
-		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: pending\n", __func__, bf);
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pending\n", __func__);
 		ATH_TXQ_INSERT_TAIL(atid, bf, bf_list);
 		/* XXX sched? */
 	} else if (ath_tx_ampdu_running(sc, an, tid)) {
 		/* AMPDU running, attempt direct dispatch if possible */
+
+		/*
+		 * Always queue the frame to the tail of the list.
+		 */
+		ATH_TXQ_INSERT_TAIL(atid, bf, bf_list);
+
+		/*
+		 * If the hardware queue isn't busy, direct dispatch
+		 * the head frame in the list.  Don't schedule the
+		 * TID - let it build some more frames first?
+		 *
+		 * Otherwise, schedule the TID.
+		 */
 		if (txq->axq_depth < sc->sc_hwq_limit) {
+			bf = TAILQ_FIRST(&atid->axq_q);
+			ATH_TXQ_REMOVE(atid, bf, bf_list);
+			ath_tx_xmit_aggr(sc, an, bf);
 			DPRINTF(sc, ATH_DEBUG_SW_TX,
-			    "%s: bf=%p: xmit_aggr\n",
-			    __func__, bf);
-			ath_tx_xmit_aggr(sc, an, bf);
+			    "%s: xmit_aggr\n",
+			    __func__);
 		} else {
 			DPRINTF(sc, ATH_DEBUG_SW_TX,
-			    "%s: bf=%p: ampdu; swq'ing\n",
-			    __func__, bf);
-			ATH_TXQ_INSERT_TAIL(atid, bf, bf_list);
+			    "%s: ampdu; swq'ing\n",
+			    __func__);
 			ath_tx_tid_sched(sc, atid);
 		}
 	} else if (txq->axq_depth < sc->sc_hwq_limit) {
 		/* AMPDU not running, attempt direct dispatch */
-		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: xmit_normal\n", __func__, bf);
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: xmit_normal\n", __func__);
 		ath_tx_xmit_normal(sc, txq, bf);
 	} else {
 		/* Busy; queue */
-		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: swq'ing\n", __func__, bf);
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: swq'ing\n", __func__);
 		ATH_TXQ_INSERT_TAIL(atid, bf, bf_list);
 		ath_tx_tid_sched(sc, atid);
 	}
-	ATH_TXQ_UNLOCK(txq);
 }
 
 /*
@@ -2673,10 +2610,12 @@
 {
 	ATH_TXQ_LOCK_ASSERT(sc->sc_ac2q[tid->ac]);
 
-	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
-	    "%s: tid=%p, called\n",
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: tid=%p, bar_wait=%d, bar_tx=%d, called\n",
 	    __func__,
-	    tid);
+	    tid,
+	    tid->bar_wait,
+	    tid->bar_tx);
 
 	/* We shouldn't be called when bar_tx is 1 */
 	if (tid->bar_tx) {
@@ -2704,7 +2643,7 @@
 {
 	ATH_TXQ_LOCK_ASSERT(sc->sc_ac2q[tid->ac]);
 
-	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
 	    "%s: tid=%p, called\n",
 	    __func__,
 	    tid);
@@ -2732,6 +2671,9 @@
 	if (tid->bar_wait == 0 || tid->hwq_depth > 0)
 		return (0);
 
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: tid=%p (%d), bar ready\n",
+	    __func__, tid, tid->tid);
+
 	return (1);
 }
 
@@ -2754,7 +2696,7 @@
 
 	ATH_TXQ_LOCK_ASSERT(sc->sc_ac2q[tid->ac]);
 
-	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
 	    "%s: tid=%p, called\n",
 	    __func__,
 	    tid);
@@ -2776,7 +2718,7 @@
 
 	/* Don't do anything if we still have pending frames */
 	if (tid->hwq_depth > 0) {
-		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
 		    "%s: tid=%p, hwq_depth=%d, waiting\n",
 		    __func__,
 		    tid,
@@ -2793,7 +2735,7 @@
 	 *
 	 * XXX verify this is _actually_ the valid value to begin at!
 	 */
-	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
 	    "%s: tid=%p, new BAW left edge=%d\n",
 	    __func__,
 	    tid,
@@ -2856,25 +2798,24 @@
 		if (t == 0) {
 			device_printf(sc->sc_dev,
 			    "%s: node %p: bf=%p: addbaw=%d, dobaw=%d, "
-			    "seqno_assign=%d, seqno_required=%d, seqno=%d, retry=%d\n",
+			    "seqno=%d, retry=%d\n",
 			    __func__, ni, bf,
 			    bf->bf_state.bfs_addedbaw,
 			    bf->bf_state.bfs_dobaw,
-			    bf->bf_state.bfs_need_seqno,
-			    bf->bf_state.bfs_seqno_assigned,
 			    SEQNO(bf->bf_state.bfs_seqno),
 			    bf->bf_state.bfs_retries);
 			device_printf(sc->sc_dev,
-			    "%s: node %p: bf=%p: tid txq_depth=%d hwq_depth=%d\n",
+			    "%s: node %p: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d\n",
 			    __func__, ni, bf,
 			    tid->axq_depth,
-			    tid->hwq_depth);
+			    tid->hwq_depth,
+			    tid->bar_wait);
 			device_printf(sc->sc_dev,
-			    "%s: node %p: bf=%p: tid %d: txq_depth=%d, "
+			    "%s: node %p: tid %d: txq_depth=%d, "
 			    "txq_aggr_depth=%d, sched=%d, paused=%d, "
 			    "hwq_depth=%d, incomp=%d, baw_head=%d, "
 			    "baw_tail=%d txa_start=%d, ni_txseqs=%d\n",
-			     __func__, ni, bf, tid->tid, txq->axq_depth,
+			     __func__, ni, tid->tid, txq->axq_depth,
 			     txq->axq_aggr_depth, tid->sched, tid->paused,
 			     tid->hwq_depth, tid->incomp, tid->baw_head,
 			     tid->baw_tail, tap == NULL ? -1 : tap->txa_start,
@@ -3088,14 +3029,14 @@
  * The caller is responsible for pausing the TID.
  */
 static void
-ath_tx_cleanup(struct ath_softc *sc, struct ath_node *an, int tid)
+ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid)
 {
 	struct ath_tid *atid = &an->an_tid[tid];
 	struct ieee80211_tx_ampdu *tap;
 	struct ath_buf *bf, *bf_next;
 	ath_bufhead bf_cq;
 
-	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
 	    "%s: TID %d: called\n", __func__, tid);
 
 	TAILQ_INIT(&bf_cq);
@@ -3232,7 +3173,7 @@
 		 * the list.)
 		 */
 		ATH_TXBUF_LOCK(sc);
-		TAILQ_INSERT_HEAD(&sc->sc_txbuf, nbf, bf_list);
+		ath_returnbuf_head(sc, nbf);
 		ATH_TXBUF_UNLOCK(sc);
 		return NULL;
 	}
@@ -3455,6 +3396,9 @@
 		ATH_TXQ_INSERT_HEAD(tid, bf, bf_list);
 	}
 
+	/*
+	 * Schedule the TID to be re-tried.
+	 */
 	ath_tx_tid_sched(sc, tid);
 
 	/*
@@ -3469,12 +3413,9 @@
 		ath_tx_tid_bar_suspend(sc, tid);
 	}
 
-	ATH_TXQ_UNLOCK(sc->sc_ac2q[tid->ac]);
-
 	/*
 	 * Send BAR if required
 	 */
-	ATH_TXQ_LOCK(sc->sc_ac2q[tid->ac]);
 	if (ath_tx_tid_bar_tx_ready(sc, tid))
 		ath_tx_tid_bar_tx(sc, tid);
 	ATH_TXQ_UNLOCK(sc->sc_ac2q[tid->ac]);
@@ -3562,7 +3503,7 @@
 	int nframes = 0, nbad = 0, nf;
 	int pktlen;
 	/* XXX there's too much on the stack? */
-	struct ath_rc_series rc[4];
+	struct ath_rc_series rc[ATH_RC_NUM];
 	int txseq;
 
 	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: called; hwq_depth=%d\n",
@@ -3597,9 +3538,16 @@
 	pktlen = bf_first->bf_state.bfs_pktlen;
 
 	/*
-	 * handle errors first
+	 * Handle errors first!
+	 *
+	 * Here, handle _any_ error as a "exceeded retries" error.
+	 * Later on (when filtered frames are to be specially handled)
+	 * it'll have to be expanded.
 	 */
+#if 0
 	if (ts.ts_status & HAL_TXERR_XRETRY) {
+#endif
+	if (ts.ts_status != 0) {
 		ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
 		ath_tx_comp_aggr_error(sc, bf_first, atid);
 		return;
@@ -3648,6 +3596,10 @@
 		    "seq_st=%d\n",
 		    __func__, hasba, tx_ok, isaggr, seq_st);
 		/* XXX TODO: schedule an interface reset */
+#ifdef ATH_DEBUG
+		ath_printtxbuf(sc, bf_first,
+		    sc->sc_ac2q[atid->ac]->axq_qnum, 0, 0);
+#endif
 	}
 
 	/*
@@ -3742,24 +3694,28 @@
 		ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
 	}
 
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+	    "%s: txa_start now %d\n", __func__, tap->txa_start);
+
+	ATH_TXQ_LOCK(sc->sc_ac2q[atid->ac]);
+
 	/* Prepend all frames to the beginning of the queue */
-	ATH_TXQ_LOCK(sc->sc_ac2q[atid->ac]);
 	while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) {
 		TAILQ_REMOVE(&bf_q, bf, bf_list);
 		ATH_TXQ_INSERT_HEAD(atid, bf, bf_list);
 	}
+
+	/*
+	 * Reschedule to grab some further frames.
+	 */
 	ath_tx_tid_sched(sc, atid);
-	ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
-
-	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
-	    "%s: txa_start now %d\n", __func__, tap->txa_start);
 
 	/*
 	 * Send BAR if required
 	 */
-	ATH_TXQ_LOCK(sc->sc_ac2q[atid->ac]);
 	if (ath_tx_tid_bar_tx_ready(sc, atid))
 		ath_tx_tid_bar_tx(sc, atid);
+
 	ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
 
 	/* Do deferred completion */
@@ -3835,7 +3791,10 @@
 	 * Don't bother with the retry check if all frames
 	 * are being failed (eg during queue deletion.)
 	 */
+#if 0
 	if (fail == 0 && ts->ts_status & HAL_TXERR_XRETRY) {
+#endif
+	if (fail == 0 && ts->ts_status != 0) {
 		ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
 		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: retry_unaggr\n",
 		    __func__);
@@ -4303,9 +4262,17 @@
 	 * it'll be "after" the left edge of the BAW and thus it'll
 	 * fall within it.
 	 */
-	ATH_TXQ_LOCK(sc->sc_ac2q[atid->tid]);
-	ath_tx_tid_pause(sc, atid);
-	ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->tid]);
+	ATH_TXQ_LOCK(sc->sc_ac2q[atid->ac]);
+	/*
+	 * This is a bit annoying.  Until net80211 HT code inherits some
+	 * (any) locking, we may have this called in parallel BUT only
+	 * one response/timeout will be called.  Grr.
+	 */
+	if (atid->addba_tx_pending == 0) {
+		ath_tx_tid_pause(sc, atid);
+		atid->addba_tx_pending = 1;
+	}
+	ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
 
 	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
 	    "%s: called; dialogtoken=%d, baparamset=%d, batimeout=%d\n",
@@ -4365,6 +4332,7 @@
 	r = sc->sc_addba_response(ni, tap, status, code, batimeout);
 
 	ATH_TXQ_LOCK(sc->sc_ac2q[atid->ac]);
+	atid->addba_tx_pending = 0;
 	/*
 	 * XXX dirty!
 	 * Slide the BAW left edge to wherever net80211 left it for us.
@@ -4379,6 +4347,9 @@
 
 /*
  * Stop ADDBA on a queue.
+ *
+ * This can be called whilst BAR TX is currently active on the queue,
+ * so make sure this is unblocked before continuing.
  */
 void
 ath_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
@@ -4390,20 +4361,32 @@
 
 	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: called\n", __func__);
 
-	/* Pause TID traffic early, so there aren't any races */
-	ATH_TXQ_LOCK(sc->sc_ac2q[atid->tid]);
+	/*
+	 * Pause TID traffic early, so there aren't any races
+	 * Unblock the pending BAR held traffic, if it's currently paused.
+	 */
+	ATH_TXQ_LOCK(sc->sc_ac2q[atid->ac]);
 	ath_tx_tid_pause(sc, atid);
-	ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->tid]);
+	if (atid->bar_wait) {
+		/*
+		 * bar_unsuspend() expects bar_tx == 1, as it should be
+		 * called from the TX completion path.  This quietens
+		 * the warning.  It's cleared for us anyway.
+		 */
+		atid->bar_tx = 1;
+		ath_tx_tid_bar_unsuspend(sc, atid);
+	}
+	ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
 
 	/* There's no need to hold the TXQ lock here */
 	sc->sc_addba_stop(ni, tap);
 
 	/*
-	 * ath_tx_cleanup will resume the TID if possible, otherwise
+	 * ath_tx_tid_cleanup will resume the TID if possible, otherwise
 	 * it'll set the cleanup flag, and it'll be unpaused once
 	 * things have been cleaned up.
 	 */
-	ath_tx_cleanup(sc, an, tid);
+	ath_tx_tid_cleanup(sc, an, tid);
 }
 
 /*
@@ -4426,8 +4409,15 @@
 	struct ath_tid *atid = &an->an_tid[tid];
 	int attempts = tap->txa_attempts;
 
-	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
-	    "%s: called; status=%d\n", __func__, status);
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: called; tap=%p, atid=%p, txa_tid=%d, atid->tid=%d, status=%d, attempts=%d\n",
+	    __func__,
+	    tap,
+	    atid,
+	    tap->txa_tid,
+	    atid->tid,
+	    status,
+	    attempts);
 
 	/* Note: This may update the BAW details */
 	sc->sc_bar_response(ni, tap, status);
@@ -4461,6 +4451,10 @@
 	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
 	    "%s: called; resuming\n", __func__);
 
+	ATH_TXQ_LOCK(sc->sc_ac2q[atid->ac]);
+	atid->addba_tx_pending = 0;
+	ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
+
 	/* Note: This updates the aggregate state to (again) pending */
 	sc->sc_addba_response_timeout(ni, tap);
 
@@ -4469,3 +4463,34 @@
 	ath_tx_tid_resume(sc, atid);
 	ATH_TXQ_UNLOCK(sc->sc_ac2q[atid->ac]);
 }
+
+static int
+ath_legacy_dma_txsetup(struct ath_softc *sc)
+{
+
+	/* nothing new needed */
+	return (0);
+}
+
+static int
+ath_legacy_dma_txteardown(struct ath_softc *sc)
+{
+
+	/* nothing new needed */
+	return (0);
+}
+
+void
+ath_xmit_setup_legacy(struct ath_softc *sc)
+{
+	/*
+	 * For now, just set the descriptor length to sizeof(ath_desc);
+	 * worry about extracting the real length out of the HAL later.
+	 */
+	sc->sc_tx_desclen = sizeof(struct ath_desc);
+	sc->sc_tx_statuslen = 0;
+	sc->sc_tx_nmaps = 1;	/* only one buffer per TX desc */
+
+	sc->sc_tx.xmit_setup = ath_legacy_dma_txsetup;
+	sc->sc_tx.xmit_teardown = ath_legacy_dma_txteardown;
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_tx.h
--- a/head/sys/dev/ath/if_ath_tx.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_tx.h	Wed Jul 25 17:07:47 2012 +0300
@@ -26,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/if_ath_tx.h 233227 2012-03-20 04:50:25Z adrian $
+ * $FreeBSD: head/sys/dev/ath/if_ath_tx.h 238710 2012-07-23 03:52:18Z adrian $
  */
 #ifndef	__IF_ATH_TX_H__
 #define	__IF_ATH_TX_H__
@@ -109,8 +109,6 @@
     struct ath_tid *tid, struct ath_buf *bf);
 extern struct ieee80211_tx_ampdu * ath_tx_get_tx_tid(struct ath_node *an,
     int tid);
-extern int ath_tx_tid_seqno_assign(struct ath_softc *sc,
-    struct ieee80211_node *ni, struct ath_buf *bf, struct mbuf *m0);
 
 /* TX addba handling */
 extern	int ath_addba_request(struct ieee80211_node *ni,
@@ -126,4 +124,13 @@
 extern	void ath_addba_response_timeout(struct ieee80211_node *ni,
     struct ieee80211_tx_ampdu *tap);
 
+/*
+ * Setup path
+ */
+#define	ath_txdma_setup(_sc)			\
+	(_sc)->sc_tx.xmit_setup(_sc)
+#define	ath_txdma_teardown(_sc)			\
+	(_sc)->sc_tx.xmit_teardown(_sc)
+extern	void ath_xmit_setup_legacy(struct ath_softc *sc);
+
 #endif
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_tx_edma.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_tx_edma.c	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 2012 Adrian Chadd <adrian at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_tx_edma.c 238710 2012-07-23 03:52:18Z adrian $");
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+
+#include "opt_inet.h"
+#include "opt_ath.h"
+/*
+ * This is needed for register operations which are performed
+ * by the driver - eg, calls to ath_hal_gettsf32().
+ *
+ * It's also required for any AH_DEBUG checks in here, eg the
+ * module dependencies.
+ */
+#include "opt_ah.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/callout.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kthread.h>
+#include <sys/taskqueue.h>
+#include <sys/priv.h>
+#include <sys/module.h>
+#include <sys/ktr.h>
+#include <sys/smp.h>	/* for mp_ncpus */
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#ifdef IEEE80211_SUPPORT_SUPERG
+#include <net80211/ieee80211_superg.h>
+#endif
+#ifdef IEEE80211_SUPPORT_TDMA
+#include <net80211/ieee80211_tdma.h>
+#endif
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/ath/if_athvar.h>
+#include <dev/ath/ath_hal/ah_devid.h>		/* XXX for softled */
+#include <dev/ath/ath_hal/ah_diagcodes.h>
+
+#include <dev/ath/if_ath_debug.h>
+#include <dev/ath/if_ath_misc.h>
+#include <dev/ath/if_ath_tsf.h>
+#include <dev/ath/if_ath_tx.h>
+#include <dev/ath/if_ath_sysctl.h>
+#include <dev/ath/if_ath_led.h>
+#include <dev/ath/if_ath_keycache.h>
+#include <dev/ath/if_ath_rx.h>
+#include <dev/ath/if_ath_beacon.h>
+#include <dev/ath/if_athdfs.h>
+
+#ifdef ATH_TX99_DIAG
+#include <dev/ath/ath_tx99/ath_tx99.h>
+#endif
+
+#include <dev/ath/if_ath_tx_edma.h>
+
+/*
+ * some general macros
+  */
+#define	INCR(_l, _sz)		(_l) ++; (_l) &= ((_sz) - 1)
+#define	DECR(_l, _sz)		(_l) --; (_l) &= ((_sz) - 1)
+
+MALLOC_DECLARE(M_ATHDEV);
+
+static int
+ath_edma_dma_txsetup(struct ath_softc *sc)
+{
+
+	/* XXX placeholder */
+	return (0);
+}
+
+static int
+ath_edma_dma_txteardown(struct ath_softc *sc)
+{
+
+	/* XXX placeholder */
+	return (0);
+}
+
+void
+ath_xmit_setup_edma(struct ath_softc *sc)
+{
+
+	/* Fetch EDMA field and buffer sizes */
+	(void) ath_hal_gettxdesclen(sc->sc_ah, &sc->sc_tx_desclen);
+	(void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen);
+	(void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps);
+
+	device_printf(sc->sc_dev, "TX descriptor length: %d\n",
+	    sc->sc_tx_desclen);
+	device_printf(sc->sc_dev, "TX status length: %d\n",
+	    sc->sc_tx_statuslen);
+	device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n",
+	    sc->sc_tx_nmaps);
+
+	sc->sc_tx.xmit_setup = ath_edma_dma_txsetup;
+	sc->sc_tx.xmit_teardown = ath_edma_dma_txteardown;
+}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_tx_edma.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/head/sys/dev/ath/if_ath_tx_edma.h	Wed Jul 25 17:07:47 2012 +0300
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2012 Adrian Chadd <adrian at FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: head/sys/dev/ath/if_ath_tx_edma.h 238710 2012-07-23 03:52:18Z adrian $
+ */
+#ifndef	__IF_ATH_TX_EDMA_H__
+#define	__IF_ATH_TX_EDMA_H__
+
+extern	void ath_xmit_setup_edma(struct ath_softc *sc);
+
+#endif
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_ath_tx_ht.c
--- a/head/sys/dev/ath/if_ath_tx_ht.c	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_ath_tx_ht.c	Wed Jul 25 17:07:47 2012 +0300
@@ -28,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_tx_ht.c 233988 2012-04-07 05:46:00Z adrian $");
+__FBSDID("$FreeBSD: head/sys/dev/ath/if_ath_tx_ht.c 238711 2012-07-23 03:55:19Z adrian $");
 
 #include "opt_inet.h"
 #include "opt_ath.h"
@@ -96,9 +96,10 @@
  */
 #define	IEEE80211_AMPDU_SUBFRAME_DEFAULT		32
 
-#define	ATH_AGGR_DELIM_SZ	4	/* delimiter size   */
+#define	ATH_AGGR_DELIM_SZ	4	/* delimiter size */
 #define	ATH_AGGR_MINPLEN	256	/* in bytes, minimum packet length */
-#define	ATH_AGGR_ENCRYPTDELIM	10	/* number of delimiters for encryption padding */
+/* number of delimiters for encryption padding */
+#define	ATH_AGGR_ENCRYPTDELIM	10
 
 /*
  * returns delimiter padding required given the packet length
@@ -414,7 +415,7 @@
 	int amin = 65530;
 	int i;
 
-	for (i = 0; i < 4; i++) {
+	for (i = 0; i < ATH_RC_NUM; i++) {
 		if (bf->bf_state.bfs_rc[i].tries == 0)
 			continue;
 		amin = MIN(amin, bf->bf_state.bfs_rc[i].max4msframelen);
@@ -438,7 +439,6 @@
 ath_rateseries_setup(struct ath_softc *sc, struct ieee80211_node *ni,
     struct ath_buf *bf, HAL_11N_RATE_SERIES *series)
 {
-#define	HT_RC_2_STREAMS(_rc)	((((_rc) & 0x78) >> 3) + 1)
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ath_hal *ah = sc->sc_ah;
 	HAL_BOOL shortPreamble = AH_FALSE;
@@ -466,7 +466,7 @@
 	 * XXX fields.
 	 */
 	memset(series, 0, sizeof(HAL_11N_RATE_SERIES) * 4);
-	for (i = 0; i < 4;  i++) {
+	for (i = 0; i < ATH_RC_NUM;  i++) {
 		/* Only set flags for actual TX attempts */
 		if (rc[i].tries == 0)
 			continue;
@@ -512,7 +512,10 @@
 
 		series[i].Rate = rt->info[rc[i].rix].rateCode;
 
-		/* PktDuration doesn't include slot, ACK, RTS, etc timing - it's just the packet duration */
+		/*
+		 * PktDuration doesn't include slot, ACK, RTS, etc timing -
+		 * it's just the packet duration
+		 */
 		if (series[i].Rate & IEEE80211_RATE_MCS) {
 			series[i].PktDuration =
 			    ath_computedur_ht(pktlen
@@ -528,16 +531,16 @@
 			    rt, pktlen, rc[i].rix, shortPreamble);
 		}
 	}
-#undef	HT_RC_2_STREAMS
 }
 
 #if 0
 static void
-ath_rateseries_print(HAL_11N_RATE_SERIES *series)
+ath_rateseries_print(struct ath_softc *sc, HAL_11N_RATE_SERIES *series)
 {
 	int i;
-	for (i = 0; i < 4; i++) {
-		printf("series %d: rate %x; tries %d; pktDuration %d; chSel %d; rateFlags %x\n",
+	for (i = 0; i < ATH_RC_NUM; i++) {
+		device_printf(sc->sc_dev ,"series %d: rate %x; tries %d; "
+		    "pktDuration %d; chSel %d; rateFlags %x\n",
 		    i,
 		    series[i].Rate,
 		    series[i].Tries,
@@ -582,19 +585,34 @@
 
 #if 0
 	printf("pktlen: %d; flags 0x%x\n", pktlen, flags);
-	ath_rateseries_print(series);
+	ath_rateseries_print(sc, series);
 #endif
 
 	/* Set rate scenario */
+	/*
+	 * Note: Don't allow hardware to override the duration on
+	 * ps-poll packets.
+	 */
 	ath_hal_set11nratescenario(ah, ds,
 	    !is_pspoll,	/* whether to override the duration or not */
-			/* don't allow hardware to override the duration on ps-poll packets */
 	    ctsrate,	/* rts/cts rate */
 	    series,	/* 11n rate series */
 	    4,		/* number of series */
 	    flags);
 
 	/* Setup the last descriptor in the chain */
+	/*
+	 * XXX Why is this done here, and not in the upper layer?
+	 * The rate control code stores a copy of the RC info in
+	 * the last descriptor as well as the first, then uses
+	 * the shadow copy in the last descriptor to see what the RC
+	 * decisions were.  I'm not sure why; perhaps earlier hardware
+	 * overwrote the first descriptor contents.
+	 *
+	 * In the 802.11n case, it also clears the moreaggr/delim
+	 * fields.  Again, this should be done by the caller of
+	 * ath_buf_set_rate().
+	 */
 	ath_hal_setuplasttxdesc(ah, lastds, ds);
 
 	/* Set burst duration */
@@ -630,8 +648,9 @@
  * descriptor setup, and ath_buf_set_rate() will configure the
  * rate control.
  *
- * Note that the TID lock is only grabbed when dequeuing packets from
- * the TID queue. If some code in another thread adds to the head of this
+ * The TID lock is required for the entirety of this function.
+ *
+ * If some code in another thread adds to the head of this
  * list, very strange behaviour will occur. Since retransmission is the
  * only reason this will occur, and this routine is designed to be called
  * from within the scheduler task, it won't ever clash with the completion
@@ -641,10 +660,10 @@
  * dispatch aggregate frames to the hardware), please keep this in mind.
  */
 ATH_AGGR_STATUS
-ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an, struct ath_tid *tid,
-    ath_bufhead *bf_q)
+ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid, ath_bufhead *bf_q)
 {
-	struct ieee80211_node *ni = &an->an_node;
+	//struct ieee80211_node *ni = &an->an_node;
 	struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
 	int nframes = 0;
 	uint16_t aggr_limit = 0, al = 0, bpad = 0, al_delta, h_baw;
@@ -751,74 +770,11 @@
 		    (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA);
 
 		/*
-		 * TODO: If it's _before_ the BAW left edge, complain very
-		 * loudly.
-		 *
-		 * This means something (else) has slid the left edge along
-		 * before we got a chance to be TXed.
-		 */
-
-		/*
-		 * Check if we have space in the BAW for this frame before
-		 * we add it.
-		 *
-		 * see ath_tx_xmit_aggr() for more info.
-		 */
-		if (bf->bf_state.bfs_dobaw) {
-			ieee80211_seq seqno;
-
-			/*
-			 * If the sequence number is allocated, use it.
-			 * Otherwise, use the sequence number we WOULD
-			 * allocate.
-			 */
-			if (bf->bf_state.bfs_seqno_assigned)
-				seqno = SEQNO(bf->bf_state.bfs_seqno);
-			else
-				seqno = ni->ni_txseqs[bf->bf_state.bfs_tid];
-
-			/*
-			 * Check whether either the currently allocated
-			 * sequence number _OR_ the to-be allocated
-			 * sequence number is inside the BAW.
-			 */
-			if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
-			    seqno)) {
-				status = ATH_AGGR_BAW_CLOSED;
-				break;
-			}
-
-			/* XXX check for bfs_need_seqno? */
-			if (! bf->bf_state.bfs_seqno_assigned) {
-				int seqno;
-				seqno = ath_tx_tid_seqno_assign(sc, ni, bf, bf->bf_m);
-				if (seqno < 0) {
-					device_printf(sc->sc_dev,
-					    "%s: bf=%p, huh, seqno=-1?\n",
-					    __func__,
-					    bf);
-					/* XXX what can we even do here? */
-				}
-				/* Flush seqno update to RAM */
-				/*
-				 * XXX This is required because the dmasetup
-				 * XXX is done early rather than at dispatch
-				 * XXX time. Ew, we should fix this!
-				 */
-				bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
-				    BUS_DMASYNC_PREWRITE);
-			}
-		}
-
-		/*
 		 * If the packet has a sequence number, do not
 		 * step outside of the block-ack window.
 		 */
 		if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
 		    SEQNO(bf->bf_state.bfs_seqno))) {
-			device_printf(sc->sc_dev,
-			    "%s: bf=%p, seqno=%d, outside?!\n",
-			    __func__, bf, SEQNO(bf->bf_state.bfs_seqno));
 			status = ATH_AGGR_BAW_CLOSED;
 			break;
 		}
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_athdfs.h
--- a/head/sys/dev/ath/if_athdfs.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_athdfs.h	Wed Jul 25 17:07:47 2012 +0300
@@ -26,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/if_athdfs.h 230658 2012-01-28 21:44:42Z adrian $
+ * $FreeBSD: head/sys/dev/ath/if_athdfs.h 237526 2012-06-24 08:09:06Z adrian $
  */
 #ifndef	__IF_ATHDFS_H__
 #define	__IF_ATHDFS_H__
@@ -35,7 +35,7 @@
 extern	int ath_dfs_detach(struct ath_softc *sc);
 extern	int ath_dfs_radar_enable(struct ath_softc *,
     struct ieee80211_channel *chan);
-extern	void ath_dfs_process_phy_err(struct ath_softc *sc, const char *buf,
+extern	void ath_dfs_process_phy_err(struct ath_softc *sc, struct mbuf *m,
     uint64_t tsf, struct ath_rx_status *rxstat);
 extern	int ath_dfs_process_radar_event(struct ath_softc *sc,
     struct ieee80211_channel *chan);
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_athioctl.h
--- a/head/sys/dev/ath/if_athioctl.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_athioctl.h	Wed Jul 25 17:07:47 2012 +0300
@@ -26,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/if_athioctl.h 234090 2012-04-10 07:23:37Z adrian $
+ * $FreeBSD: head/sys/dev/ath/if_athioctl.h 238638 2012-07-20 02:17:48Z adrian $
  */
 
 /*
@@ -161,11 +161,14 @@
 	u_int32_t	ast_tx_aggr_ok;		/* aggregate TX ok */
 	u_int32_t	ast_tx_aggr_fail;	/* aggregate TX failed */
 	u_int32_t	ast_tx_mcastq_overflow;	/* multicast queue overflow */
-	u_int32_t	ast_pad[1];
+	u_int32_t	ast_rx_keymiss;
+
+	u_int32_t	ast_pad[16];
 };
 
 #define	SIOCGATHSTATS	_IOWR('i', 137, struct ifreq)
 #define	SIOCZATHSTATS	_IOWR('i', 139, struct ifreq)
+#define	SIOCGATHAGSTATS	_IOWR('i', 141, struct ifreq)
 
 struct ath_diag {
 	char	ad_name[IFNAMSIZ];	/* if name, e.g. "ath0" */
@@ -183,10 +186,56 @@
 #define	SIOCGATHDIAG	_IOWR('i', 138, struct ath_diag)
 #define	SIOCGATHPHYERR	_IOWR('i', 140, struct ath_diag)
 
+
+/*
+ * The rate control ioctl has to support multiple potential rate
+ * control classes.  For now, instead of trying to support an
+ * abstraction for this in the API, let's just use a TLV
+ * representation for the payload and let userspace sort it out.
+ */
+struct ath_rateioctl_tlv {
+	uint16_t	tlv_id;
+	uint16_t	tlv_len;	/* length excluding TLV header */
+};
+
+/*
+ * This is purely the six byte MAC address.
+ */
+#define	ATH_RATE_TLV_MACADDR		0xaab0
+
+/*
+ * The rate control modules may decide to push a mapping table
+ * of rix -> net80211 ratecode as part of the update.
+ */
+#define	ATH_RATE_TLV_RATETABLE_NENTRIES	64
+struct ath_rateioctl_rt {
+	uint16_t	nentries;
+	uint16_t	pad[1];
+	uint8_t		ratecode[ATH_RATE_TLV_RATETABLE_NENTRIES];
+};
+#define	ATH_RATE_TLV_RATETABLE		0xaab1
+
+/*
+ * This is the sample node statistics structure.
+ * More in ath_rate/sample/sample.h.
+ */
+#define	ATH_RATE_TLV_SAMPLENODE		0xaab2
+
+struct ath_rateioctl {
+	char	if_name[IFNAMSIZ];	/* if name */
+	union {
+		uint8_t		macaddr[IEEE80211_ADDR_LEN];
+		uint64_t	pad;
+	} is_u;
+	uint32_t		len;
+	caddr_t			buf;
+};
+#define	SIOCGATHNODERATESTATS	_IOWR('i', 149, struct ath_rateioctl)
+
 /*
  * Radio capture format.
  */
-#define ATH_RX_RADIOTAP_PRESENT (		\
+#define ATH_RX_RADIOTAP_PRESENT_BASE (		\
 	(1 << IEEE80211_RADIOTAP_TSFT)		| \
 	(1 << IEEE80211_RADIOTAP_FLAGS)		| \
 	(1 << IEEE80211_RADIOTAP_RATE)		| \
@@ -196,8 +245,80 @@
 	(1 << IEEE80211_RADIOTAP_XCHANNEL)	| \
 	0)
 
+#ifdef	ATH_ENABLE_RADIOTAP_VENDOR_EXT
+#define	ATH_RX_RADIOTAP_PRESENT \
+	(ATH_RX_RADIOTAP_PRESENT_BASE		| \
+	(1 << IEEE80211_RADIOTAP_VENDOREXT)	| \
+	(1 << IEEE80211_RADIOTAP_EXT)		| \
+	0)
+#else
+#define	ATH_RX_RADIOTAP_PRESENT	ATH_RX_RADIOTAP_PRESENT_BASE
+#endif	/* ATH_ENABLE_RADIOTAP_PRESENT */
+
+#ifdef	ATH_ENABLE_RADIOTAP_VENDOR_EXT
+/*
+ * This is higher than the vendor bitmap used inside
+ * the Atheros reference codebase.
+ */
+
+/* Bit 8 */
+#define	ATH_RADIOTAP_VENDOR_HEADER	8
+
+/*
+ * Using four chains makes all the fields in the
+ * per-chain info header be 4-byte aligned.
+ */
+#define	ATH_RADIOTAP_MAX_CHAINS		4
+
+/*
+ * The vendor radiotap header data needs to be:
+ *
+ * + Aligned to a 4 byte address
+ * + .. so all internal fields are 4 bytes aligned;
+ * + .. and no 64 bit fields are allowed.
+ *
+ * So padding is required to ensure this is the case.
+ *
+ * Note that because of the lack of alignment with the
+ * vendor header (6 bytes), the first field must be
+ * two bytes so it can be accessed by alignment-strict
+ * platform (eg MIPS.)
+ */
+struct ath_radiotap_vendor_hdr {		/* 30 bytes */
+	uint8_t		vh_version;		/* 1 */
+	uint8_t		vh_rx_chainmask;	/* 1 */
+
+	/* At this point it should be 4 byte aligned */
+	uint32_t	evm[ATH_RADIOTAP_MAX_CHAINS];	/* 4 * 4 = 16 */
+
+	uint8_t		rssi_ctl[ATH_RADIOTAP_MAX_CHAINS];	/* 4 */
+	uint8_t		rssi_ext[ATH_RADIOTAP_MAX_CHAINS];	/* 4 */
+
+	uint8_t		vh_phyerr_code;	/* Phy error code, or 0xff */
+	uint8_t		vh_rs_status;	/* RX status */
+	uint8_t		vh_rssi;	/* Raw RSSI */
+	uint8_t		vh_pad1[1];	/* Pad to 4 byte boundary */
+} __packed;
+#endif	/* ATH_ENABLE_RADIOTAP_VENDOR_EXT */
+
 struct ath_rx_radiotap_header {
 	struct ieee80211_radiotap_header wr_ihdr;
+
+#ifdef	ATH_ENABLE_RADIOTAP_VENDOR_EXT
+	/* Vendor extension header bitmap */
+	uint32_t	wr_ext_bitmap;          /* 4 */
+
+	/*
+	 * This padding is needed because:
+	 * + the radiotap header is 8 bytes;
+	 * + the extension bitmap is 4 bytes;
+	 * + the tsf is 8 bytes, so it must start on an 8 byte
+	 *   boundary.
+	 */
+	uint32_t	wr_pad1;
+#endif	/* ATH_ENABLE_RADIOTAP_VENDOR_EXT */
+
+	/* Normal radiotap fields */
 	u_int64_t	wr_tsf;
 	u_int8_t	wr_flags;
 	u_int8_t	wr_rate;
@@ -209,6 +330,26 @@
 	u_int16_t	wr_chan_freq;
 	u_int8_t	wr_chan_ieee;
 	int8_t		wr_chan_maxpow;
+
+#ifdef	ATH_ENABLE_RADIOTAP_VENDOR_EXT
+	/*
+	 * Vendor header section, as required by the
+	 * presence of the vendor extension bit and bitmap
+	 * entry.
+	 *
+	 * XXX This must be aligned to a 4 byte address?
+	 * XXX or 8 byte address?
+	 */
+	struct ieee80211_radiotap_vendor_header wr_vh;  /* 6 bytes */
+
+	/*
+	 * Because of the lack of alignment enforced by the above
+	 * header, this vendor section won't be aligned in any
+	 * useful way.  So, this will include a two-byte version
+	 * value which will force the structure to be 4-byte aligned.
+	 */
+	struct ath_radiotap_vendor_hdr wr_v;
+#endif	/* ATH_ENABLE_RADIOTAP_VENDOR_EXT */
 } __packed;
 
 #define ATH_TX_RADIOTAP_PRESENT (		\
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_athrate.h
--- a/head/sys/dev/ath/if_athrate.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_athrate.h	Wed Jul 25 17:07:47 2012 +0300
@@ -27,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/if_athrate.h 227364 2011-11-08 22:43:13Z adrian $
+ * $FreeBSD: head/sys/dev/ath/if_athrate.h 238632 2012-07-20 01:27:20Z adrian $
  */
 #ifndef _ATH_RATECTRL_H_
 #define _ATH_RATECTRL_H_
@@ -150,4 +150,16 @@
 void	ath_rate_tx_complete(struct ath_softc *, struct ath_node *,
 		const struct ath_rc_series *, const struct ath_tx_status *,
 		int pktlen, int nframes, int nbad);
+
+/*
+ * Fetch the global rate control statistics.
+ */
+int	ath_rate_fetch_stats(struct ath_softc *sc, struct ath_rateioctl *rs);
+
+/*
+ * Fetch the per-node statistics.
+ */
+int	ath_rate_fetch_node_stats(struct ath_softc *sc, struct ath_node *an,
+		struct ath_rateioctl *rs);
+
 #endif /* _ATH_RATECTRL_H_ */
diff -r 8a9451a986e0 -r 8f13631d24de head/sys/dev/ath/if_athvar.h
--- a/head/sys/dev/ath/if_athvar.h	Wed Jul 25 17:04:43 2012 +0300
+++ b/head/sys/dev/ath/if_athvar.h	Wed Jul 25 17:07:47 2012 +0300
@@ -26,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: head/sys/dev/ath/if_athvar.h 234369 2012-04-17 06:02:41Z adrian $
+ * $FreeBSD: head/sys/dev/ath/if_athvar.h 238731 2012-07-24 01:18:19Z adrian $
  */
 
 /*
@@ -44,10 +44,18 @@
 #define	ATH_TIMEOUT		1000
 
 /*
+ * There is a separate TX ath_buf pool for management frames.
+ * This ensures that management frames such as probe responses
+ * and BAR frames can be transmitted during periods of high
+ * TX activity.
+ */
+#define	ATH_MGMT_TXBUF		32
+
+/*
  * 802.11n requires more TX and RX buffers to do AMPDU.
  */
 #ifdef	ATH_ENABLE_11N
-#define	ATH_TXBUF	128
+#define	ATH_TXBUF	512
 #define	ATH_RXBUF	512
 #endif
 
@@ -106,6 +114,7 @@
 	TAILQ_ENTRY(ath_tid)	axq_qelem;
 	int			sched;
 	int			paused;	/* >0 if the TID has been paused */
+	int			addba_tx_pending;	/* TX ADDBA pending */
 	int			bar_wait;	/* waiting for BAR */
 	int			bar_tx;		/* BAR TXed */
 
@@ -171,10 +180,16 @@
 	((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
 #define	ATH_RSSI(x)		ATH_EP_RND(x, HAL_RSSI_EP_MULTIPLIER)
 
+typedef enum {
+	ATH_BUFTYPE_NORMAL	= 0,
+	ATH_BUFTYPE_MGMT	= 1,
+} ath_buf_type_t;
+
 struct ath_buf {
 	TAILQ_ENTRY(ath_buf)	bf_list;
 	struct ath_buf *	bf_next;	/* next buffer in the aggregate */
 	int			bf_nseg;
+	HAL_STATUS		bf_rxstatus;
 	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 */
@@ -197,14 +212,15 @@
 
 	/* This state is kept to support software retries and aggregation */
 	struct {
-		int bfs_seqno;		/* sequence number of this packet */
-		int bfs_retries;	/* retry count */
-		uint16_t bfs_tid;	/* packet TID (or TID_MAX for no QoS) */
-		uint16_t bfs_pri;	/* packet AC priority */
+		uint16_t bfs_seqno;	/* sequence number of this packet */
+		uint16_t bfs_ndelim;	/* number of delims for padding */
+
+		uint8_t bfs_retries;	/* retry count */
+		uint8_t bfs_tid;	/* packet TID (or TID_MAX for no QoS) */
+		uint8_t bfs_nframes;	/* number of frames in aggregate */
+		uint8_t bfs_pri;	/* packet AC priority */
+
 		struct ath_txq *bfs_txq;	/* eventual dest hardware TXQ */
-		uint16_t bfs_pktdur;	/* packet duration (at current rate?) */
-		uint16_t bfs_nframes;	/* number of frames in aggregate */
-		uint16_t bfs_ndelim;	/* number of delims for padding */
 
 		u_int32_t bfs_aggr:1,		/* part of aggregate? */
 		    bfs_aggrburst:1,	/* part of aggregate burst? */
@@ -215,36 +231,44 @@
 		    bfs_istxfrag:1,	/* is fragmented */
 		    bfs_ismrr:1,	/* do multi-rate TX retry */
 		    bfs_doprot:1,	/* do RTS/CTS based protection */
-		    bfs_doratelookup:1,	/* do rate lookup before each TX */
-		    bfs_need_seqno:1,	/* need to assign a seqno for aggr */
-		    bfs_seqno_assigned:1;	/* seqno has been assigned */
-
-		int bfs_nfl;		/* next fragment length */
+		    bfs_doratelookup:1;	/* do rate lookup before each TX */
 
 		/*
 		 * These fields are passed into the
 		 * descriptor setup functions.
 		 */
+
+		/* Make this an 8 bit value? */
 		HAL_PKT_TYPE bfs_atype;	/* packet type */
-		int bfs_pktlen;		/* length of this packet */
-		int bfs_hdrlen;		/* length of this packet header */
+
+		uint32_t bfs_pktlen;	/* length of this packet */
+
+		uint16_t bfs_hdrlen;	/* length of this packet header */
 		uint16_t bfs_al;	/* length of aggregate */
-		int bfs_txflags;	/* HAL (tx) descriptor flags */
-		int bfs_txrate0;	/* first TX rate */
-		int bfs_try0;		/* first try count */
+
+		uint16_t bfs_txflags;	/* HAL (tx) descriptor flags */
+		uint8_t bfs_txrate0;	/* first TX rate */
+		uint8_t bfs_try0;		/* first try count */
+
+		uint16_t bfs_txpower;	/* tx power */
 		uint8_t bfs_ctsrate0;	/* Non-zero - use this as ctsrate */
-		int bfs_keyix;		/* crypto key index */
-		int bfs_txpower;	/* tx power */
-		int bfs_txantenna;	/* TX antenna config */
+		uint8_t bfs_ctsrate;	/* CTS rate */
+
+		/* 16 bit? */
+		int32_t bfs_keyix;		/* crypto key index */
+		int32_t bfs_txantenna;	/* TX antenna config */
+
+		/* Make this an 8 bit value? */
 		enum ieee80211_protmode bfs_protmode;
-		HAL_11N_RATE_SERIES bfs_rc11n[ATH_RC_NUM];	/* 11n TX series */
-		int bfs_ctsrate;	/* CTS rate */
-		int bfs_ctsduration;	/* CTS duration (pre-11n NICs) */
+
+		/* 16 bit? */
+		uint32_t bfs_ctsduration;	/* CTS duration (pre-11n NICs) */
 		struct ath_rc_series bfs_rc[ATH_RC_NUM];	/* non-11n TX series */
 	} bf_state;
 };
 typedef TAILQ_HEAD(ath_bufhead_s, ath_buf) ath_bufhead;
 
+#define	ATH_BUF_MGMT	0x00000001	/* (tx) desc is a mgmt desc */
 #define	ATH_BUF_BUSY	0x00000002	/* (tx) desc owned by h/w */
 
 /*
@@ -253,6 +277,7 @@
 struct ath_descdma {
 	const char*		dd_name;
 	struct ath_desc		*dd_desc;	/* descriptors */
+	int			dd_descsize;	/* size of single descriptor */
 	bus_addr_t		dd_desc_paddr;	/* physical addr of dd_desc */
 	bus_size_t		dd_desc_len;	/* size of dd_desc */
 	bus_dma_segment_t	dd_dseg;
@@ -304,6 +329,9 @@
 #define	ATH_TXQ_LOCK_ASSERT(_tq)	mtx_assert(&(_tq)->axq_lock, MA_OWNED)
 #define	ATH_TXQ_IS_LOCKED(_tq)		mtx_owned(&(_tq)->axq_lock)
 
+#define	ATH_TID_LOCK_ASSERT(_sc, _tid)	\
+	    ATH_TXQ_LOCK_ASSERT((_sc)->sc_ac2q[(_tid)->ac])
+
 #define ATH_TXQ_INSERT_HEAD(_tq, _elm, _field) do { \
 	TAILQ_INSERT_HEAD(&(_tq)->axq_q, (_elm), _field); \
 	(_tq)->axq_depth++; \
@@ -346,12 +374,40 @@
 	ATH_RESET_FULL = 2,
 } ATH_RESET_TYPE;
 
+struct ath_rx_methods {
+	void		(*recv_stop)(struct ath_softc *sc, int dodelay);
+	int		(*recv_start)(struct ath_softc *sc);
+	void		(*recv_flush)(struct ath_softc *sc);
+	void		(*recv_tasklet)(void *arg, int npending);
+	int		(*recv_rxbuf_init)(struct ath_softc *sc,
+			    struct ath_buf *bf);
+	int		(*recv_setup)(struct ath_softc *sc);
+	int		(*recv_teardown)(struct ath_softc *sc);
+};
+
+/*
+ * Represent the current state of the RX FIFO.
+ */
+struct ath_rx_edma {
+	struct ath_buf	**m_fifo;
+	int		m_fifolen;
+	int		m_fifo_head;
+	int		m_fifo_tail;
+	int		m_fifo_depth;
+	struct mbuf	*m_rxpending;
+};
+
+struct ath_tx_methods {
+	int		(*xmit_setup)(struct ath_softc *sc);
+	int		(*xmit_teardown)(struct ath_softc *sc);
+};
+
 struct ath_softc {
 	struct ifnet		*sc_ifp;	/* interface common */
 	struct ath_stats	sc_stats;	/* interface statistics */
 	struct ath_tx_aggr_stats	sc_aggr_stats;
 	struct ath_intr_stats	sc_intr_stats;
-	int			sc_debug;
+	uint64_t		sc_debug;
 	int			sc_nvaps;	/* # vaps */
 	int			sc_nstavaps;	/* # station vaps */
 	int			sc_nmeshvaps;	/* # mbss vaps */
@@ -359,6 +415,16 @@
 	u_int8_t		sc_nbssid0;	/* # vap's using base mac */
 	uint32_t		sc_bssidmask;	/* bssid mask */
 
+	struct ath_rx_methods	sc_rx;
+	struct ath_rx_edma	sc_rxedma[HAL_NUM_RX_QUEUES];	/* HP/LP queues */
+	struct ath_tx_methods	sc_tx;
+
+	int			sc_rx_statuslen;
+	int			sc_tx_desclen;
+	int			sc_tx_statuslen;
+	int			sc_tx_nmaps;	/* Number of TX maps */
+	int			sc_edma_bufsize;
+
 	void 			(*sc_node_cleanup)(struct ieee80211_node *);
 	void 			(*sc_node_free)(struct ieee80211_node *);
 	device_t		sc_dev;
@@ -368,6 +434,8 @@
 	struct mtx		sc_mtx;		/* master lock (recursive) */
 	struct mtx		sc_pcu_mtx;	/* PCU access mutex */
 	char			sc_pcu_mtx_name[32];
+	struct mtx		sc_rx_mtx;	/* RX access mutex */
+	char			sc_rx_mtx_name[32];
 	struct taskqueue	*sc_tq;		/* private task queue */
 	struct ath_hal		*sc_ah;		/* Atheros HAL */
 	struct ath_ratectrl	*sc_rc;		/* tx rate control support */
@@ -402,7 +470,8 @@
 				sc_setcca   : 1,/* set/clr CCA with TDMA */
 				sc_resetcal : 1,/* reset cal state next trip */
 				sc_rxslink  : 1,/* do self-linked final descriptor */
-				sc_rxtsf32  : 1;/* RX dec TSF is 32 bits */
+				sc_rxtsf32  : 1,/* RX dec TSF is 32 bits */
+				sc_isedma   : 1;/* supports EDMA */
 	uint32_t		sc_eerd;	/* regdomain from EEPROM */
 	uint32_t		sc_eecc;	/* country code from EEPROM */
 						/* rate tables */
@@ -473,7 +542,6 @@
 
 	struct ath_descdma	sc_rxdma;	/* RX descriptors */
 	ath_bufhead		sc_rxbuf;	/* receive buffer */
-	struct mbuf		*sc_rxpending;	/* pending receive data */
 	u_int32_t		*sc_rxlink;	/* link ptr in last RX desc */
 	struct task		sc_rxtask;	/* rx int processing */
 	u_int8_t		sc_defant;	/* current default antenna */
@@ -486,6 +554,9 @@
 
 	struct ath_descdma	sc_txdma;	/* TX descriptors */
 	ath_bufhead		sc_txbuf;	/* transmit buffer */
+	int			sc_txbuf_cnt;	/* how many buffers avail */
+	struct ath_descdma	sc_txdma_mgmt;	/* mgmt TX descriptors */
+	ath_bufhead		sc_txbuf_mgmt;	/* mgmt transmit buffer */
 	struct mtx		sc_txbuflock;	/* txbuf lock */
 	char			sc_txname[12];	/* e.g. "ath0_buf" */
 	u_int			sc_txqsetup;	/* h/w queues setup */
@@ -494,6 +565,11 @@
 	struct ath_txq		*sc_ac2q[5];	/* WME AC -> h/w q map */ 
 	struct task		sc_txtask;	/* tx int processing */
 	struct task		sc_txqtask;	/* tx proc processing */
+
+	struct ath_descdma	sc_txcompdma;	/* TX EDMA completion */
+	struct mtx		sc_txcomplock;	/* TX EDMA completion lock */
+	char			sc_txcompname[12];	/* eg ath0_txcomp */
+
 	int			sc_wd_timer;	/* count down for wd timer */
 	struct callout		sc_wd_ch;	/* tx watchdog timer */
 	struct ath_tx_radiotap_header sc_tx_th;
@@ -636,6 +712,28 @@
 #define	ATH_PCU_UNLOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_pcu_mtx,	\
 		MA_NOTOWNED)
 
+/*
+ * The RX lock is primarily a(nother) workaround to ensure that the
+ * RX FIFO/list isn't modified by various execution paths.
+ * Even though RX occurs in a single context (the ath taskqueue), the
+ * RX path can be executed via various reset/channel change paths.
+ */
+#define	ATH_RX_LOCK_INIT(_sc) do {\
+	snprintf((_sc)->sc_rx_mtx_name,					\
+	    sizeof((_sc)->sc_rx_mtx_name),				\
+	    "%s RX lock",						\
+	    device_get_nameunit((_sc)->sc_dev));			\
+	mtx_init(&(_sc)->sc_rx_mtx, (_sc)->sc_rx_mtx_name,		\
+		 NULL, MTX_DEF);					\
+	} while (0)
+#define	ATH_RX_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_rx_mtx)
+#define	ATH_RX_LOCK(_sc)		mtx_lock(&(_sc)->sc_rx_mtx)
+#define	ATH_RX_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_rx_mtx)
+#define	ATH_RX_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_rx_mtx,	\
+		MA_OWNED)
+#define	ATH_RX_UNLOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_rx_mtx,	\
+		MA_NOTOWNED)
+
 #define	ATH_TXQ_SETUP(sc, i)	((sc)->sc_txqsetup & (1<<i))
 
 #define	ATH_TXBUF_LOCK_INIT(_sc) do { \
@@ -649,6 +747,19 @@
 #define	ATH_TXBUF_LOCK_ASSERT(_sc) \
 	mtx_assert(&(_sc)->sc_txbuflock, MA_OWNED)
 
+#define	ATH_TXSTATUS_LOCK_INIT(_sc) do { \
+	snprintf((_sc)->sc_txcompname, sizeof((_sc)->sc_txcompname), \
+		"%s_buf", \
+		device_get_nameunit((_sc)->sc_dev)); \
+	mtx_init(&(_sc)->sc_txcomplock, (_sc)->sc_txcompname, NULL, \
+		MTX_DEF); \
+} while (0)
+#define	ATH_TXSTATUS_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_txcomplock)
+#define	ATH_TXSTATUS_LOCK(_sc)		mtx_lock(&(_sc)->sc_txcomplock)
+#define	ATH_TXSTATUS_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_txcomplock)
+#define	ATH_TXSTATUS_LOCK_ASSERT(_sc) \
+	mtx_assert(&(_sc)->sc_txcomplock, MA_OWNED)
+
 int	ath_attach(u_int16_t, struct ath_softc *);
 int	ath_detach(struct ath_softc *);
 void	ath_resume(struct ath_softc *);
@@ -705,8 +816,8 @@
 	((*(_ah)->ah_setMulticastFilter)((_ah), (_mfilt0), (_mfilt1)))
 #define	ath_hal_waitforbeacon(_ah, _bf) \
 	((*(_ah)->ah_waitForBeaconDone)((_ah), (_bf)->bf_daddr))
-#define	ath_hal_putrxbuf(_ah, _bufaddr) \
-	((*(_ah)->ah_setRxDP)((_ah), (_bufaddr)))
+#define	ath_hal_putrxbuf(_ah, _bufaddr, _rxq) \
+	((*(_ah)->ah_setRxDP)((_ah), (_bufaddr), (_rxq)))
 /* NB: common across all chips */
 #define	AR_TSF_L32	0x804c	/* MAC local clock lower 32 bits */
 #define	ath_hal_gettsf32(_ah) \
@@ -723,8 +834,8 @@
 	((*(_ah)->ah_getTxDP)((_ah), (_q)))
 #define	ath_hal_numtxpending(_ah, _q) \
 	((*(_ah)->ah_numTxPending)((_ah), (_q)))
-#define	ath_hal_getrxbuf(_ah) \
-	((*(_ah)->ah_getRxDP)((_ah)))
+#define	ath_hal_getrxbuf(_ah, _rxq) \
+	((*(_ah)->ah_getRxDP)((_ah), (_rxq)))
 #define	ath_hal_txstart(_ah, _q) \
 	((*(_ah)->ah_startTxDma)((_ah), (_q)))
 #define	ath_hal_setchannel(_ah, _chan) \
@@ -913,8 +1024,34 @@
 #define	ath_hal_setintmit(_ah, _v) \
 	ath_hal_setcapability(_ah, HAL_CAP_INTMIT, \
 	HAL_CAP_INTMIT_ENABLE, _v, NULL)
+
+/* EDMA definitions */
+#define	ath_hal_hasedma(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_ENHANCED_DMA_SUPPORT,	\
+	0, NULL) == HAL_OK)
+#define	ath_hal_getrxfifodepth(_ah, _qtype, _req) \
+	(ath_hal_getcapability(_ah, HAL_CAP_RXFIFODEPTH, _qtype, _req)	\
+	== HAL_OK)
+#define	ath_hal_getntxmaps(_ah, _req) \
+	(ath_hal_getcapability(_ah, HAL_CAP_NUM_TXMAPS, 0, _req)	\
+	== HAL_OK)
+#define	ath_hal_gettxdesclen(_ah, _req) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TXDESCLEN, 0, _req)		\
+	== HAL_OK)
+#define	ath_hal_gettxstatuslen(_ah, _req) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TXSTATUSLEN, 0, _req)	\
+	== HAL_OK)
+#define	ath_hal_getrxstatuslen(_ah, _req) \
+	(ath_hal_getcapability(_ah, HAL_CAP_RXSTATUSLEN, 0, _req)	\
+	== HAL_OK)
+#define	ath_hal_setrxbufsize(_ah, _req) \
+	(ath_hal_setcapability(_ah, HAL_CAP_RXBUFSIZE, 0, _req, NULL)	\
+	== HAL_OK)
+
 #define	ath_hal_getchannoise(_ah, _c) \
 	((*(_ah)->ah_getChanNoise)((_ah), (_c)))
+
+/* 802.11n HAL methods */
 #define	ath_hal_getrxchainmask(_ah, _prxchainmask) \
 	(ath_hal_getcapability(_ah, HAL_CAP_RX_CHAINMASK, 0, _prxchainmask))
 #define	ath_hal_gettxchainmask(_ah, _ptxchainmask) \
@@ -956,6 +1093,15 @@
 	((*(_ah)->ah_getTxIntrQueue)((_ah), (_txqs)))
 #define ath_hal_gettxcompletionrates(_ah, _ds, _rates, _tries) \
 	((*(_ah)->ah_getTxCompletionRates)((_ah), (_ds), (_rates), (_tries)))
+#define ath_hal_settxdesclink(_ah, _ds, _link) \
+	((*(_ah)->ah_setTxDescLink)((_ah), (_ds), (_link)))
+#define ath_hal_gettxdesclink(_ah, _ds, _link) \
+	((*(_ah)->ah_getTxDescLink)((_ah), (_ds), (_link)))
+#define ath_hal_gettxdesclinkptr(_ah, _ds, _linkptr) \
+	((*(_ah)->ah_getTxDescLinkPtr)((_ah), (_ds), (_linkptr)))
+#define	ath_hal_setuptxstatusring(_ah, _tsstart, _tspstart, _size) \
+	((*(_ah)->ah_setupTxStatusRing)((_ah), (_tsstart), (_tspstart), \
+		(_size)))
 
 #define	ath_hal_setupfirsttxdesc(_ah, _ds, _aggrlen, _flags, _txpower, \
 		_txr0, _txtr0, _antm, _rcr, _rcd) \
@@ -995,6 +1141,14 @@
 	((*(_ah)->ah_gpioSetIntr)((_ah), (_gpio), (_b)))
 
 /*
+ * PCIe suspend/resume/poweron/poweroff related macros
+ */
+#define	ath_hal_enablepcie(_ah, _restore, _poweroff) \
+	((*(_ah)->ah_configPCIE)((_ah), (_restore), (_poweroff)))
+#define	ath_hal_disablepcie(_ah) \
+	((*(_ah)->ah_disablePCIE)((_ah)))
+
+/*
  * This is badly-named; you need to set the correct parameters
  * to begin to receive useful radar events; and even then
  * it doesn't "enable" DFS. See the ath_dfs/null/ module for
@@ -1011,6 +1165,8 @@
 	((*(_ah)->ah_isFastClockEnabled)((_ah)))
 #define	ath_hal_radar_wait(_ah, _chan) \
 	((*(_ah)->ah_radarWait)((_ah), (_chan)))
+#define	ath_hal_get_mib_cycle_counts(_ah, _sample) \
+	((*(_ah)->ah_getMibCycleCounts)((_ah), (_sample)))
 #define	ath_hal_get_chan_ext_busy(_ah) \
 	((*(_ah)->ah_get11nExtBusy)((_ah)))
 


More information about the Zrouter-src-freebsd mailing list