[Midnightbsd-cvs] src [10127] trunk/sys/dev/ath: update ath

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sun May 27 20:25:21 EDT 2018


Revision: 10127
          http://svnweb.midnightbsd.org/src/?rev=10127
Author:   laffer1
Date:     2018-05-27 20:25:20 -0400 (Sun, 27 May 2018)
Log Message:
-----------
update ath

Modified Paths:
--------------
    trunk/sys/dev/ath/ah_osdep.c
    trunk/sys/dev/ath/ah_osdep.h
    trunk/sys/dev/ath/ath_dfs/null/dfs_null.c
    trunk/sys/dev/ath/ath_hal/ah.c
    trunk/sys/dev/ath/ath_hal/ah.h
    trunk/sys/dev/ath/ath_hal/ah_debug.h
    trunk/sys/dev/ath/ath_hal/ah_decode.h
    trunk/sys/dev/ath/ath_hal/ah_desc.h
    trunk/sys/dev/ath/ath_hal/ah_devid.h
    trunk/sys/dev/ath/ath_hal/ah_diagcodes.h
    trunk/sys/dev/ath/ath_hal/ah_eeprom.h
    trunk/sys/dev/ath/ath_hal/ah_eeprom_9287.c
    trunk/sys/dev/ath/ath_hal/ah_eeprom_9287.h
    trunk/sys/dev/ath/ath_hal/ah_eeprom_v1.c
    trunk/sys/dev/ath/ath_hal/ah_eeprom_v1.h
    trunk/sys/dev/ath/ath_hal/ah_eeprom_v14.c
    trunk/sys/dev/ath/ath_hal/ah_eeprom_v14.h
    trunk/sys/dev/ath/ath_hal/ah_eeprom_v3.c
    trunk/sys/dev/ath/ath_hal/ah_eeprom_v3.h
    trunk/sys/dev/ath/ath_hal/ah_eeprom_v4k.c
    trunk/sys/dev/ath/ath_hal/ah_eeprom_v4k.h
    trunk/sys/dev/ath/ath_hal/ah_internal.h
    trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h
    trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h
    trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h
    trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h
    trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h
    trunk/sys/dev/ath/ath_hal/ah_regdomain.c
    trunk/sys/dev/ath/ath_hal/ah_regdomain.h
    trunk/sys/dev/ath/ath_hal/ah_soc.h
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210.h
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_beacon.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_interrupts.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_keycache.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_phy.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_power.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210desc.h
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210phy.h
    trunk/sys/dev/ath/ath_hal/ar5210/ar5210reg.h
    trunk/sys/dev/ath/ath_hal/ar5210/ar5k_0007.ini
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211.h
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_beacon.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_interrupts.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_keycache.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_phy.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_power.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211desc.h
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211phy.h
    trunk/sys/dev/ath/ath_hal/ar5211/ar5211reg.h
    trunk/sys/dev/ath/ath_hal/ar5211/boss.ini
    trunk/sys/dev/ath/ath_hal/ar5212/ar2316.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar2317.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar2413.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar2425.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5111.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5112.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212.h
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212.ini
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_beacon.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_eeprom.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_gpio.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_interrupts.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_keycache.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_phy.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_power.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_rfgain.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212desc.h
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212phy.h
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212reg.h
    trunk/sys/dev/ath/ath_hal/ar5212/ar5311reg.h
    trunk/sys/dev/ath/ath_hal/ar5212/ar5413.c
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312.h
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312_eeprom.c
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312_gpio.c
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312_interrupts.c
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312_misc.c
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312_power.c
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312_reset.c
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312phy.h
    trunk/sys/dev/ath/ath_hal/ar5312/ar5312reg.h
    trunk/sys/dev/ath/ath_hal/ar5312/ar5315_gpio.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar2133.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416.h
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416.ini
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal.h
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_eeprom.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_keycache.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_phy.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_power.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416desc.h
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416phy.h
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416reg.h
    trunk/sys/dev/ath/ath_hal/ar9001/ar9130.ini
    trunk/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c
    trunk/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.c
    trunk/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.h
    trunk/sys/dev/ath/ath_hal/ar9001/ar9130_phy.c
    trunk/sys/dev/ath/ath_hal/ar9001/ar9130_phy.h
    trunk/sys/dev/ath/ath_hal/ar9001/ar9130reg.h
    trunk/sys/dev/ath/ath_hal/ar9001/ar9160.ini
    trunk/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9002phy.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280v1.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280v2.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_cal.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_cal.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_phy.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_phy.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_reset.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285an.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285phy.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285v2.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287_cal.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287_cal.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287_olc.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287_olc.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287_reset.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287an.h
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287phy.h
    trunk/sys/dev/ath/ath_rate/amrr/amrr.c
    trunk/sys/dev/ath/ath_rate/amrr/amrr.h
    trunk/sys/dev/ath/ath_rate/onoe/onoe.c
    trunk/sys/dev/ath/ath_rate/onoe/onoe.h
    trunk/sys/dev/ath/ath_rate/sample/sample.c
    trunk/sys/dev/ath/ath_rate/sample/sample.h
    trunk/sys/dev/ath/ath_rate/sample/tx_schedules.h
    trunk/sys/dev/ath/if_ath.c
    trunk/sys/dev/ath/if_ath_ahb.c
    trunk/sys/dev/ath/if_ath_debug.c
    trunk/sys/dev/ath/if_ath_debug.h
    trunk/sys/dev/ath/if_ath_keycache.c
    trunk/sys/dev/ath/if_ath_keycache.h
    trunk/sys/dev/ath/if_ath_misc.h
    trunk/sys/dev/ath/if_ath_pci.c
    trunk/sys/dev/ath/if_ath_sysctl.c
    trunk/sys/dev/ath/if_ath_sysctl.h
    trunk/sys/dev/ath/if_ath_tx.c
    trunk/sys/dev/ath/if_ath_tx.h
    trunk/sys/dev/ath/if_ath_tx_ht.c
    trunk/sys/dev/ath/if_ath_tx_ht.h
    trunk/sys/dev/ath/if_athdfs.h
    trunk/sys/dev/ath/if_athioctl.h
    trunk/sys/dev/ath/if_athrate.h
    trunk/sys/dev/ath/if_athvar.h

Added Paths:
-----------
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c
    trunk/sys/dev/ath/ath_hal/ar9003/
    trunk/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h
    trunk/sys/dev/ath/ath_hal/ar9003/ar9300_devid.h
    trunk/sys/dev/ath/if_ath_alq.c
    trunk/sys/dev/ath/if_ath_alq.h
    trunk/sys/dev/ath/if_ath_beacon.c
    trunk/sys/dev/ath/if_ath_beacon.h
    trunk/sys/dev/ath/if_ath_btcoex.c
    trunk/sys/dev/ath/if_ath_btcoex.h
    trunk/sys/dev/ath/if_ath_led.c
    trunk/sys/dev/ath/if_ath_led.h
    trunk/sys/dev/ath/if_ath_lna_div.c
    trunk/sys/dev/ath/if_ath_lna_div.h
    trunk/sys/dev/ath/if_ath_rx.c
    trunk/sys/dev/ath/if_ath_rx.h
    trunk/sys/dev/ath/if_ath_rx_edma.c
    trunk/sys/dev/ath/if_ath_rx_edma.h
    trunk/sys/dev/ath/if_ath_spectral.c
    trunk/sys/dev/ath/if_ath_spectral.h
    trunk/sys/dev/ath/if_ath_tdma.c
    trunk/sys/dev/ath/if_ath_tdma.h
    trunk/sys/dev/ath/if_ath_tsf.h
    trunk/sys/dev/ath/if_ath_tx_edma.c
    trunk/sys/dev/ath/if_ath_tx_edma.h

Property Changed:
----------------
    trunk/sys/dev/ath/ath_hal/ar5210/ar5k_0007.ini
    trunk/sys/dev/ath/ath_hal/ar5211/boss.ini
    trunk/sys/dev/ath/ath_hal/ar5212/ar5212.ini
    trunk/sys/dev/ath/ath_hal/ar5416/ar5416.ini
    trunk/sys/dev/ath/ath_hal/ar9001/ar9130.ini
    trunk/sys/dev/ath/ath_hal/ar9001/ar9160.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280v1.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9280v2.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9285v2.ini
    trunk/sys/dev/ath/ath_hal/ar9002/ar9287.ini

Modified: trunk/sys/dev/ath/ah_osdep.c
===================================================================
--- trunk/sys/dev/ath/ah_osdep.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ah_osdep.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ah_osdep.c 237864 2012-07-01 02:34:32Z adrian $
  */
 #include "opt_ah.h"
 
@@ -38,6 +39,9 @@
 #include <sys/bus.h>
 #include <sys/malloc.h>
 #include <sys/proc.h>
+#include <sys/pcpu.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
 
 #include <machine/stdarg.h>
 
@@ -44,6 +48,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
@@ -59,6 +64,17 @@
 #define	BUSTAG(ah)	((ah)->ah_st)
 #endif
 
+/*
+ * This lock is used to seralise register access for chips which have
+ * problems w/ SMP CPUs issuing concurrent PCI transactions.
+ *
+ * XXX This is a global lock for now; it should be pushed to
+ * a per-device lock in some platform-independent fashion.
+ */
+struct mtx ah_regser_mtx;
+MTX_SYSINIT(ah_regser, &ah_regser_mtx, "Atheros register access mutex",
+    MTX_SPIN);
+
 extern	void ath_hal_printf(struct ath_hal *, const char*, ...)
 		__printflike(2,3);
 extern	void ath_hal_vprintf(struct ath_hal *, const char*, __va_list)
@@ -123,13 +139,11 @@
 
 #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, ...)
 {
 	if ((mask == HAL_DEBUG_UNMASKABLE) ||
-	    (ah->ah_config.ah_debug & mask) ||
+	    (ah != NULL && ah->ah_config.ah_debug & mask) ||
 	    (ath_hal_debug & mask)) {
 		__va_list ap;
 		va_start(ap, fmt);
@@ -244,6 +258,7 @@
 		struct ale *ale = ath_hal_alq_get(ah);
 		if (ale) {
 			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->threadid = curthread->td_tid;
 			r->op = OP_WRITE;
 			r->reg = reg;
 			r->val = val;
@@ -250,12 +265,11 @@
 			alq_post(ath_hal_alq, ale);
 		}
 	}
-#if _BYTE_ORDER == _BIG_ENDIAN
-	if (OS_REG_UNSWAPPED(reg))
-		bus_space_write_4(tag, h, reg, val);
-	else
-#endif
-		bus_space_write_stream_4(tag, h, reg, val);
+	if (ah->ah_config.ah_serialise_reg_war)
+		mtx_lock_spin(&ah_regser_mtx);
+	bus_space_write_4(tag, h, reg, val);
+	if (ah->ah_config.ah_serialise_reg_war)
+		mtx_unlock_spin(&ah_regser_mtx);
 }
 
 u_int32_t
@@ -265,16 +279,16 @@
 	bus_space_handle_t h = ah->ah_sh;
 	u_int32_t val;
 
-#if _BYTE_ORDER == _BIG_ENDIAN
-	if (OS_REG_UNSWAPPED(reg))
-		val = bus_space_read_4(tag, h, reg);
-	else
-#endif
-		val = bus_space_read_stream_4(tag, h, reg);
+	if (ah->ah_config.ah_serialise_reg_war)
+		mtx_lock_spin(&ah_regser_mtx);
+	val = bus_space_read_4(tag, h, reg);
+	if (ah->ah_config.ah_serialise_reg_war)
+		mtx_unlock_spin(&ah_regser_mtx);
 	if (ath_hal_alq) {
 		struct ale *ale = ath_hal_alq_get(ah);
 		if (ale) {
 			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->threadid = curthread->td_tid;
 			r->op = OP_READ;
 			r->reg = reg;
 			r->val = val;
@@ -291,6 +305,7 @@
 		struct ale *ale = ath_hal_alq_get(ah);
 		if (ale) {
 			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->threadid = curthread->td_tid;
 			r->op = OP_MARK;
 			r->reg = id;
 			r->val = v;
@@ -316,12 +331,11 @@
 	bus_space_tag_t tag = BUSTAG(ah);
 	bus_space_handle_t h = ah->ah_sh;
 
-#if _BYTE_ORDER == _BIG_ENDIAN
-	if (OS_REG_UNSWAPPED(reg))
-		bus_space_write_4(tag, h, reg, val);
-	else
-#endif
-		bus_space_write_stream_4(tag, h, reg, val);
+	if (ah->ah_config.ah_serialise_reg_war)
+		mtx_lock_spin(&ah_regser_mtx);
+	bus_space_write_4(tag, h, reg, val);
+	if (ah->ah_config.ah_serialise_reg_war)
+		mtx_unlock_spin(&ah_regser_mtx);
 }
 
 u_int32_t
@@ -331,12 +345,11 @@
 	bus_space_handle_t h = ah->ah_sh;
 	u_int32_t val;
 
-#if _BYTE_ORDER == _BIG_ENDIAN
-	if (OS_REG_UNSWAPPED(reg))
-		val = bus_space_read_4(tag, h, reg);
-	else
-#endif
-		val = bus_space_read_stream_4(tag, h, reg);
+	if (ah->ah_config.ah_serialise_reg_war)
+		mtx_lock_spin(&ah_regser_mtx);
+	val = bus_space_read_4(tag, h, reg);
+	if (ah->ah_config.ah_serialise_reg_war)
+		mtx_unlock_spin(&ah_regser_mtx);
 	return val;
 }
 #endif /* AH_DEBUG || AH_REGOPS_FUNC */

Modified: trunk/sys/dev/ath/ah_osdep.h
===================================================================
--- trunk/sys/dev/ath/ah_osdep.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ah_osdep.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ah_osdep.h 239051 2012-08-05 10:12:27Z adrian $
  */
 #ifndef _ATH_AH_OSDEP_H_
 #define _ATH_AH_OSDEP_H_
@@ -49,6 +50,12 @@
 typedef bus_space_handle_t HAL_BUS_HANDLE;
 
 /*
+ * Although the underlying hardware may support 64 bit DMA, the
+ * current Atheros hardware only supports 32 bit addressing.
+ */
+typedef uint32_t HAL_DMA_ADDR;
+
+/*
  * Linker set writearounds for chip and RF backend registration.
  */
 #define	OS_DATA_SET(set, item)	DATA_SET(set, item)
@@ -63,6 +70,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 +83,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 +97,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
@@ -97,31 +123,6 @@
 extern	void ath_hal_reg_write(struct ath_hal *ah, u_int reg, u_int32_t val);
 extern	u_int32_t ath_hal_reg_read(struct ath_hal *ah, u_int reg);
 #else
-/*
- * The hardware registers are native little-endian byte order.
- * Big-endian hosts are handled by enabling hardware byte-swap
- * of register reads and writes at reset.  But the PCI clock
- * domain registers are not byte swapped!  Thus, on big-endian
- * platforms we have to explicitly byte-swap those registers.
- * Most of this code is collapsed at compile time because the
- * register values are constants.
- */
-#if _BYTE_ORDER == _BIG_ENDIAN
-#define OS_REG_WRITE(_ah, _reg, _val) do {				\
-	if (OS_REG_UNSWAPPED(_reg))					\
-		bus_space_write_4((bus_space_tag_t)(_ah)->ah_st,	\
-		    (bus_space_handle_t)(_ah)->ah_sh, (_reg), (_val));	\
-	else								\
-		bus_space_write_stream_4((bus_space_tag_t)(_ah)->ah_st,	\
-		    (bus_space_handle_t)(_ah)->ah_sh, (_reg), (_val));	\
-} while (0)
-#define OS_REG_READ(_ah, _reg)						\
-	(OS_REG_UNSWAPPED(_reg) ?					\
-		bus_space_read_4((bus_space_tag_t)(_ah)->ah_st,		\
-		    (bus_space_handle_t)(_ah)->ah_sh, (_reg)) :		\
-		bus_space_read_stream_4((bus_space_tag_t)(_ah)->ah_st,	\
-		    (bus_space_handle_t)(_ah)->ah_sh, (_reg)))
-#else /* _BYTE_ORDER == _LITTLE_ENDIAN */
 #define	OS_REG_WRITE(_ah, _reg, _val)					\
 	bus_space_write_4((bus_space_tag_t)(_ah)->ah_st,		\
 	    (bus_space_handle_t)(_ah)->ah_sh, (_reg), (_val))
@@ -128,8 +129,7 @@
 #define	OS_REG_READ(_ah, _reg)						\
 	bus_space_read_4((bus_space_tag_t)(_ah)->ah_st,			\
 	    (bus_space_handle_t)(_ah)->ah_sh, (_reg))
-#endif /* _BYTE_ORDER */
-#endif /* AH_DEBUG || AH_REGFUNC || AH_DEBUG_ALQ */
+#endif
 
 #ifdef AH_DEBUG_ALQ
 extern	void OS_MARK(struct ath_hal *, u_int id, u_int32_t value);

Modified: trunk/sys/dev/ath/ath_dfs/null/dfs_null.c
===================================================================
--- trunk/sys/dev/ath/ath_dfs/null/dfs_null.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_dfs/null/dfs_null.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd
  * All rights reserved.
@@ -26,14 +27,15 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_dfs/null/dfs_null.c 244946 2013-01-02 01:36:10Z adrian $
  */
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/ath_dfs/null/dfs_null.c 244946 2013-01-02 01:36:10Z adrian $");
 
 /*
  * This implements an empty DFS module.
  */
+#include "opt_ath.h"
 #include "opt_inet.h"
 #include "opt_wlan.h"
 
@@ -80,7 +82,7 @@
 int
 ath_dfs_attach(struct ath_softc *sc)
 {
-	return 1;
+	return (1);
 }
 
 /*
@@ -89,25 +91,89 @@
 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.
  */
-void
+int
 ath_dfs_radar_enable(struct ath_softc *sc, struct ieee80211_channel *chan)
 {
+#if 0
+	HAL_PHYERR_PARAM pe;
+
+	/* Check if the hardware supports radar reporting */
+	/* XXX TODO: migrate HAL_CAP_RADAR/HAL_CAP_AR to somewhere public! */
+	if (ath_hal_getcapability(sc->sc_ah,
+	    HAL_CAP_PHYDIAG, 0, NULL) != HAL_OK)
+		return (0);
+
 	/* Check if the current channel is radar-enabled */
 	if (! IEEE80211_IS_CHAN_DFS(chan))
-		return;
+		return (0);
+
+	/* Fetch the default parameters */
+	memset(&pe, '\0', sizeof(pe));
+	if (! ath_hal_getdfsdefaultthresh(sc->sc_ah, &pe))
+		return (0);
+
+	/* Enable radar PHY error reporting */
+	sc->sc_dodfs = 1;
+
+	/* Tell the hardware to enable radar reporting */
+	pe.pe_enabled = 1;
+
+	/* Flip on extension channel events only if doing HT40 */
+	if (IEEE80211_IS_CHAN_HT40(chan))
+		pe.pe_extchannel = 1;
+	else
+		pe.pe_extchannel = 0;
+
+	ath_hal_enabledfs(sc->sc_ah, &pe);
+
+	/*
+	 * Disable strong signal fast diversity - needed for
+	 * AR5212 and similar PHYs for reliable short pulse
+	 * duration.
+	 */
+	(void) ath_hal_setcapability(sc->sc_ah, HAL_CAP_DIVERSITY, 2, 0, NULL);
+
+	return (1);
+#else
+	return (0);
+#endif
 }
 
 /*
+ * Explicity disable radar reporting.
+ *
+ * Return 0 if it was disabled, < 0 on error.
+ */
+int
+ath_dfs_radar_disable(struct ath_softc *sc)
+{
+#if 0
+	HAL_PHYERR_PARAM pe;
+
+	(void) ath_hal_getdfsthresh(sc->sc_ah, &pe);
+	pe.pe_enabled = 0;
+	(void) ath_hal_enabledfs(sc->sc_ah, &pe);
+	return (0);
+#else
+	return (0);
+#endif
+}
+
+/*
  * 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)
 {
 
@@ -124,7 +190,7 @@
 ath_dfs_process_radar_event(struct ath_softc *sc,
     struct ieee80211_channel *chan)
 {
-	return 0;
+	return (0);
 }
 
 /*
@@ -137,7 +203,7 @@
 int
 ath_dfs_tasklet_needed(struct ath_softc *sc, struct ieee80211_channel *chan)
 {
-	return 0;
+	return (0);
 }
 
 /*
@@ -214,7 +280,7 @@
 		free(indata, M_TEMP);
 	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
 		free(outdata, M_TEMP);
-	return error;
+	return (error);
 }
 
 /*
@@ -224,5 +290,5 @@
 ath_dfs_get_thresholds(struct ath_softc *sc, HAL_PHYERR_PARAM *param)
 {
 	ath_hal_getdfsthresh(sc->sc_ah, param);
-	return 1;
+	return (1);
 }

Modified: trunk/sys/dev/ath/ath_hal/ah.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah.c 252236 2013-06-26 04:46:03Z adrian $
  */
 #include "opt_ah.h"
 
@@ -24,6 +25,7 @@
 #include "ah_eeprom.h"			/* for 5ghz fast clock flag */
 
 #include "ar5416/ar5416reg.h"		/* NB: includes ar5212reg.h */
+#include "ar9003/ar9300_devid.h"
 
 /* linker set of registered chips */
 OS_SET_DECLARE(ah_chips, struct ath_hal_chip);
@@ -101,9 +103,9 @@
 		return "5413";
 	case AR_SREV_VERSION_COBRA:
 		return "2415";
-	case AR_SREV_2425:
+	case AR_SREV_2425:	/* Swan */
 		return "2425";
-	case AR_SREV_2417:
+	case AR_SREV_2417:	/* Nala */
 		return "2417";
 	case AR_XSREV_VERSION_OWL_PCI:
 		return "5416";
@@ -114,11 +116,33 @@
 	case AR_XSREV_VERSION_SOWL:
 		return "9160";
 	case AR_XSREV_VERSION_MERLIN:
-		return "9280";
+		if (AH_PRIVATE(ah)->ah_ispcie)
+			return "9280";
+		return "9220";
 	case AR_XSREV_VERSION_KITE:
 		return "9285";
 	case AR_XSREV_VERSION_KIWI:
-		return "9287";
+		if (AH_PRIVATE(ah)->ah_ispcie)
+			return "9287";
+		return "9227";
+	case AR_SREV_VERSION_AR9380:
+		if (ah->ah_macRev >= AR_SREV_REVISION_AR9580_10)
+			return "9580";
+		return "9380";
+	case AR_SREV_VERSION_AR9460:
+		return "9460";
+	case AR_SREV_VERSION_AR9330:
+		return "9330";
+	case AR_SREV_VERSION_AR9340:
+		return "9340";
+	case AR_SREV_VERSION_QCA9550:
+		/* XXX should say QCA, not AR */
+		return "9550";
+	case AR_SREV_VERSION_AR9485:
+		return "9485";
+	case AR_SREV_VERSION_QCA9565:
+		/* XXX should say QCA, not AR */
+		return "9565";
 	}
 	return "????";
 }
@@ -271,36 +295,42 @@
 
 	/* 11n frame - extract out the number of spatial streams */
 	numStreams = HT_RC_2_STREAMS(rc);
-	KASSERT(numStreams == 1 || numStreams == 2, ("number of spatial streams needs to be 1 or 2: MCS rate 0x%x!", rateix));
+	KASSERT(numStreams > 0 && numStreams <= 4,
+	    ("number of spatial streams needs to be 1..3: MCS rate 0x%x!",
+	    rateix));
 
 	return ath_computedur_ht(frameLen, rc, numStreams, isht40, shortPreamble);
 }
 
+static const uint16_t ht20_bps[32] = {
+    26, 52, 78, 104, 156, 208, 234, 260,
+    52, 104, 156, 208, 312, 416, 468, 520,
+    78, 156, 234, 312, 468, 624, 702, 780,
+    104, 208, 312, 416, 624, 832, 936, 1040
+};
+static const uint16_t ht40_bps[32] = {
+    54, 108, 162, 216, 324, 432, 486, 540,
+    108, 216, 324, 432, 648, 864, 972, 1080,
+    162, 324, 486, 648, 972, 1296, 1458, 1620,
+    216, 432, 648, 864, 1296, 1728, 1944, 2160
+};
+
 /*
  * Calculate the transmit duration of an 11n frame.
- * This only works for MCS0->MCS15.
  */
 uint32_t
-ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams, HAL_BOOL isht40,
-    HAL_BOOL isShortGI)
+ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams,
+    HAL_BOOL isht40, HAL_BOOL isShortGI)
 {
-	static const uint16_t ht20_bps[16] = {
-	    26, 52, 78, 104, 156, 208, 234, 260,
-	    52, 104, 156, 208, 312, 416, 468, 520
-	};
-	static const uint16_t ht40_bps[16] = {
-	    54, 108, 162, 216, 324, 432, 486, 540,
-	    108, 216, 324, 432, 648, 864, 972, 1080,
-	};
 	uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
 
 	KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
-	KASSERT((rate &~ IEEE80211_RATE_MCS) < 16, ("bad mcs 0x%x", rate));
+	KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
 
 	if (isht40)
-		bitsPerSymbol = ht40_bps[rate & 0xf];
+		bitsPerSymbol = ht40_bps[rate & 0x1f];
 	else
-		bitsPerSymbol = ht20_bps[rate & 0xf];
+		bitsPerSymbol = ht20_bps[rate & 0x1f];
 	numBits = OFDM_PLCP_BITS + (frameLen << 3);
 	numSymbols = howmany(numBits, bitsPerSymbol);
 	if (isShortGI)
@@ -394,6 +424,50 @@
 	return txTime;
 }
 
+int
+ath_hal_get_curmode(struct ath_hal *ah, const struct ieee80211_channel *chan)
+{
+	/*
+	 * Pick a default mode at bootup. A channel change is inevitable.
+	 */
+	if (!chan)
+		return HAL_MODE_11NG_HT20;
+
+	if (IEEE80211_IS_CHAN_TURBO(chan))
+		return HAL_MODE_TURBO;
+
+	/* check for NA_HT before plain A, since IS_CHAN_A includes NA_HT */
+	if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan))
+		return HAL_MODE_11NA_HT20;
+	if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan))
+		return HAL_MODE_11NA_HT40PLUS;
+	if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan))
+		return HAL_MODE_11NA_HT40MINUS;
+	if (IEEE80211_IS_CHAN_A(chan))
+		return HAL_MODE_11A;
+
+	/* check for NG_HT before plain G, since IS_CHAN_G includes NG_HT */
+	if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan))
+		return HAL_MODE_11NG_HT20;
+	if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan))
+		return HAL_MODE_11NG_HT40PLUS;
+	if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan))
+		return HAL_MODE_11NG_HT40MINUS;
+
+	/*
+	 * XXX For FreeBSD, will this work correctly given the DYN
+	 * chan mode (OFDM+CCK dynamic) ? We have pure-G versions DYN-BG..
+	 */
+	if (IEEE80211_IS_CHAN_G(chan))
+		return HAL_MODE_11G;
+	if (IEEE80211_IS_CHAN_B(chan))
+		return HAL_MODE_11B;
+
+	HALASSERT(0);
+	return HAL_MODE_11NG_HT20;
+}
+
+
 typedef enum {
 	WIRELESS_MODE_11a   = 0,
 	WIRELESS_MODE_TURBO = 1,
@@ -444,6 +518,13 @@
 			clks <<= 1;
 	} else
 		clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b];
+
+	/* Compensate for half/quarter rate */
+	if (c != AH_NULL && IEEE80211_IS_CHAN_HALF(c))
+		clks = clks / 2;
+	else if (c != AH_NULL && IEEE80211_IS_CHAN_QUARTER(c))
+		clks = clks / 4;
+
 	return clks;
 }
 
@@ -615,6 +696,10 @@
 		return pCap->hal4AddrAggrSupport ? HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_EXT_CHAN_DFS:
 		return pCap->halExtChanDfsSupport ? HAL_OK : HAL_ENOTSUPP;
+	case HAL_CAP_RX_STBC:
+		return pCap->halRxStbcSupport ? HAL_OK : HAL_ENOTSUPP;
+	case HAL_CAP_TX_STBC:
+		return pCap->halTxStbcSupport ? HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_COMBINED_RADAR_RSSI:
 		return pCap->halUseCombinedRadarRssi ? HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_AUTO_SLEEP:
@@ -626,13 +711,47 @@
 	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:
+		*result = pCap->halNumMRRetries;
+		return HAL_OK;
 	case HAL_CAP_BT_COEX:
 		return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP;
+	case HAL_CAP_SPECTRAL_SCAN:
+		return pCap->halSpectralScanSupport ? HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_HT20_SGI:
 		return pCap->halHTSGI20Support ? HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_RXTSTAMP_PREC:	/* rx desc tstamp precision (bits) */
 		*result = pCap->halTstampPrecision;
 		return HAL_OK;
+	case HAL_CAP_ANT_DIV_COMB:	/* AR9285/AR9485 LNA diversity */
+		return pCap->halAntDivCombSupport ? HAL_OK  : HAL_ENOTSUPP;
+
 	case HAL_CAP_ENHANCED_DFS_SUPPORT:
 		return pCap->halEnhancedDfsSupport ? HAL_OK : HAL_ENOTSUPP;
 
@@ -659,6 +778,15 @@
 		return pCap->halHasRxSelfLinkedTail ? HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_LONG_RXDESC_TSF:		/* 32 bit TSF in RX descriptor? */
 		return pCap->halHasLongRxDescTsf ? HAL_OK : HAL_ENOTSUPP;
+	case HAL_CAP_BB_READ_WAR:		/* Baseband read WAR */
+		return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP;
+	case HAL_CAP_SERIALISE_WAR:		/* PCI register serialisation */
+		return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP;
+	case HAL_CAP_MFP:			/* Management frame protection setting */
+		*result = pCap->halMfpSupport;
+		return HAL_OK;
+	case HAL_CAP_RX_LNA_MIXING:	/* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */
+		return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP;
 	default:
 		return HAL_EINVAL;
 	}
@@ -940,7 +1068,7 @@
  * populated with values from NOISE_FLOOR[] + ath_hal_getNfAdjust().
  *
  * The caller must supply ctl/ext NF arrays which are at least
- * AH_MIMO_MAX_CHAINS entries long.
+ * AH_MAX_CHAINS entries long.
  */
 int
 ath_hal_get_mimo_chan_noise(struct ath_hal *ah,
@@ -956,7 +1084,7 @@
 		HALDEBUG(ah, HAL_DEBUG_NFCAL,
 		    "%s: invalid channel %u/0x%x; no mapping\n",
 		    __func__, chan->ic_freq, chan->ic_flags);
-		for (i = 0; i < AH_MIMO_MAX_CHAINS; i++) {
+		for (i = 0; i < AH_MAX_CHAINS; i++) {
 			nf_ctl[i] = nf_ext[i] = 0;
 		}
 		return 0;
@@ -964,7 +1092,7 @@
 
 	/* Return 0 if there's no valid MIMO values (yet) */
 	if (! (ichan->privFlags & CHANNEL_MIMO_NF_VALID)) {
-		for (i = 0; i < AH_MIMO_MAX_CHAINS; i++) {
+		for (i = 0; i < AH_MAX_CHAINS; i++) {
 			nf_ctl[i] = nf_ext[i] = 0;
 		}
 		return 0;
@@ -977,7 +1105,7 @@
 		 * stations which have a very low RSSI, below the
 		 * 'normalised' NF values in NOISE_FLOOR[].
 		 */
-		for (i = 0; i < AH_MIMO_MAX_CHAINS; i++) {
+		for (i = 0; i < AH_MAX_CHAINS; i++) {
 			nf_ctl[i] = nf_ext[i] = NOISE_FLOOR[mode] +
 			    ath_hal_getNfAdjust(ah, ichan);
 		}
@@ -996,7 +1124,7 @@
 		 * don't "wrap" when RSSI is less than the "adjusted" NF value.
 		 * ("Adjust" here is via ichan->noiseFloorAdjust.)
 		 */
-		for (i = 0; i < AH_MIMO_MAX_CHAINS; i++) {
+		for (i = 0; i < AH_MAX_CHAINS; i++) {
 			nf_ctl[i] = ichan->noiseFloorCtl[i] + ath_hal_getNfAdjust(ah, ichan);
 			nf_ext[i] = ichan->noiseFloorExt[i] + ath_hal_getNfAdjust(ah, ichan);
 		}
@@ -1258,3 +1386,45 @@
 		return 1;
 	return ((diag & 0x500000) == 0);
 }
+
+/*
+ * This routine is only needed when supporting EEPROM-in-RAM setups
+ * (eg embedded SoCs and on-board PCI/PCIe devices.)
+ */
+/* NB: This is in 16 bit words; not bytes */
+/* XXX This doesn't belong here!  */
+#define ATH_DATA_EEPROM_SIZE    2048
+
+HAL_BOOL
+ath_hal_EepromDataRead(struct ath_hal *ah, u_int off, uint16_t *data)
+{
+	if (ah->ah_eepromdata == AH_NULL) {
+		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__);
+		return AH_FALSE;
+	}
+	if (off > ATH_DATA_EEPROM_SIZE) {
+		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset %x > %x\n",
+		    __func__, off, ATH_DATA_EEPROM_SIZE);
+		return AH_FALSE;
+	}
+	(*data) = ah->ah_eepromdata[off];
+	return AH_TRUE;
+}
+
+/*
+ * Do a 2GHz specific MHz->IEEE based on the hardware
+ * frequency.
+ *
+ * This is the unmapped frequency which is programmed into the hardware.
+ */
+int
+ath_hal_mhz2ieee_2ghz(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan)
+{
+
+	if (ichan->channel == 2484)
+		return 14;
+	if (ichan->channel < 2484)
+		return ((int) ichan->channel - 2407) / 5;
+	else
+		return 15 + ((ichan->channel - 2512) / 20);
+}

Modified: trunk/sys/dev/ath/ath_hal/ah.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah.h 251655 2013-06-12 14:52:57Z adrian $
  */
 
 #ifndef _ATH_AH_H_
@@ -35,7 +36,7 @@
  * This is intended to be used by various statistics gathering operations
  * (NF, RSSI, EVM).
  */
-#define	AH_MIMO_MAX_CHAINS		3
+#define	AH_MAX_CHAINS			3
 #define	AH_MIMO_MAX_EVM_PILOTS		6
 
 /*
@@ -73,6 +74,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 +111,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 */
@@ -118,7 +120,9 @@
 	HAL_CAP_NUM_GPIO_PINS	= 36,	/* number of GPIO pins */
 
 	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,13 +134,57 @@
 	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_RX_STBC		= 58,
+	HAL_CAP_TX_STBC		= 59,
+
 	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_SPECTRAL_SCAN	= 90,	/* Hardware supports spectral scan */
+
+	HAL_CAP_BB_PANIC_WATCHDOG	= 92,
+
 	HAL_CAP_HT20_SGI	= 96,	/* hardware supports HT20 short GI */
 
+	HAL_CAP_LDPC		= 99,
+
 	HAL_CAP_RXTSTAMP_PREC	= 100,	/* rx desc tstamp precision (bits) */
+
+	HAL_CAP_ANT_DIV_COMB	= 105,	/* Enable antenna diversity/combining */
+	HAL_CAP_PHYRESTART_CLR_WAR	= 106,	/* in some cases, clear phy restart to fix bb hang */
+	HAL_CAP_ENTERPRISE_MODE	= 107,	/* Enterprise mode features */
+	HAL_CAP_LDPCWAR		= 108,
+	HAL_CAP_CHANNEL_SWITCH_TIME_USEC	= 109,	/* Channel change time, usec */
+	HAL_CAP_ENABLE_APM	= 110,	/* APM enabled */
+	HAL_CAP_PCIE_LCR_EXTSYNC_EN	= 111,
+	HAL_CAP_PCIE_LCR_OFFSET	= 112,
+
 	HAL_CAP_ENHANCED_DFS_SUPPORT	= 117,	/* hardware supports enhanced DFS */
+	HAL_CAP_MCI		= 118,
+	HAL_CAP_SMARTANTENNA	= 119,
+	HAL_CAP_TRAFFIC_FAST_RECOVER	= 120,
+	HAL_CAP_TX_DIVERSITY	= 121,
+	HAL_CAP_CRDC		= 122,
 
 	/* The following are private to the FreeBSD HAL (224 onward) */
 
@@ -143,12 +191,15 @@
 	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 */
 	HAL_CAP_RXDESC_SELFLINK	= 242,	/* support a self-linked tail RX descriptor */
 	HAL_CAP_LONG_RXDESC_TSF	= 243,	/* hardware supports 32bit TSF in RX descriptor */
+	HAL_CAP_BB_READ_WAR	= 244,	/* baseband read WAR */
+	HAL_CAP_SERIALISE_WAR	= 245,	/* serialise register access on PCI */
+	HAL_CAP_ENFORCE_TXOP	= 246,	/* Enforce TXOP if supported */
+	HAL_CAP_RX_LNA_MIXING	= 247,	/* RX hardware uses LNA mixing */
 } HAL_CAPABILITY_TYPE;
 
 /* 
@@ -178,11 +229,27 @@
 	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 */
 
 /*
+ * Receive queue types.  These are used to tag
+ * each transmit queue in the hardware and to identify a set
+ * of transmit queues for operations such as start/stop dma.
+ */
+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 */
+
+#define	HAL_TXFIFO_DEPTH	8		/* transmit fifo depth */
+
+/*
  * Transmit queue subtype.  These map directly to
  * WME Access Categories (except for UPSD).  Refer
  * to Table 5 of the WME spec.
@@ -361,6 +428,23 @@
 } HAL_POWER_MODE;
 
 /*
+ * Enterprise mode flags
+ */
+#define	AH_ENT_DUAL_BAND_DISABLE	0x00000001
+#define	AH_ENT_CHAIN2_DISABLE		0x00000002
+#define	AH_ENT_5MHZ_DISABLE		0x00000004
+#define	AH_ENT_10MHZ_DISABLE		0x00000008
+#define	AH_ENT_49GHZ_DISABLE		0x00000010
+#define	AH_ENT_LOOPBACK_DISABLE		0x00000020
+#define	AH_ENT_TPC_PERF_DISABLE		0x00000040
+#define	AH_ENT_MIN_PKT_SIZE_DISABLE	0x00000080
+#define	AH_ENT_SPECTRAL_PRECISION	0x00000300
+#define	AH_ENT_SPECTRAL_PRECISION_S	8
+#define	AH_ENT_RTSCTS_DELIM_WAR		0x00010000
+
+#define AH_FIRST_DESC_NDELIMS 60
+
+/*
  * NOTE WELL:
  * These are mapped to take advantage of the common locations for many of
  * the bits on all of the currently supported MAC chips. This is to make
@@ -371,7 +455,10 @@
  */
 typedef enum {
 	HAL_INT_RX	= 0x00000001,	/* Non-common mapping */
-	HAL_INT_RXDESC	= 0x00000002,
+	HAL_INT_RXDESC	= 0x00000002,	/* Legacy mapping */
+	HAL_INT_RXERR	= 0x00000004,
+	HAL_INT_RXHP	= 0x00000001,	/* EDMA */
+	HAL_INT_RXLP	= 0x00000002,	/* EDMA */
 	HAL_INT_RXNOFRM	= 0x00000008,
 	HAL_INT_RXEOL	= 0x00000010,
 	HAL_INT_RXORN	= 0x00000020,
@@ -378,11 +465,14 @@
 	HAL_INT_TX	= 0x00000040,	/* Non-common mapping */
 	HAL_INT_TXDESC	= 0x00000080,
 	HAL_INT_TIM_TIMER= 0x00000100,
+	HAL_INT_MCI	= 0x00000200,
+	HAL_INT_BBPANIC	= 0x00000400,
 	HAL_INT_TXURN	= 0x00000800,
 	HAL_INT_MIB	= 0x00001000,
 	HAL_INT_RXPHY	= 0x00004000,
 	HAL_INT_RXKCM	= 0x00008000,
 	HAL_INT_SWBA	= 0x00010000,
+	HAL_INT_BRSSI	= 0x00020000,
 	HAL_INT_BMISS	= 0x00040000,
 	HAL_INT_BNR	= 0x00100000,
 	HAL_INT_TIM	= 0x00200000,	/* Non-common mapping */
@@ -392,6 +482,8 @@
 	HAL_INT_CABEND	= 0x02000000,	/* Non-common mapping */
 	HAL_INT_TSFOOR	= 0x04000000,	/* Non-common mapping */
 	HAL_INT_TBTT	= 0x08000000,	/* Non-common mapping */
+	/* Atheros ref driver has a generic timer interrupt now..*/
+	HAL_INT_GENTIMER	= 0x08000000,	/* Non-common mapping */
 	HAL_INT_CST	= 0x10000000,	/* Non-common mapping */
 	HAL_INT_GTT	= 0x20000000,	/* Non-common mapping */
 	HAL_INT_FATAL	= 0x40000000,	/* Non-common mapping */
@@ -414,18 +506,85 @@
 			| HAL_INT_RXKCM
 			| HAL_INT_SWBA
 			| HAL_INT_BMISS
+			| HAL_INT_BRSSI
 			| HAL_INT_BNR
 			| 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;
+
+/* XXX this is duplicate information! */
+typedef struct {
+	u_int32_t	cyclecnt_diff;		/* delta cycle count */
+	u_int32_t	rxclr_cnt;		/* rx clear count */
+	u_int32_t	txframecnt_diff;	/* delta tx frame count */
+	u_int32_t	rxframecnt_diff;	/* delta rx frame count */
+	u_int32_t	listen_time;		/* listen time in msec - time for which ch is free */
+	u_int32_t	ofdmphyerr_cnt;		/* OFDM err count since last reset */
+	u_int32_t	cckphyerr_cnt;		/* CCK err count since last reset */
+	u_int32_t	ofdmphyerrcnt_diff;	/* delta OFDM Phy Error Count */
+	HAL_BOOL	valid;			/* if the stats are valid*/
+} HAL_ANISTATS;
+
+typedef struct {
+	u_int8_t	txctl_offset;
+	u_int8_t	txctl_numwords;
+	u_int8_t	txstatus_offset;
+	u_int8_t	txstatus_numwords;
+
+	u_int8_t	rxctl_offset;
+	u_int8_t	rxctl_numwords;
+	u_int8_t	rxstatus_offset;
+	u_int8_t	rxstatus_numwords;
+
+	u_int8_t	macRevision;
+} HAL_DESC_INFO;
+
+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_OUTPUT_MUX_AS_MCI_WLAN_DATA,
+	HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK,
+	HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA,
+	HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK,
+	HAL_GPIO_OUTPUT_MUX_AS_WL_IN_TX,
+	HAL_GPIO_OUTPUT_MUX_AS_WL_IN_RX,
+	HAL_GPIO_OUTPUT_MUX_AS_BT_IN_TX,
+	HAL_GPIO_OUTPUT_MUX_AS_BT_IN_RX,
+	HAL_GPIO_OUTPUT_MUX_AS_RUCKUS_STROBE,
+	HAL_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA,
+	HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL0,
+	HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL1,
+	HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL2,
+	HAL_GPIO_OUTPUT_MUX_NUM_ENTRIES
 } HAL_GPIO_MUX_TYPE;
 
 typedef enum {
@@ -434,6 +593,15 @@
 	HAL_GPIO_INTR_DISABLE		= 2
 } HAL_GPIO_INTR_TYPE;
 
+typedef struct halCounters {
+    u_int32_t   tx_frame_count;
+    u_int32_t   rx_frame_count;
+    u_int32_t   rx_clear_count;
+    u_int32_t   cycle_count;
+    u_int8_t    is_rx_active;     // true (1) or false (0)
+    u_int8_t    is_tx_active;     // true (1) or false (0)
+} HAL_COUNTERS;
+
 typedef enum {
 	HAL_RFGAIN_INACTIVE		= 0,
 	HAL_RFGAIN_READ_REQUESTED	= 1,
@@ -456,6 +624,17 @@
 	uint32_t	beacons;
 } HAL_MIB_STATS;
 
+/*
+ * These bits represent what's in ah_currentRDext.
+ */
+typedef enum {
+	REG_EXT_FCC_MIDBAND		= 0,
+	REG_EXT_JAPAN_MIDBAND		= 1,
+	REG_EXT_FCC_DFS_HT40		= 2,
+	REG_EXT_JAPAN_NONDFS_HT40	= 3,
+	REG_EXT_JAPAN_DFS_HT40		= 4
+} REG_EXT_BITMAP;
+
 enum {
 	HAL_MODE_11A	= 0x001,		/* 11a channels */
 	HAL_MODE_TURBO	= 0x002,		/* 11a turbo-only channels */
@@ -483,7 +662,7 @@
 
 typedef struct {
 	int		rateCount;		/* NB: for proper padding */
-	uint8_t		rateCodeToIndex[144];	/* back mapping */
+	uint8_t		rateCodeToIndex[256];	/* back mapping */
 	struct {
 		uint8_t		valid;		/* valid for rate control use */
 		uint8_t		phy;		/* CCK/OFDM/XR */
@@ -497,12 +676,12 @@
 						 * rate; used for dur. calcs */
 		uint16_t	lpAckDuration;	/* long preamble ACK duration */
 		uint16_t	spAckDuration;	/* short preamble ACK duration*/
-	} info[32];
+	} info[64];
 } HAL_RATE_TABLE;
 
 typedef struct {
 	u_int		rs_count;		/* number of valid entries */
-	uint8_t	rs_rates[32];		/* rates */
+	uint8_t	rs_rates[64];		/* rates */
 } HAL_RATE_SET;
 
 /*
@@ -515,7 +694,8 @@
 
 typedef struct {
 	u_int	Tries;
-	u_int	Rate;
+	u_int	Rate;		/* hardware rate code */
+	u_int	RateIndex;	/* rate series table index */
 	u_int	PktDuration;
 	u_int	ChSel;
 	u_int	RateFlags;
@@ -522,6 +702,8 @@
 #define	HAL_RATESERIES_RTS_CTS		0x0001	/* use rts/cts w/this series */
 #define	HAL_RATESERIES_2040		0x0002	/* use ext channel for series */
 #define	HAL_RATESERIES_HALFGI		0x0004	/* use half-gi for series */
+#define	HAL_RATESERIES_STBC		0x0008	/* use STBC for series */
+	u_int	tx_power_cap;		/* in 1/2 dBm units XXX TODO */
 } HAL_11N_RATE_SERIES;
 
 typedef enum {
@@ -545,6 +727,11 @@
 	HAL_RX_CLEAR_EXT_LOW	= 0x2,	/* force extension channel to appear busy */
 } HAL_HT_RXCLEAR;
 
+typedef enum {
+	HAL_FREQ_BAND_5GHZ	= 0,
+	HAL_FREQ_BAND_2GHZ	= 1,
+} HAL_FREQ_BAND;
+
 /*
  * Antenna switch control.  By default antenna selection
  * enables multiple (2) antenna use.  To force use of the
@@ -566,7 +753,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 */
@@ -573,7 +760,19 @@
 	uint8_t		kv_txmic[8];		/* TKIP TX MIC key (optional) */
 } HAL_KEYVAL;
 
+/*
+ * This is the TX descriptor field which marks the key padding requirement.
+ * The naming is unfortunately unclear.
+ */
+#define AH_KEYTYPE_MASK     0x0F
 typedef enum {
+    HAL_KEY_TYPE_CLEAR,
+    HAL_KEY_TYPE_WEP,
+    HAL_KEY_TYPE_AES,
+    HAL_KEY_TYPE_TKIP,
+} HAL_KEY_TYPE;
+
+typedef enum {
 	HAL_CIPHER_WEP		= 0,
 	HAL_CIPHER_AES_OCB	= 1,
 	HAL_CIPHER_AES_CCM	= 2,
@@ -601,9 +800,16 @@
 	uint32_t	bs_nexttbtt;		/* next beacon in TU */
 	uint32_t	bs_nextdtim;		/* next DTIM in TU */
 	uint32_t	bs_intval;		/* beacon interval+flags */
+/*
+ * HAL_BEACON_PERIOD, HAL_BEACON_ENA and HAL_BEACON_RESET_TSF
+ * are all 1:1 correspondances with the pre-11n chip AR_BEACON
+ * register.
+ */
 #define	HAL_BEACON_PERIOD	0x0000ffff	/* beacon interval period */
+#define	HAL_BEACON_PERIOD_TU8	0x0007ffff	/* beacon interval, tu/8 */
 #define	HAL_BEACON_ENA		0x00800000	/* beacon xmit enable */
 #define	HAL_BEACON_RESET_TSF	0x01000000	/* clear TSF */
+#define	HAL_TSFOOR_THRESHOLD	0x00004240	/* TSF OOR thresh (16k uS) */
 	uint32_t	bs_dtimperiod;
 	uint16_t	bs_cfpperiod;		/* CFP period in TU */
 	uint16_t	bs_cfpmaxduration;	/* max CFP duration in TU */
@@ -611,6 +817,7 @@
 	uint16_t	bs_timoffset;		/* byte offset to TIM bitmap */
 	uint16_t	bs_bmissthreshold;	/* beacon miss threshold */
 	uint32_t	bs_sleepduration;	/* max sleep duration */
+	uint32_t	bs_tsfoor_threshold;	/* TSF out of range threshold */
 } HAL_BEACON_STATE;
 
 /*
@@ -641,6 +848,7 @@
 
 #define	HAL_RSSI_EP_MULTIPLIER	(1<<7)	/* pow2 to optimize out * and / */
 
+
 struct ath_desc;
 struct ath_tx_status;
 struct ath_rx_status;
@@ -658,7 +866,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;
 
 /*
@@ -691,8 +903,11 @@
 	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;
 
+#define	HAL_ANI_ALL		0xffffffff
+
 /*
  * This is the layout of the ANI INTMIT capability.
  *
@@ -731,11 +946,28 @@
 					 */
 	int32_t		pe_extchannel;	/* Enable DFS on ext channel */
 	int32_t		pe_enabled;	/* Whether radar detection is enabled */
+	int32_t		pe_enrelpwr;
+	int32_t		pe_en_relstep_check;
 } HAL_PHYERR_PARAM;
 
 #define	HAL_PHYERR_PARAM_NOVAL	65535
-#define	HAL_PHYERR_PARAM_ENABLE	0x8000	/* Enable/Disable if applicable */
 
+typedef struct {
+	u_int16_t	ss_fft_period;	/* Skip interval for FFT reports */
+	u_int16_t	ss_period;	/* Spectral scan period */
+	u_int16_t	ss_count;	/* # of reports to return from ss_active */
+	u_int16_t	ss_short_report;/* Set to report ony 1 set of FFT results */
+	u_int8_t	radar_bin_thresh_sel;	/* strong signal radar FFT threshold configuration */
+	u_int16_t	ss_spectral_pri;		/* are we doing a noise power cal ? */
+	int8_t		ss_nf_cal[AH_MAX_CHAINS*2];     /* nf calibrated values for ctl+ext from eeprom */
+	int8_t		ss_nf_pwr[AH_MAX_CHAINS*2];     /* nf pwr values for ctl+ext from eeprom */
+	int32_t		ss_nf_temp_data;	/* temperature data taken during nf scan */
+	int		ss_enabled;
+	int		ss_active;
+} HAL_SPECTRAL_PARAM;
+#define	HAL_SPECTRAL_PARAM_NOVAL	0xFFFF
+#define	HAL_SPECTRAL_PARAM_ENABLE	0x8000	/* Enable/Disable if applicable */
+
 /*
  * DFS operating mode flags.
  */
@@ -746,7 +978,40 @@
 	HAL_DFS_MKK4_DOMAIN	= 3,	/* Japan dfs domain */
 } HAL_DFS_DOMAIN;
 
+
 /*
+ * MFP decryption options for initializing the MAC.
+ */
+typedef enum {
+	HAL_MFP_QOSDATA = 0,	/* Decrypt MFP frames like QoS data frames. All chips before Merlin. */
+	HAL_MFP_PASSTHRU,	/* Don't decrypt MFP frames at all. Passthrough */
+	HAL_MFP_HW_CRYPTO	/* hardware decryption enabled. Merlin can do it. */
+} HAL_MFP_OPT_T;
+
+/* LNA config supported */
+typedef enum {
+	HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2	= 0,
+	HAL_ANT_DIV_COMB_LNA2			= 1,
+	HAL_ANT_DIV_COMB_LNA1			= 2,
+	HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2		= 3,
+} HAL_ANT_DIV_COMB_LNA_CONF;
+
+typedef struct {
+	u_int8_t	main_lna_conf;
+	u_int8_t	alt_lna_conf;
+	u_int8_t	fast_div_bias;
+	u_int8_t	main_gaintb;
+	u_int8_t	alt_gaintb;
+	u_int8_t	antdiv_configgroup;
+	int8_t		lna1_lna2_delta;
+} HAL_ANT_COMB_CONFIG;
+
+#define	DEFAULT_ANTDIV_CONFIG_GROUP	0x00
+#define	HAL_ANTDIV_CONFIG_GROUP_1	0x01
+#define	HAL_ANTDIV_CONFIG_GROUP_2	0x02
+#define	HAL_ANTDIV_CONFIG_GROUP_3	0x03
+
+/*
  * Flag for setting QUIET period
  */
 typedef enum {
@@ -770,6 +1035,183 @@
 };
 typedef struct hal_dfs_event HAL_DFS_EVENT;
 
+/*
+ * Generic Timer domain
+ */
+typedef enum {
+	HAL_GEN_TIMER_TSF = 0,
+	HAL_GEN_TIMER_TSF2,
+	HAL_GEN_TIMER_TSF_ANY
+} HAL_GEN_TIMER_DOMAIN;
+
+typedef enum {
+	HAL_RESET_NONE = 0x0,
+	HAL_RESET_BBPANIC = 0x1,
+} HAL_RESET_TYPE;
+
+/*
+ * 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_MCI_MAX_TX_PWR,	/* Set max tx power for concurrent tx */
+	HAL_BT_COEX_MCI_FTP_STOMP_RX,	/* Use a different weight for stomp low */
+} 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;
+
+struct hal_bb_panic_info {
+	u_int32_t	status;
+	u_int32_t	tsf;
+	u_int32_t	phy_panic_wd_ctl1;
+	u_int32_t	phy_panic_wd_ctl2;
+	u_int32_t	phy_gen_ctrl;
+	u_int32_t	rxc_pcnt;
+	u_int32_t	rxf_pcnt;
+	u_int32_t	txf_pcnt;
+	u_int32_t	cycles;
+	u_int32_t	wd;
+	u_int32_t	det;
+	u_int32_t	rdar;
+	u_int32_t	r_odfm;
+	u_int32_t	r_cck;
+	u_int32_t	t_odfm;
+	u_int32_t	t_cck;
+	u_int32_t	agc;
+	u_int32_t	src;
+};
+
+/* Serialize Register Access Mode */
+typedef enum {
+	SER_REG_MODE_OFF	= 0,
+	SER_REG_MODE_ON		= 1,
+	SER_REG_MODE_AUTO	= 2,
+} SER_REG_MODE;
+
 typedef struct
 {
 	int ah_debug;			/* only used if AH_DEBUG is defined */
@@ -779,6 +1221,50 @@
 	int ah_dma_beacon_response_time;/* in TU's */
 	int ah_sw_beacon_response_time;	/* in TU's */
 	int ah_additional_swba_backoff;	/* in TU's */
+	int ah_force_full_reset;	/* force full chip reset rather then warm reset */
+	int ah_serialise_reg_war;	/* force serialisation of register IO */
+
+	/* XXX these don't belong here, they're just for the ar9300  HAL port effort */
+	int ath_hal_desc_tpc;		/* Per-packet TPC */
+	int ath_hal_sta_update_tx_pwr_enable;	/* GreenTX */
+	int ath_hal_sta_update_tx_pwr_enable_S1;	/* GreenTX */
+	int ath_hal_sta_update_tx_pwr_enable_S2;	/* GreenTX */
+	int ath_hal_sta_update_tx_pwr_enable_S3;	/* GreenTX */
+
+	/* I'm not sure what the default values for these should be */
+	int ath_hal_pll_pwr_save;
+	int ath_hal_pcie_power_save_enable;
+	int ath_hal_intr_mitigation_rx;
+	int ath_hal_intr_mitigation_tx;
+
+	int ath_hal_pcie_clock_req;
+#define	AR_PCIE_PLL_PWRSAVE_CONTROL	(1<<0)
+#define	AR_PCIE_PLL_PWRSAVE_ON_D3	(1<<1)
+#define	AR_PCIE_PLL_PWRSAVE_ON_D0	(1<<2)
+
+	int ath_hal_pcie_waen;
+	int ath_hal_pcie_ser_des_write;
+
+	/* these are important for correct AR9300 behaviour */
+	int ath_hal_ht_enable;		/* needs to be enabled for AR9300 HT */
+	int ath_hal_diversity_control;
+	int ath_hal_antenna_switch_swap;
+	int ath_hal_ext_lna_ctl_gpio;
+	int ath_hal_spur_mode;
+	int ath_hal_6mb_ack;		/* should set this to 1 for 11a/11na? */
+	int ath_hal_enable_msi;		/* enable MSI interrupts (needed?) */
+	int ath_hal_beacon_filter_interval;	/* ok to be 0 for now? */
+
+	/* For now, set this to 0 - net80211 needs to know about hardware MFP support */
+	int ath_hal_mfp_support;
+
+	int ath_hal_enable_ani;	/* should set this.. */
+	int ath_hal_cwm_ignore_ext_cca;
+	int ath_hal_show_bb_panic;
+	int ath_hal_ant_ctrl_comm2g_switch_enable;
+	int ath_hal_ext_atten_margin_cfg;
+	int ath_hal_war70c;
+	uint32_t ath_hal_mci_config;
 } HAL_OPS_CONFIG;
 
 /*
@@ -809,6 +1295,9 @@
 
 	uint16_t	*ah_eepromdata;	/* eeprom buffer, if needed */
 
+	uint32_t	ah_intrstate[8];	/* last int state */
+	uint32_t	ah_syncstate;		/* last sync intr state */
+
 	HAL_OPS_CONFIG ah_config;
 	const HAL_RATE_TABLE *__ahdecl(*ah_getRateTable)(struct ath_hal *,
 				u_int mode);
@@ -820,7 +1309,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*,
@@ -865,7 +1355,8 @@
 				u_int txRate2, u_int txTries2,
 				u_int txRate3, u_int txTries3);
 	HAL_BOOL  __ahdecl(*ah_fillTxDesc)(struct ath_hal *, struct ath_desc *,
-				u_int segLen, HAL_BOOL firstSeg,
+				HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList,
+				u_int descId, u_int qcuId, HAL_BOOL firstSeg,
 				HAL_BOOL lastSeg, const struct ath_desc *);
 	HAL_STATUS __ahdecl(*ah_procTxDesc)(struct ath_hal *,
 				struct ath_desc *, struct ath_tx_status *);
@@ -873,10 +1364,20 @@
 	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);
+	void	  __ahdecl(*ah_getTxRawTxDesc)(struct ath_hal *, u_int32_t *);
 
 	/* 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*);
@@ -902,9 +1403,6 @@
 				const struct ieee80211_channel *);
 	void	  __ahdecl(*ah_procMibEvent)(struct ath_hal *,
 				const HAL_NODE_STATS *);
-	void	  __ahdecl(*ah_rxAntCombDiversity)(struct ath_hal *,
-				struct ath_rx_status *,
-				unsigned long, int);
 
 	/* Misc Functions */
 	HAL_STATUS __ahdecl(*ah_getCapability)(struct ath_hal *,
@@ -934,6 +1432,7 @@
 	void	  __ahdecl(*ah_gpioSetIntr)(struct ath_hal*, u_int, uint32_t);
 	uint32_t __ahdecl(*ah_getTsf32)(struct ath_hal*);
 	uint64_t __ahdecl(*ah_getTsf64)(struct ath_hal*);
+	void     __ahdecl(*ah_setTsf64)(struct ath_hal *, uint64_t);
 	void	  __ahdecl(*ah_resetTsf)(struct ath_hal*);
 	HAL_BOOL  __ahdecl(*ah_detectCardPresent)(struct ath_hal*);
 	void	  __ahdecl(*ah_updateMibCounters)(struct ath_hal*,
@@ -959,6 +1458,8 @@
 	HAL_STATUS	__ahdecl(*ah_setQuiet)(struct ath_hal *ah, uint32_t period,
 				uint32_t duration, uint32_t nextStart,
 				HAL_QUIET_FLAG flag);
+	void	  __ahdecl(*ah_setChainMasks)(struct ath_hal *,
+				uint32_t, uint32_t);
 
 	/* DFS functions */
 	void	  __ahdecl(*ah_enableDfs)(struct ath_hal *ah,
@@ -965,11 +1466,24 @@
 				HAL_PHYERR_PARAM *pe);
 	void	  __ahdecl(*ah_getDfsThresh)(struct ath_hal *ah,
 				HAL_PHYERR_PARAM *pe);
+	HAL_BOOL  __ahdecl(*ah_getDfsDefaultThresh)(struct ath_hal *ah,
+				HAL_PHYERR_PARAM *pe);
 	HAL_BOOL  __ahdecl(*ah_procRadarEvent)(struct ath_hal *ah,
 				struct ath_rx_status *rxs, uint64_t fulltsf,
 				const char *buf, HAL_DFS_EVENT *event);
 	HAL_BOOL  __ahdecl(*ah_isFastClockEnabled)(struct ath_hal *ah);
 
+	/* Spectral Scan functions */
+	void	__ahdecl(*ah_spectralConfigure)(struct ath_hal *ah,
+				HAL_SPECTRAL_PARAM *sp);
+	void	__ahdecl(*ah_spectralGetConfig)(struct ath_hal *ah,
+				HAL_SPECTRAL_PARAM *sp);
+	void	__ahdecl(*ah_spectralStart)(struct ath_hal *);
+	void	__ahdecl(*ah_spectralStop)(struct ath_hal *);
+	HAL_BOOL	__ahdecl(*ah_spectralIsEnabled)(struct ath_hal *);
+	HAL_BOOL	__ahdecl(*ah_spectralIsActive)(struct ath_hal *);
+	/* XXX getNfPri() and getNfExt() */
+
 	/* Key Cache Functions */
 	uint32_t __ahdecl(*ah_getKeyCacheSize)(struct ath_hal*);
 	HAL_BOOL  __ahdecl(*ah_resetKeyCacheEntry)(struct ath_hal*, uint16_t);
@@ -1001,9 +1515,12 @@
 
 	/* 802.11n Functions */
 	HAL_BOOL  __ahdecl(*ah_chainTxDesc)(struct ath_hal *,
-				struct ath_desc *, u_int, u_int, HAL_PKT_TYPE,
-				u_int, HAL_CIPHER, uint8_t, u_int, HAL_BOOL,
-				HAL_BOOL);
+				struct ath_desc *,
+				HAL_DMA_ADDR *bufAddrList,
+				uint32_t *segLenList,
+				u_int, u_int, HAL_PKT_TYPE,
+				u_int, HAL_CIPHER, uint8_t, HAL_BOOL,
+				HAL_BOOL, HAL_BOOL);
 	HAL_BOOL  __ahdecl(*ah_setupFirstTxDesc)(struct ath_hal *,
 				struct ath_desc *, u_int, u_int, u_int,
 				u_int, u_int, u_int, u_int, u_int);
@@ -1012,12 +1529,32 @@
 	void	  __ahdecl(*ah_set11nRateScenario)(struct ath_hal *,
 	    			struct ath_desc *, u_int, u_int,
 				HAL_11N_RATE_SERIES [], u_int, u_int);
+
+	/*
+	 * The next 4 (set11ntxdesc -> set11naggrlast) are specific
+	 * to the EDMA HAL.  Descriptors are chained together by
+	 * using filltxdesc (not ChainTxDesc) and then setting the
+	 * aggregate flags appropriately using first/middle/last.
+	 */
+	void	  __ahdecl(*ah_set11nTxDesc)(struct ath_hal *,
+				void *, u_int, HAL_PKT_TYPE, u_int, u_int,
+				u_int);
+	void	  __ahdecl(*ah_set11nAggrFirst)(struct ath_hal *,
+				struct ath_desc *, u_int, u_int);
 	void	  __ahdecl(*ah_set11nAggrMiddle)(struct ath_hal *,
 	    			struct ath_desc *, u_int);
+	void	  __ahdecl(*ah_set11nAggrLast)(struct ath_hal *,
+				struct ath_desc *);
 	void	  __ahdecl(*ah_clr11nAggr)(struct ath_hal *,
 	    			struct ath_desc *);
 	void	  __ahdecl(*ah_set11nBurstDuration)(struct ath_hal *,
 	    			struct ath_desc *, u_int);
+	void	  __ahdecl(*ah_set11nVirtMoreFrag)(struct ath_hal *,
+				struct ath_desc *, u_int);
+
+	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 *,
 				HAL_HT_MACMODE);
@@ -1030,6 +1567,28 @@
 	HAL_BOOL  __ahdecl(*ah_getPendingInterrupts)(struct ath_hal*, HAL_INT*);
 	HAL_INT	  __ahdecl(*ah_getInterrupts)(struct ath_hal*);
 	HAL_INT	  __ahdecl(*ah_setInterrupts)(struct ath_hal*, HAL_INT);
+
+	/* Bluetooth Coexistence functions */
+	void	    __ahdecl(*ah_btCoexSetInfo)(struct ath_hal *,
+				HAL_BT_COEX_INFO *);
+	void	    __ahdecl(*ah_btCoexSetConfig)(struct ath_hal *,
+				HAL_BT_COEX_CONFIG *);
+	void	    __ahdecl(*ah_btCoexSetQcuThresh)(struct ath_hal *,
+				int);
+	void	    __ahdecl(*ah_btCoexSetWeights)(struct ath_hal *,
+				uint32_t);
+	void	    __ahdecl(*ah_btCoexSetBmissThresh)(struct ath_hal *,
+				uint32_t);
+	void	    __ahdecl(*ah_btCoexSetParameter)(struct ath_hal *,
+				uint32_t, uint32_t);
+	void	    __ahdecl(*ah_btCoexDisable)(struct ath_hal *);
+	int	    __ahdecl(*ah_btCoexEnable)(struct ath_hal *);
+
+	/* LNA diversity configuration */
+	void	    __ahdecl(*ah_divLnaConfGet)(struct ath_hal *,
+				HAL_ANT_COMB_CONFIG *);
+	void	    __ahdecl(*ah_divLnaConfSet)(struct ath_hal *,
+				HAL_ANT_COMB_CONFIG *);
 };
 
 /* 
@@ -1122,6 +1681,12 @@
 extern	u_int __ahdecl ath_hal_getwirelessmodes(struct ath_hal*);
 
 /*
+ * Get the HAL wireless mode for the given channel.
+ */
+extern	int ath_hal_get_curmode(struct ath_hal *ah,
+    const struct ieee80211_channel *chan);
+
+/*
  * Calculate the packet TX time for a legacy or 11n frame
  */
 extern uint32_t __ahdecl ath_hal_pkt_txtime(struct ath_hal *ah,
@@ -1156,4 +1721,20 @@
  */
 int __ahdecl ath_hal_getcca(struct ath_hal *ah);
 
+/*
+ * Read EEPROM data from ah_eepromdata
+ */
+HAL_BOOL __ahdecl ath_hal_EepromDataRead(struct ath_hal *ah,
+		u_int off, uint16_t *data);
+
+/*
+ * For now, simply pass through MFP frames.
+ */
+static inline u_int32_t
+ath_hal_get_mfp_qos(struct ath_hal *ah)
+{
+	//return AH_PRIVATE(ah)->ah_mfp_qos;
+	return HAL_MFP_QOSDATA;
+}
+
 #endif /* _ATH_AH_H_ */

Modified: trunk/sys/dev/ath/ath_hal/ah_debug.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_debug.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_debug.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_debug.h 249131 2013-04-05 07:41:47Z adrian $
  */
 #ifndef _ATH_AH_DEBUG_H_
 #define _ATH_AH_DEBUG_H_
@@ -47,8 +48,15 @@
 	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_FCS_RTT	= 0x10000000,
+	HAL_DEBUG_BT_COEX	= 0x20000000,
 
-	HAL_DEBUG_UNMASKABLE	= 0xf0000000,	/* always printed */
+	HAL_DEBUG_UNMASKABLE	= 0x80000000,	/* always printed */
 	HAL_DEBUG_ANY		= 0xffffffff
 };
 #endif /* _ATH_AH_DEBUG_H_ */

Modified: trunk/sys/dev/ath/ath_hal/ah_decode.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_decode.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_decode.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_decode.h 233887 2012-04-04 20:46:20Z adrian $
  */
 #ifndef _ATH_AH_DECODE_H_
 #define _ATH_AH_DECODE_H_
@@ -29,6 +30,7 @@
  * existing file.
  */
 struct athregrec {
+	uint32_t	threadid;
 	uint32_t	op	: 8,
 			reg	: 24;
 	uint32_t	val;

Modified: trunk/sys/dev/ath/ath_hal/ah_desc.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_desc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_desc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_desc.h 251399 2013-06-05 00:39:20Z adrian $
  */
 
 #ifndef _DEV_ATH_DESC_H
@@ -23,6 +24,19 @@
 #include "opt_ah.h"		/* NB: required for AH_SUPPORT_AR5416 */
 
 /*
+ * For now, define this for the structure definitions.
+ * Because of how the HAL / driver module currently builds,
+ * it's not very feasible to build the module without
+ * this defined.  The rest of the code (eg in the driver
+ * body) can work fine with these fields being uninitialised;
+ * they'll be initialised to 0 anyway.
+ */
+
+#ifndef	AH_SUPPORT_AR5416
+#define	AH_SUPPORT_AR5416	1
+#endif
+
+/*
  * Transmit descriptor status.  This structure is filled
  * in only after the tx descriptor process method finds a
  * ``done'' descriptor; at which point it returns something
@@ -44,15 +58,19 @@
 	uint8_t		ts_finaltsi;	/* final transmit series index */
 #ifdef AH_SUPPORT_AR5416
 					/* 802.11n status */
-	uint8_t    	ts_flags;   	/* misc flags */
-	int8_t      	ts_rssi_ctl[3];	/* tx ack RSSI [ctl, chain 0-2] */
-	int8_t      	ts_rssi_ext[3];	/* tx ack RSSI [ext, chain 0-2] */
+	uint8_t		ts_flags;	/* misc flags */
+	uint8_t		ts_queue_id;	/* AR9300: TX queue id */
+	uint8_t		ts_desc_id;	/* AR9300: TX descriptor id */
+	uint8_t		ts_tid;		/* TID */
 /* #define ts_rssi ts_rssi_combined */
-	uint32_t   	ts_ba_low;	/* blockack bitmap low */
-	uint32_t   	ts_ba_high;	/* blockack bitmap high */
-	uint32_t  	ts_evm0;	/* evm bytes */
-	uint32_t   	ts_evm1;
-	uint32_t   	ts_evm2;
+	uint32_t	ts_ba_low;	/* blockack bitmap low */
+	uint32_t	ts_ba_high;	/* blockack bitmap high */
+	uint32_t	ts_evm0;	/* evm bytes */
+	uint32_t	ts_evm1;
+	uint32_t	ts_evm2;
+	int8_t		ts_rssi_ctl[3];	/* tx ack RSSI [ctl, chain 0-2] */
+	int8_t		ts_rssi_ext[3];	/* tx ack RSSI [ext, chain 0-2] */
+	uint8_t		ts_pad[2];
 #endif /* AH_SUPPORT_AR5416 */
 };
 
@@ -108,11 +126,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 */
@@ -124,17 +143,47 @@
 #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 */
+#define	HAL_RX_STBC		0x0200	/* Is an STBC frame */
 
+/*
+ * This is the format of RSSI[2] on the AR9285/AR9485.
+ * It encodes the LNA configuration information.
+ *
+ * For boards with an external diversity antenna switch,
+ * HAL_RX_LNA_EXTCFG encodes which configuration was
+ * used (antenna 1 or antenna 2.)  This feeds into the
+ * switch table and ensures that the given antenna was
+ * connected to an LNA.
+ */
+#define	HAL_RX_LNA_LNACFG	0x80	/* 1 = main LNA config used, 0 = ALT */
+#define	HAL_RX_LNA_EXTCFG	0x40	/* 0 = external diversity ant1, 1 = ant2 */
+#define	HAL_RX_LNA_CFG_USED	0x30	/* 2 bits; LNA config used on RX */
+#define	HAL_RX_LNA_CFG_USED_S		4
+#define	HAL_RX_LNA_CFG_MAIN	0x0c	/* 2 bits; "Main" LNA config */
+#define	HAL_RX_LNA_CFG_ALT	0x02	/* 2 bits; "Alt" LNA config */
+
+/*
+ * This is the format of RSSI_EXT[2] on the AR9285/AR9485.
+ * It encodes the switch table configuration and fast diversity
+ * value.
+ */
+#define	HAL_RX_LNA_FASTDIV	0x40	/* 1 = fast diversity measurement done */
+#define	HAL_RX_LNA_SWITCH_0	0x30	/* 2 bits; sw_0[1:0] */
+#define	HAL_RX_LNA_SWITCH_COM	0x0f	/* 4 bits, sw_com[3:0] */
+
 enum {
 	HAL_PHYERR_UNDERRUN		= 0,	/* Transmit underrun */
 	HAL_PHYERR_TIMING		= 1,	/* Timing error */
@@ -164,6 +213,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 */
@@ -200,6 +251,12 @@
 	uint32_t	ds_hw[HAL_DESC_HW_SIZE];	/* opaque h/w region */
 };
 
+struct ath_desc_txedma {
+	uint32_t	ds_info;
+	uint32_t	ds_link;
+	uint32_t	ds_hw[21];	/* includes buf/len */
+};
+
 struct ath_desc_status {
 	union {
 		struct ath_tx_status tx;/* xmit status */
@@ -211,6 +268,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 */
@@ -222,6 +280,8 @@
 #define	HAL_TXDESC_EXT_ONLY	0x0080	/* send on ext channel only (11n) */
 #define	HAL_TXDESC_EXT_AND_CTL	0x0100	/* send on ext + ctl channels (11n) */
 #define	HAL_TXDESC_VMF		0x0200	/* virtual more frag */
+#define	HAL_TXDESC_LOWRXCHAIN	0x0400	/* switch to low RX chain */
+#define	HAL_TXDESC_LDPC		0x1000
 
 /* flags passed to rx descriptor setup methods */
 #define	HAL_RXDESC_INTREQ	0x0020	/* enable per-descriptor interrupt */

Modified: trunk/sys/dev/ath/ath_hal/ah_devid.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_devid.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_devid.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_devid.h 250166 2013-05-02 00:59:39Z adrian $
  */
 
 #ifndef _DEV_ATH_DEVID_H_
@@ -73,16 +74,27 @@
 
 /* AR5416 compatible devid's  */
 #define AR5416_DEVID_PCI	0x0023          /* AR5416 PCI (MB/CB) Owl */
-#define AR5416_DEVID_PCIE	0x0024          /* AR5416 PCI-E (XB) Owl */
+#define AR5416_DEVID_PCIE	0x0024          /* AR5418 PCI-E (XB) Owl */
 #define	AR5416_AR9130_DEVID     0x000b          /* AR9130 SoC WiMAC */
 #define AR9160_DEVID_PCI	0x0027          /* AR9160 PCI Sowl */
 #define AR9280_DEVID_PCI	0x0029          /* AR9280 PCI Merlin */
-#define AR9280_DEVID_PCIE	0x002a          /* AR9280 PCI-E Merlin */
+#define AR9280_DEVID_PCIE	0x002a          /* AR9220 PCI-E Merlin */
 #define AR9285_DEVID_PCIE	0x002b          /* AR9285 PCI-E Kite */
 #define	AR2427_DEVID_PCIE	0x002c		/* AR2427 PCI-E w/ 802.11n bonded out */
 #define	AR9287_DEVID_PCI	0x002d		/* AR9227 PCI Kiwi */
 #define	AR9287_DEVID_PCIE	0x002e		/* AR9287 PCI-E Kiwi */
 
+/* AR9300 */
+#define	AR9300_DEVID_AR9380_PCIE	0x0030
+#define	AR9300_DEVID_EMU_PCIE		0xabcd
+#define	AR9300_DEVID_AR9340		0x0031
+#define	AR9300_DEVID_AR9485_PCIE	0x0032
+#define	AR9300_DEVID_AR9580_PCIE	0x0033
+#define	AR9300_DEVID_AR946X_PCIE	0x0034
+#define	AR9300_DEVID_AR9330		0x0035
+#define	AR9300_DEVID_QCA9565		0x0036
+#define	AR9300_DEVID_QCA955X		0x0039
+
 #define	AR_SUBVENDOR_ID_NOG	0x0e11		/* No 11G subvendor ID */
 #define AR_SUBVENDOR_ID_NEW_A	0x7065		/* Update device to new RD */
 #endif /* _DEV_ATH_DEVID_H */

Modified: trunk/sys/dev/ath/ath_hal/ah_diagcodes.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_diagcodes.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_diagcodes.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_diagcodes.h 239629 2012-08-24 00:17:39Z adrian $
  */
 #ifndef _ATH_AH_DIAGCODES_H_
 #define _ATH_AH_DIAGCODES_H_
@@ -63,6 +64,10 @@
 	HAL_DIAG_CHECK_HANGS	= 32,	/* check h/w hangs */
 	HAL_DIAG_SETREGS	= 33,	/* write registers */
 	HAL_DIAG_CHANSURVEY	= 34,	/* channel survey */
+	HAL_DIAG_PRINT_REG	= 35,
+	HAL_DIAG_PRINT_REG_ALL	= 36,
+	HAL_DIAG_CHANNELS	= 37,
+	HAL_DIAG_PRINT_REG_COUNTER	= 38,
 };
 
 #endif /* _ATH_AH_DIAGCODES_H_ */

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom.h 239637 2012-08-24 01:14:00Z adrian $
  */
 #ifndef _ATH_AH_EEPROM_H_
 #define _ATH_AH_EEPROM_H_
@@ -105,6 +106,10 @@
 	AR_EEP_TEMPSENSE_SLOPE,	/* int8_t* */
 	AR_EEP_TEMPSENSE_SLOPE_PAL_ON,	/* int8_t* */
 	AR_EEP_FRAC_N_5G,	/* uint8_t* */
+
+	/* New fields for AR9300 and later */
+	AR_EEP_DRIVE_STRENGTH,
+	AR_EEP_PAPRD_ENABLED,
 };
 
 typedef struct {
@@ -127,6 +132,30 @@
 #define	CTL_2GHT40		7
 #define	CTL_5GHT40		8
 
+/* XXX must match what FCC/MKK/ETSI are defined as in ah_regdomain.h */
+#define	HAL_REG_DMN_MASK	0xf0
+#define	HAL_REGDMN_FCC		0x10
+#define	HAL_REGDMN_MKK		0x40
+#define	HAL_REGDMN_ETSI		0x30
+
+#define	is_reg_dmn_fcc(reg_dmn)	\
+	   (((reg_dmn & HAL_REG_DMN_MASK) == HAL_REGDMN_FCC) ? 1 : 0)
+#define	is_reg_dmn_etsi(reg_dmn)	\
+	    (((reg_dmn & HAL_REG_DMN_MASK) == HAL_REGDMN_ETSI) ? 1 : 0)
+#define	is_reg_dmn_mkk(reg_dmn)	\
+	    (((reg_dmn & HAL_REG_DMN_MASK) == HAL_REGDMN_MKK) ? 1 : 0)
+
+#define	AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND	0x0040
+#define	AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN	0x0080
+#define	AR_EEPROM_EEREGCAP_EN_KK_U2		0x0100
+#define	AR_EEPROM_EEREGCAP_EN_KK_MIDBAND	0x0200
+#define	AR_EEPROM_EEREGCAP_EN_KK_U1_ODD		0x0400
+#define	AR_EEPROM_EEREGCAP_EN_KK_NEW_11A	0x0800
+
+/* regulatory capabilities prior to eeprom version 4.0 */
+#define	AR_EEPROM_EEREGCAP_EN_KK_U1_ODD_PRE4_0  0x4000
+#define	AR_EEPROM_EEREGCAP_EN_KK_NEW_11A_PRE4_0 0x8000
+
 #define	AR_NO_SPUR		0x8000
 
 /* XXX exposed to chip code */

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_9287.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_9287.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_9287.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2010 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_9287.c 239704 2012-08-26 04:26:49Z adrian $
  */
 #include "opt_ah.h"
 
@@ -292,7 +293,7 @@
 {
 #define	NW(a)	(sizeof(a) / sizeof(uint16_t))
 	HAL_EEPROM_9287 *ee = AH_PRIVATE(ah)->ah_eeprom;
-	uint16_t *eep_data, magic = 0;
+	uint16_t *eep_data, magic;
 	HAL_BOOL need_swap;
 	u_int w, off, len;
 	uint32_t sum;
@@ -310,13 +311,13 @@
 			    "%s Error reading Eeprom MAGIC\n", __func__);
 			return HAL_EEREAD;
 		}
+		HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s Eeprom Magic = 0x%x\n",
+		    __func__, magic);
+		if (magic != AR5416_EEPROM_MAGIC) {
+			HALDEBUG(ah, HAL_DEBUG_ANY, "Bad magic number\n");
+			return HAL_EEMAGIC;
+		}
 	}
-	HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s Eeprom Magic = 0x%x\n",
-	    __func__, magic);
-	if (magic != AR5416_EEPROM_MAGIC) {
-		HALDEBUG(ah, HAL_DEBUG_ANY, "Bad magic number\n");
-		return HAL_EEMAGIC;
-	}
 
 	ee = ath_hal_malloc(sizeof(HAL_EEPROM_9287));
 	if (ee == AH_NULL) {

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_9287.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_9287.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_9287.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Atheros Communications Inc.
  *
@@ -13,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.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_9287.h 223615 2011-06-28 00:01:55Z adrian $
  */
 
 #ifndef	__AH_EEPROM_9287_H__

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_v1.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_v1.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_v1.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_v1.c 221896 2011-05-14 15:12:02Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_v1.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_v1.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_v1.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_v1.h 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #ifndef _ATH_AH_EEPROM_V1_H_
 #define _ATH_AH_EEPROM_V1_H_

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_v14.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_v14.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_v14.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_v14.c 224519 2011-07-30 13:45:12Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_v14.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_v14.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_v14.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_v14.h 224519 2011-07-30 13:45:12Z adrian $
  */
 #ifndef _AH_EEPROM_V14_H_
 #define _AH_EEPROM_V14_H_

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_v3.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_v3.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_v3.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_v3.c 221896 2011-05-14 15:12:02Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_v3.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_v3.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_v3.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_v3.h 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #ifndef _ATH_AH_EEPROM_V3_H_
 #define _ATH_AH_EEPROM_V3_H_

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_v4k.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_v4k.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_v4k.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2009 Rui Paulo <rpaulo at FreeBSD.org>
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_v4k.c 234508 2012-04-20 21:56:13Z adrian $
  */
 #include "opt_ah.h"
 
@@ -281,7 +282,7 @@
 {
 #define	NW(a)	(sizeof(a) / sizeof(uint16_t))
 	HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
-	uint16_t *eep_data, magic = 0;
+	uint16_t *eep_data, magic;
 	HAL_BOOL need_swap;
 	u_int w, off, len;
 	uint32_t sum;

Modified: trunk/sys/dev/ath/ath_hal/ah_eeprom_v4k.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_eeprom_v4k.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_eeprom_v4k.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2009 Rui Paulo <rpaulo at FreeBSD.org>
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_eeprom_v4k.h 220946 2011-04-22 10:57:46Z adrian $
  */
 #ifndef _AH_EEPROM_V4K_H_
 #define _AH_EEPROM_V4K_H_

Modified: trunk/sys/dev/ath/ath_hal/ah_internal.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_internal.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_internal.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_internal.h 252236 2013-06-26 04:46:03Z adrian $
  */
 #ifndef _ATH_AH_INTERAL_H_
 #define _ATH_AH_INTERAL_H_
@@ -30,6 +31,10 @@
 #include <net80211/_ieee80211.h>
 #include "opt_ah.h"			/* needed for AH_SUPPORT_AR5416 */
 
+#ifndef	AH_SUPPORT_AR5416
+#define	AH_SUPPORT_AR5416	1
+#endif
+
 #ifndef NBBY
 #define	NBBY	8			/* number of bits/byte */
 #endif
@@ -75,6 +80,11 @@
 } HAL_PHYDIAG_CAPS;
 
 /*
+ * Enable/disable strong signal fast diversity
+ */
+#define	HAL_CAP_STRONG_DIV		2
+
+/*
  * Each chip or class of chips registers to offer support.
  */
 struct ath_hal_chip {
@@ -127,7 +137,42 @@
 #define	AH_MAXCHAN	96
 #endif
 
+#define	HAL_NF_CAL_HIST_LEN_FULL	5
+#define	HAL_NF_CAL_HIST_LEN_SMALL	1
+#define	HAL_NUM_NF_READINGS		6	/* 3 chains * (ctl + ext) */
+#define	HAL_NF_LOAD_DELAY		1000
+
 /*
+ * PER_CHAN doesn't work for now, as it looks like the device layer
+ * has to pre-populate the per-channel list with nominal values.
+ */
+//#define	ATH_NF_PER_CHAN		1
+
+typedef struct {
+    u_int8_t    curr_index;
+    int8_t      invalidNFcount; /* TO DO: REMOVE THIS! */
+    int16_t     priv_nf[HAL_NUM_NF_READINGS];
+} HAL_NFCAL_BASE;
+
+typedef struct {
+    HAL_NFCAL_BASE base;
+    int16_t     nf_cal_buffer[HAL_NF_CAL_HIST_LEN_FULL][HAL_NUM_NF_READINGS];
+} HAL_NFCAL_HIST_FULL;
+
+typedef struct {
+    HAL_NFCAL_BASE base;
+    int16_t     nf_cal_buffer[HAL_NF_CAL_HIST_LEN_SMALL][HAL_NUM_NF_READINGS];
+} HAL_NFCAL_HIST_SMALL;
+
+#ifdef	ATH_NF_PER_CHAN
+typedef HAL_NFCAL_HIST_FULL HAL_CHAN_NFCAL_HIST;
+#define	AH_HOME_CHAN_NFCAL_HIST(ah, ichan) (ichan ? &ichan->nf_cal_hist: NULL)
+#else
+typedef HAL_NFCAL_HIST_SMALL HAL_CHAN_NFCAL_HIST;
+#define	AH_HOME_CHAN_NFCAL_HIST(ah, ichan) (&AH_PRIVATE(ah)->nf_cal_hist)
+#endif	/* ATH_NF_PER_CHAN */
+
+/*
  * Internal per-channel state.  These are found
  * using ic_devdata in the ieee80211_channel.
  */
@@ -144,10 +189,16 @@
 	int16_t		rawNoiseFloor;
 	int16_t		noiseFloorAdjust;
 #ifdef	AH_SUPPORT_AR5416
-	int16_t		noiseFloorCtl[AH_MIMO_MAX_CHAINS];
-	int16_t		noiseFloorExt[AH_MIMO_MAX_CHAINS];
+	int16_t		noiseFloorCtl[AH_MAX_CHAINS];
+	int16_t		noiseFloorExt[AH_MAX_CHAINS];
 #endif	/* AH_SUPPORT_AR5416 */
 	uint16_t	mainSpur;	/* cached spur value for this channel */
+
+	/*XXX TODO: make these part of privFlags */
+	uint8_t  paprd_done:1,           /* 1: PAPRD DONE, 0: PAPRD Cal not done */
+	       paprd_table_write_done:1; /* 1: DONE, 0: Cal data write not done */
+	int		one_time_cals_done;
+	HAL_CHAN_NFCAL_HIST nf_cal_hist;
 } HAL_CHANNEL_INTERNAL;
 
 /* channel requires noise floor check */
@@ -208,8 +259,30 @@
 			halBssidMatchSupport		: 1,
 			hal4kbSplitTransSupport		: 1,
 			halHasRxSelfLinkedTail		: 1,
-			halSupportsFastClock5GHz	: 1,	/* Hardware supports 5ghz fast clock; check eeprom/channel before using */
-			halHasLongRxDescTsf		: 1;
+			halSupportsFastClock5GHz	: 1,
+			halHasLongRxDescTsf		: 1,
+			halHasBBReadWar			: 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,
+			hal49GhzSupport			: 1,
+			halAntDivCombSupport		: 1,
+			halAntDivCombSupportOrg		: 1,
+			halRadioRetentionSupport	: 1,
+			halSpectralScanSupport		: 1,
+			halRxUsingLnaMixing		: 1;
+
 	uint32_t	halWirelessModes;
 	uint16_t	halTotalQueues;
 	uint16_t	halKeyCacheSize;
@@ -225,11 +298,33 @@
 	uint32_t	halIntrMask;
 	uint8_t		halTxStreams;
 	uint8_t		halRxStreams;
+	HAL_MFP_OPT_T	halMfpSupport;
+
+	/* AR9300 HAL porting capabilities */
+	int		hal_paprd_enabled;
+	int		hal_pcie_lcr_offset;
+	int		hal_pcie_lcr_extsync_en;
+	int		halNumTxMaps;
+	int		halTxDescLen;
+	int		halTxStatusLen;
+	int		halRxStatusLen;
+	int		halRxHpFifoDepth;
+	int		halRxLpFifoDepth;
+	uint32_t	halRegCap;		/* XXX needed? */
+	int		halNumMRRetries;
+	int		hal_ani_poll_interval;
+	int		hal_channel_switch_time_usec;
 } HAL_CAPABILITIES;
 
 struct regDomain;
 
 /*
+ * Definitions for ah_flags in ath_hal_private
+ */
+#define		AH_USE_EEPROM	0x1
+#define		AH_IS_HB63	0x2
+
+/*
  * The ``private area'' follows immediately after the ``public area''
  * in the data structure returned by ath_hal_attach.  Private data are
  * used by device-independent code such as the regulatory domain support.
@@ -288,7 +383,9 @@
 	uint16_t	ah_phyRev;		/* PHY revision */
 	uint16_t	ah_analog5GhzRev;	/* 2GHz radio revision */
 	uint16_t	ah_analog2GhzRev;	/* 5GHz radio revision */
+	uint32_t	ah_flags;		/* misc flags */
 	uint8_t		ah_ispcie;		/* PCIE, special treatment */
+	uint8_t		ah_devType;		/* card type - CB, PCI, PCIe */
 
 	HAL_OPMODE	ah_opmode;		/* operating mode from reset */
 	const struct ieee80211_channel *ah_curchan;/* operating channel */
@@ -297,6 +394,7 @@
 	int16_t		ah_powerLimit;		/* tx power cap */
 	uint16_t	ah_maxPowerLevel;	/* calculated max tx power */
 	u_int		ah_tpScale;		/* tx power scale factor */
+	u_int16_t	ah_extraTxPow;		/* low rates extra-txpower */
 	uint32_t	ah_11nCompat;		/* 11n compat controls */
 
 	/*
@@ -321,6 +419,10 @@
 	 */
 	uint32_t	ah_fatalState[6];	/* AR_ISR+shadow regs */
 	int		ah_rxornIsFatal;	/* how to treat HAL_INT_RXORN */
+
+#ifndef	ATH_NF_PER_CHAN
+	HAL_NFCAL_HIST_FULL	nf_cal_hist;
+#endif	/* ! ATH_NF_PER_CHAN */
 };
 
 #define	AH_PRIVATE(_ah)	((struct ath_hal_private *)(_ah))
@@ -349,13 +451,20 @@
 	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) \
 	(_ah)->ah_setInterrupts(_ah, _mask)
 
+#define ath_hal_isrfkillenabled(_ah)  \
+    (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 1, AH_NULL) == HAL_OK)
+#define ath_hal_enable_rfkill(_ah, _v) \
+    ath_hal_setcapability(_ah, HAL_CAP_RFSILENT, 1, _v, AH_NULL)
+#define ath_hal_hasrfkill_int(_ah)  \
+    (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 3, AH_NULL) == HAL_OK)
+
 #define	ath_hal_eepromDetach(_ah) do {				\
 	if (AH_PRIVATE(_ah)->ah_eepromDetach != AH_NULL)	\
 		AH_PRIVATE(_ah)->ah_eepromDetach(_ah);		\
@@ -472,10 +581,23 @@
 	OS_REG_WRITE(_a, _r, OS_REG_READ(_a, _r) | (_f))
 #define	OS_REG_CLR_BIT(_a, _r, _f) \
 	OS_REG_WRITE(_a, _r, OS_REG_READ(_a, _r) &~ (_f))
+#define OS_REG_IS_BIT_SET(_a, _r, _f) \
+	    ((OS_REG_READ(_a, _r) & (_f)) != 0)
+#define	OS_REG_RMW_FIELD_ALT(_a, _r, _f, _v) \
+	    OS_REG_WRITE(_a, _r, \
+	    (OS_REG_READ(_a, _r) &~(_f<<_f##_S)) | \
+	    (((_v) << _f##_S) & (_f<<_f##_S)))
+#define	OS_REG_READ_FIELD(_a, _r, _f) \
+	    (((OS_REG_READ(_a, _r) & _f) >> _f##_S))
+#define	OS_REG_READ_FIELD_ALT(_a, _r, _f) \
+	    ((OS_REG_READ(_a, _r) >> (_f##_S))&(_f))
 
 /* 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)
+	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,
@@ -503,26 +625,15 @@
 extern	int ath_hal_debug;	/* Global debug flags */
 
 /*
- * This is used for global debugging, when ahp doesn't yet have the
- * related debugging state. For example, during probe/attach.
+ * The typecast is purely because some callers will pass in
+ * AH_NULL directly rather than using a NULL ath_hal pointer.
  */
-#define	HALDEBUG_G(_ah, __m, ...) \
-	do {							\
-		if ((__m) == HAL_DEBUG_UNMASKABLE ||		\
-		    ath_hal_debug & (__m)) {			\
-			DO_HALDEBUG((_ah), (__m), __VA_ARGS__);	\
-		}						\
-	} while (0);
-
-/*
- * This is used for local debugging, when ahp isn't NULL and
- * thus may have debug flags set.
- */
 #define	HALDEBUG(_ah, __m, ...) \
 	do {							\
 		if ((__m) == HAL_DEBUG_UNMASKABLE ||		\
 		    ath_hal_debug & (__m) ||			\
-		    (_ah)->ah_config.ah_debug & (__m)) {	\
+		    ((_ah) != NULL &&				\
+		      ((struct ath_hal *) (_ah))->ah_config.ah_debug & (__m))) {	\
 			DO_HALDEBUG((_ah), (__m), __VA_ARGS__);	\
 		}						\
 	} while(0);
@@ -531,7 +642,6 @@
 	__printflike(3,4);
 #else
 #define HALDEBUG(_ah, __m, ...)
-#define HALDEBUG_G(_ah, __m, ...)
 #endif /* AH_DEBUG */
 
 /*
@@ -630,6 +740,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,
@@ -649,6 +799,17 @@
 		 | HAL_MAC_HANG_UNKNOWN,
 };
 
+/* Merge these with above */
+typedef enum hal_hw_hangs {
+    HAL_DFS_BB_HANG_WAR          = 0x1,
+    HAL_RIFS_BB_HANG_WAR         = 0x2,
+    HAL_RX_STUCK_LOW_BB_HANG_WAR = 0x4,
+    HAL_MAC_HANG_WAR             = 0x8,
+    HAL_PHYRESTART_CLR_WAR       = 0x10,
+    HAL_MAC_HANG_DETECTED        = 0x40000000,
+    HAL_BB_HANG_DETECTED         = 0x80000000
+} hal_hw_hangs_t;
+
 /*
  * Device revision information.
  */
@@ -830,5 +991,40 @@
 	 AH_PRIVATE((_ah))->ah_caps.halSupportsFastClock5GHz && \
 	ath_hal_eepromGetFlag((_ah), AR_EEP_FSTCLK_5G))
 
+/*
+ * Fetch the maximum regulatory domain power for the given channel
+ * in 1/2dBm steps.
+ */
+static inline int
+ath_hal_get_twice_max_regpower(struct ath_hal_private *ahp,
+    const HAL_CHANNEL_INTERNAL *ichan, const struct ieee80211_channel *chan)
+{
+	struct ath_hal *ah = &ahp->h;
 
+	if (! chan) {
+		ath_hal_printf(ah, "%s: called with chan=NULL!\n", __func__);
+		return (0);
+	}
+	return (chan->ic_maxpower);
+}
+
+/*
+ * Get the maximum antenna gain allowed, in 1/2dBm steps.
+ */
+static inline int
+ath_hal_getantennaallowed(struct ath_hal *ah,
+    const struct ieee80211_channel *chan)
+{
+
+	if (! chan)
+		return (0);
+
+	return (chan->ic_maxantgain);
+}
+
+/*
+ * Map the given 2GHz channel to an IEEE number.
+ */
+extern	int ath_hal_mhz2ieee_2ghz(struct ath_hal *, HAL_CHANNEL_INTERNAL *);
+
 #endif /* _ATH_AH_INTERAL_H_ */

Modified: trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2005-2011 Atheros Communications, Inc.
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_ctry.h 224226 2011-07-20 12:46:58Z adrian $
  */
 
 #ifndef	__AH_REGDOMAIN_CTRY_H__

Modified: trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2005-2006 Atheros Communications, Inc.
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_domains.h 248677 2013-03-24 04:42:56Z adrian $
  */
 
 #ifndef	__AH_REGDOMAIN_DOMAINS_H__
@@ -741,6 +742,31 @@
 				      WG1_2467_2467),
 	 .chan11g_turbo		= BM1(T3_2437_2437)},
 
+	{.regDmnEnum		= WORC_WORLD,
+	 .conformanceTestLimit	= NO_CTL,
+	 .dfsMask		= DFS_FCC3 | DFS_ETSI,
+	 .pscan			= PSCAN_WWR,
+	 .flags			= ADHOC_PER_11D,
+	 .chan11a		= BM4(W1_5260_5320,
+				      W1_5180_5240,
+				      W1_5745_5825,
+				      W1_5500_5700),
+	 .chan11b		= BM7(W1_2412_2412,
+				      W1_2437_2442,
+				      W1_2462_2462,
+				      W1_2472_2472,
+				      W1_2417_2432,
+				      W1_2447_2457,
+				      W1_2467_2467),
+	 .chan11g		= BM7(WG1_2412_2412,
+				      WG1_2437_2442,
+				      WG1_2462_2462,
+				      WG1_2472_2472,
+				      WG1_2417_2432,
+				      WG1_2447_2457,
+				      WG1_2467_2467),
+	 .chan11g_turbo		= BM1(T3_2437_2437)},
+
 	{.regDmnEnum		= NULL1,
 	 .conformanceTestLimit	= NO_CTL,
 	}

Modified: trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2005-2006 Atheros Communications, Inc.
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_freqbands.h 219442 2011-03-10 03:13:56Z adrian $
  */
 
 #ifndef	__AH_REGDOMAIN_FREQBANDS_H__

Modified: trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2005-2011 Atheros Communications, Inc.
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h 224226 2011-07-20 12:46:58Z adrian $
  */
 
 #ifndef	__AH_REGDOMAIN_REGENUM_H__

Modified: trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2005-2006 Atheros Communications, Inc.
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regmap.h 248677 2013-03-24 04:42:56Z adrian $
  */
 
 #ifndef	__AH_REGDOMAIN_REGMAP_H__
@@ -129,6 +130,7 @@
 	{WOR9_WORLD,	WOR9_WORLD,	WOR9_WORLD,	DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, CTRY_DEFAULT },
 	{WORA_WORLD,	WORA_WORLD,	WORA_WORLD,	DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, CTRY_DEFAULT },
 	{WORB_WORLD,	WORB_WORLD,	WORB_WORLD,	DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, CTRY_DEFAULT },
+	{WORC_WORLD,	WORC_WORLD,	WORC_WORLD,	NO_REQ, NO_REQ, PSCAN_DEFER, CTRY_DEFAULT },
 };
 
 #endif

Modified: trunk/sys/dev/ath/ath_hal/ah_regdomain.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_regdomain.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_regdomain.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2005-2006 Atheros Communications, Inc.
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_regdomain.c 243975 2012-12-07 06:38:30Z adrian $
  */
 #include "opt_ah.h"
 
@@ -169,7 +170,7 @@
 			if (regDomainPairs[i].regDmnEnum == rd)
 				return AH_TRUE;
 	}
-	HALDEBUG_G(ah, HAL_DEBUG_REGDOMAIN,
+	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
 	    "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd);
 	return AH_FALSE;
 }
@@ -613,7 +614,9 @@
 		return 1544 + freq;
 	if (sku == SKU_SR9)
 		return 3344 - freq;
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+	if (sku == SKU_XC900M)
+		return 1517 + freq;
+	HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 	    "%s: cannot map freq %u unknown gsm sku %u\n",
 	    __func__, freq, sku);
 	return freq;
@@ -727,6 +730,7 @@
 	case SKU_SR9:
 	case SKU_XR9:
 	case SKU_GZ901:
+	case SKU_XC900M:
 		/*
 		 * Map 900MHz sku's.  The frequencies will be mapped
 		 * according to the sku to compensate for the down-converter.

Modified: trunk/sys/dev/ath/ath_hal/ah_regdomain.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_regdomain.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_regdomain.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2005-2006 Atheros Communications, Inc.
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_regdomain.h 219442 2011-03-10 03:13:56Z adrian $
  */
 #ifndef	__AH_REGDOMAIN_H__
 #define	__AH_REGDOMAIN_H__

Modified: trunk/sys/dev/ath/ath_hal/ah_soc.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ah_soc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ah_soc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ah_soc.h 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #ifndef _ATH_AH_SOC_H_
 #define _ATH_AH_SOC_H_

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210.h 247286 2013-02-25 22:42:43Z adrian $
  */
 #ifndef _ATH_AR5210_H_
 #define _ATH_AR5210_H_
@@ -171,7 +172,8 @@
 		u_int txRate2, u_int txRetries2,
 		u_int txRate3, u_int txRetries3);
 extern	HAL_BOOL ar5210FillTxDesc(struct ath_hal *, struct ath_desc *,
-		u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
+		HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList,
+		u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
 		const struct ath_desc *ds0);
 extern	HAL_STATUS ar5210ProcTxDesc(struct ath_hal *,
 		struct ath_desc *, struct ath_tx_status *);
@@ -179,9 +181,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 +257,13 @@
 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 ar5210SetChainMasks(struct ath_hal *, uint32_t, uint32_t);
+extern	void ar5210EnableDfs(struct ath_hal *, HAL_PHYERR_PARAM *);
+extern	void ar5210GetDfsThresh(struct ath_hal *, HAL_PHYERR_PARAM *);
+extern	void ar5210UpdateDiagReg(struct ath_hal *ah, uint32_t val);
 
 extern	u_int ar5210GetKeyCacheSize(struct ath_hal *);
 extern	HAL_BOOL ar5210IsKeyCacheEntryValid(struct ath_hal *, uint16_t);

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c 247286 2013-02-25 22:42:43Z adrian $
  */
 #include "opt_ah.h"
 
@@ -33,7 +34,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 +76,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 +134,15 @@
 	.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_setChainMasks		= ar5210SetChainMasks,
+	.ah_enableDfs			= ar5210EnableDfs,
+	.ah_getDfsThresh		= ar5210GetDfsThresh,
+	/* XXX procRadarEvent */
+	/* XXX isFastClockEnabled */
 
 	/* Key Cache Functions */
 	.ah_getKeyCacheSize		= ar5210GetKeyCacheSize,
@@ -182,7 +194,7 @@
 	HAL_STATUS ecode;
 	int i;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH,
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH,
 	    "%s: devid 0x%x sc %p st %p sh %p\n", __func__, devid,
 	    sc, (void*) st, (void*) sh);
 
@@ -189,7 +201,7 @@
 	/* NB: memory is returned zero'd */
 	ahp = ath_hal_malloc(sizeof (struct ath_hal_5210));
 	if (ahp == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: no memory for state block\n", __func__);
 		ecode = HAL_ENOMEM;
 		goto bad;
@@ -326,7 +338,7 @@
 }
 
 static void
-ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar5210ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
 }
 
@@ -351,6 +363,8 @@
 
 	pCap->halSleepAfterBeaconBroken = AH_TRUE;
 	pCap->halPSPollBroken = AH_FALSE;
+	pCap->halNumMRRetries = 1;		/* No hardware MRR support */
+	pCap->halNumTxMaps = 1;			/* Single TX ptr per descr */
 
 	pCap->halTotalQueues = HAL_NUM_TX_QUEUES;
 	pCap->halKeyCacheSize = 64;
@@ -359,6 +373,12 @@
 	pCap->halChanHalfRate = AH_FALSE;
 	pCap->halChanQuarterRate = AH_FALSE;
 
+	/*
+	 * RSSI uses the combined field; some 11n NICs may use
+	 * the control chain RSSI.
+	 */
+	pCap->halUseCombinedRadarRssi = AH_TRUE;
+
 	if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL)) {
 		/*
 		 * Setup initial rfsilent settings based on the EEPROM

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_beacon.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_beacon.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_beacon.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_beacon.c 225444 2011-09-08 01:23:05Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_interrupts.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_interrupts.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_interrupts.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_interrupts.c 192397 2009-05-19 17:35:15Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_keycache.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_keycache.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_keycache.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_keycache.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c 247286 2013-02-25 22:42:43Z adrian $
  */
 #include "opt_ah.h"
 
@@ -576,8 +577,6 @@
 {
 }
 
-#define	AR_DIAG_SW_DIS_CRYPTO	(AR_DIAG_SW_DIS_ENC | AR_DIAG_SW_DIS_DEC)
-
 HAL_STATUS
 ar5210GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
 	uint32_t capability, uint32_t *result)
@@ -585,7 +584,11 @@
 
 	switch (type) {
 	case HAL_CAP_CIPHER:		/* cipher handled in hardware */
+#if 0
 		return (capability == HAL_CIPHER_WEP ? HAL_OK : HAL_ENOTSUPP);
+#else
+		return HAL_ENOTSUPP;
+#endif
 	default:
 		return ath_hal_getcapability(ah, type, capability, result);
 	}
@@ -608,7 +611,7 @@
 #else
 		AH_PRIVATE(ah)->ah_diagreg = setting & 0x6;	/* ACK+CTS */
 #endif
-		OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg);
+		ar5210UpdateDiagReg(ah, AH_PRIVATE(ah)->ah_diagreg);
 		return AH_TRUE;
 	case HAL_CAP_RXORN_FATAL:	/* HAL_INT_RXORN treated as fatal  */
 		return AH_FALSE;	/* NB: disallow */
@@ -646,3 +649,55 @@
 	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
+ar5210SetChainMasks(struct ath_hal *ah, uint32_t txchainmask,
+    uint32_t rxchainmask)
+{
+}
+
+void
+ar5210EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+}
+
+void
+ar5210GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+}
+
+/*
+ * Update the diagnostic register.
+ *
+ * This merges in the diagnostic register setting with the default
+ * value, which may or may not involve disabling hardware encryption.
+ */
+void
+ar5210UpdateDiagReg(struct ath_hal *ah, uint32_t val)
+{
+
+	/* Disable all hardware encryption */
+	val |= AR_DIAG_SW_DIS_CRYPTO;
+	OS_REG_WRITE(ah, AR_DIAG_SW, val);
+}

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_phy.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_phy.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_phy.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_phy.c 191022 2009-04-13 21:01:08Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_power.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_power.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_power.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_power.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_recv.c 243317 2012-11-19 23:42:46Z adrian $
  */
 #include "opt_ah.h"
 
@@ -30,8 +31,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 +42,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);
 }
 
@@ -82,7 +87,7 @@
 void
 ar5210StartPcuReceive(struct ath_hal *ah)
 {
-	OS_REG_WRITE(ah, AR_DIAG_SW,
+	ar5210UpdateDiagReg(ah,
 		OS_REG_READ(ah, AR_DIAG_SW) & ~(AR_DIAG_SW_DIS_RX));
 }
 
@@ -92,7 +97,7 @@
 void
 ar5210StopPcuReceive(struct ath_hal *ah)
 {
-	OS_REG_WRITE(ah, AR_DIAG_SW,
+	ar5210UpdateDiagReg(ah,
 		OS_REG_READ(ah, AR_DIAG_SW) | AR_DIAG_SW_DIS_RX);
 }
 

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c 243317 2012-11-19 23:42:46Z adrian $
  */
 #include "opt_ah.h"
 
@@ -190,7 +191,7 @@
 	OS_REG_WRITE(ah, AR_CLR_TMASK, 1);
 	OS_REG_WRITE(ah, AR_TRIG_LEV, 1);	/* minimum */
 
-	OS_REG_WRITE(ah, AR_DIAG_SW, 0);
+	ar5210UpdateDiagReg(ah, 0);
 
 	OS_REG_WRITE(ah, AR_CFP_PERIOD, 0);
 	OS_REG_WRITE(ah, AR_TIMER0, 0);		/* next beacon time */
@@ -285,7 +286,7 @@
 	if (ahp->ah_ctstimeout != (u_int) -1)
 		ar5210SetCTSTimeout(ah, ahp->ah_ctstimeout);
 	if (AH_PRIVATE(ah)->ah_diagreg != 0)
-		OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg);
+		ar5210UpdateDiagReg(ah, AH_PRIVATE(ah)->ah_diagreg);
 
 	AH_PRIVATE(ah)->ah_opmode = opmode;	/* record operating mode */
 
@@ -454,7 +455,7 @@
 	if (ichan == AH_NULL)
 		return AH_FALSE;
 	/* Disable tx and rx */
-	OS_REG_WRITE(ah, AR_DIAG_SW,
+	ar5210UpdateDiagReg(ah,
 		OS_REG_READ(ah, AR_DIAG_SW) | (AR_DIAG_SW_DIS_TX | AR_DIAG_SW_DIS_RX));
 
 	/* Disable Beacon Enable */
@@ -551,7 +552,7 @@
 	}
 
 	/* Clear tx and rx disable bit */
-	OS_REG_WRITE(ah, AR_DIAG_SW,
+	ar5210UpdateDiagReg(ah,
 		 OS_REG_READ(ah, AR_DIAG_SW) & ~(AR_DIAG_SW_DIS_TX | AR_DIAG_SW_DIS_RX));
 
 	/* Re-enable Beacons */
@@ -594,12 +595,10 @@
         if ((resetMask & AR_RC_RMAC) == 0) {
 		if (isBigEndian()) {
 			/*
-			 * Set CFG, little-endian for register
-			 * and descriptor accesses.
+			 * Set CFG, little-endian for descriptor accesses.
 			 */
-			mask = INIT_CONFIG_STATUS |
-				AR_CFG_SWTD | AR_CFG_SWRD | AR_CFG_SWRG;
-			OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask));
+			mask = INIT_CONFIG_STATUS | AR_CFG_SWTD | AR_CFG_SWRD;
+			OS_REG_WRITE(ah, AR_CFG, mask);
 		} else
 			OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS);
 	}

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210_xmit.c 243174 2012-11-17 02:39:37Z adrian $
  */
 #include "opt_ah.h"
 
@@ -518,7 +519,8 @@
 		ads->ds_ctl1 = 0;
 	if (flags & HAL_TXDESC_RTSENA) {
 		ads->ds_ctl0 |= AR_RTSCTSEnable;
-		ads->ds_ctl1 |= rtsctsDuration & AR_RTSDuration;
+		ads->ds_ctl1 |= (rtsctsDuration << AR_RTSDuration_S)
+		    & AR_RTSDuration;
 	}
 	return AH_TRUE;
 }
@@ -546,13 +548,17 @@
 
 HAL_BOOL
 ar5210FillTxDesc(struct ath_hal *ah, struct ath_desc *ds,
-	u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
+	HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int descId,
+	u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
 	const struct ath_desc *ds0)
 {
 	struct ar5210_desc *ads = AR5210DESC(ds);
+	uint32_t segLen = segLenList[0];
 
 	HALASSERT((segLen &~ AR_BufLen) == 0);
 
+	ds->ds_data = bufAddrList[0];
+
 	if (firstSeg) {
 		/*
 		 * First descriptor, don't clobber xmit control data
@@ -630,3 +636,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;
+}

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210desc.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210desc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210desc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,13 +15,11 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210desc.h 243173 2012-11-17 02:39:09Z adrian $
  */
 #ifndef _DEV_ATH_AR5210DESC_H
 #define _DEV_ATH_AR5210DESC_H
 
-#include "ah_desc.h"
-
 /*
  * Defintions for the DMA descriptors used by the Atheros
  * AR5210/AR5211 and AR5110 Wireless Lan controller parts.
@@ -57,6 +56,7 @@
 #define	AR_ClearDestMask	0x01000000	/* Clear destination mask bit */
 #define	AR_AntModeXmit		0x02000000	/* TX antenna seslection */
 #define	AR_FrmType		0x1c000000	/* frame type indication */
+#define	AR_FrmType_S		26
 #define	AR_Frm_Normal		0x00000000	/* normal frame */
 #define	AR_Frm_ATIM		0x04000000	/* ATIM frame */
 #define	AR_Frm_PSPOLL		0x08000000	/* PS poll frame */
@@ -71,6 +71,7 @@
 #define	AR_EncryptKeyIdx	0x0007e000	/* ecnrypt key table index */
 #define	AR_EncryptKeyIdx_S	13
 #define	AR_RTSDuration		0xfff80000	/* lower 13bit of duration */
+#define	AR_RTSDuration_S	19
 
 /* RX ds_ctl1 */
 /*	AR_BufLen		0x00000fff	   data buffer length */

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210phy.h 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #ifndef _DEV_ATH_AR5210PHY_H
 #define _DEV_ATH_AR5210PHY_H

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5210reg.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5210reg.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5210reg.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2004 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5210reg.h 243317 2012-11-19 23:42:46Z adrian $
  */
 #ifndef _DEV_ATH_AR5210REG_H
 #define _DEV_ATH_AR5210REG_H
@@ -385,6 +386,7 @@
 #define	AR_DIAG_SW_SCVRAM_SEED	0x0003f800	/* fixed scrambler seed */
 #define	AR_DIAG_SW_DIS_SEQ_INC	0x00040000	/* seq increment disable */
 #define	AR_DIAG_SW_FRAME_NV0	0x00080000	/* accept frame vers != 0 */
+#define	AR_DIAG_SW_DIS_CRYPTO	(AR_DIAG_SW_DIS_ENC | AR_DIAG_SW_DIS_DEC)
 #define	AR_DIAG_SW_BITS \
 	"\20\1DIS_WEP_ACK\2DIS_ACK\3DIS_CTS\4DIS_ENC\5DIS_DEC\6DIS_TX"\
 	"\7DIS_RX\10LOOP_BACK\11CORR_FCS\12CHAN_INFO\13EN_SCRAM_SEED"\

Modified: trunk/sys/dev/ath/ath_hal/ar5210/ar5k_0007.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5210/ar5k_0007.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5210/ar5k_0007.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5210/ar5k_0007.ini 204644 2010-03-03 17:32:32Z rpaulo $
  */
 
     /* crete register init */


Property changes on: trunk/sys/dev/ath/ath_hal/ar5210/ar5k_0007.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211.h 247286 2013-02-25 22:42:43Z adrian $
  */
 #ifndef _ATH_AR5211_H_
 #define _ATH_AR5211_H_
@@ -196,7 +197,8 @@
 		u_int txRate2, u_int txRetries2,
 		u_int txRate3, u_int txRetries3);
 extern	HAL_BOOL ar5211FillTxDesc(struct ath_hal *, struct ath_desc *,
-		u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
+		HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList,
+		u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
 		const struct ath_desc *ds0);
 extern	HAL_STATUS ar5211ProcTxDesc(struct ath_hal *,
 		struct ath_desc *, struct ath_tx_status *);
@@ -204,9 +206,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,7 +284,14 @@
 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 ar5211SetChainMasks(struct ath_hal *ah, uint32_t, uint32_t);
 
+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);
 extern	HAL_BOOL ar5211ResetKeyCacheEntry(struct ath_hal *, uint16_t entry);

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c 247286 2013-02-25 22:42:43Z adrian $
  */
 #include "opt_ah.h"
 
@@ -33,7 +34,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 +76,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 +134,15 @@
 	.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_setChainMasks		= ar5211SetChainMasks,
+	.ah_enableDfs			= ar5211EnableDfs,
+	.ah_getDfsThresh		= ar5211GetDfsThresh,
+	/* XXX procRadarEvent */
+	/* XXX isFastClockEnabled */
 
 	/* Key Cache Functions */
 	.ah_getKeyCacheSize		= ar5211GetKeyCacheSize,
@@ -201,13 +213,13 @@
 	uint16_t eeval;
 	HAL_STATUS ecode;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 	    __func__, sc, (void*) st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
 	ahp = ath_hal_malloc(sizeof (struct ath_hal_5211));
 	if (ahp == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		ecode = HAL_ENOMEM;
 		goto bad;
@@ -449,7 +461,7 @@
 }
 
 static void
-ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore)
+ar5211ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off)
 {
 }
 
@@ -486,6 +498,8 @@
 	pCap->halSleepAfterBeaconBroken = AH_TRUE;
 	pCap->halPSPollBroken = AH_TRUE;
 	pCap->halVEOLSupport = AH_TRUE;
+	pCap->halNumMRRetries = 1;	/* No hardware MRR support */
+	pCap->halNumTxMaps = 1;		/* Single TX ptr per descr */
 
 	pCap->halTotalQueues = HAL_NUM_TX_QUEUES;
 	pCap->halKeyCacheSize = 128;
@@ -494,6 +508,12 @@
 	pCap->halChanHalfRate = AH_FALSE;
 	pCap->halChanQuarterRate = AH_FALSE;
 
+	/*
+	 * RSSI uses the combined field; some 11n NICs may use
+	 * the control chain RSSI.
+	 */
+	pCap->halUseCombinedRadarRssi = AH_TRUE;
+
 	if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) &&
 	    ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) {
 		/* NB: enabled by default */

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_beacon.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_beacon.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_beacon.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_beacon.c 225444 2011-09-08 01:23:05Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_interrupts.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_interrupts.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_interrupts.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_interrupts.c 192397 2009-05-19 17:35:15Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_keycache.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_keycache.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_keycache.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_keycache.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_misc.c 247286 2013-02-25 22:42:43Z adrian $
  */
 #include "opt_ah.h"
 
@@ -688,3 +689,40 @@
 	}
 	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
+ar5211SetChainMasks(struct ath_hal *ah, uint32_t txchainmask,
+    uint32_t rxchainmask)
+{
+}
+
+void
+ar5211EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+}
+
+void
+ar5211GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+}

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_phy.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_phy.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_phy.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_phy.c 191022 2009-04-13 21:01:08Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_power.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_power.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_power.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_power.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_recv.c 238278 2012-07-09 07:19:11Z adrian $
  */
 #include "opt_ah.h"
 
@@ -30,8 +31,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 +42,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);
 }

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_reset.c 234450 2012-04-19 03:26:21Z adrian $
  */
 #include "opt_ah.h"
 
@@ -764,12 +765,10 @@
         if ((resetMask & AR_RC_MAC) == 0) {
 		if (isBigEndian()) {
 			/*
-			 * Set CFG, little-endian for register
-			 * and descriptor accesses.
+			 * Set CFG, little-endian for descriptor accesses.
 			 */
-			mask = INIT_CONFIG_STATUS |
-				AR_CFG_SWTD | AR_CFG_SWRD | AR_CFG_SWRG;
-			OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask));
+			mask = INIT_CONFIG_STATUS | AR_CFG_SWTD | AR_CFG_SWRD;
+			OS_REG_WRITE(ah, AR_CFG, mask);
 		} else
 			OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS);
 	}

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211_xmit.c 239051 2012-08-05 10:12:27Z adrian $
  */
 #include "opt_ah.h"
 
@@ -577,11 +578,15 @@
 
 HAL_BOOL
 ar5211FillTxDesc(struct ath_hal *ah, struct ath_desc *ds,
-	u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
+	HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int qcuId,
+	u_int descId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
 	const struct ath_desc *ds0)
 {
 	struct ar5211_desc *ads = AR5211DESC(ds);
+	uint32_t segLen = segLenList[0];
 
+	ds->ds_data = bufAddrList[0];
+
 	HALASSERT((segLen &~ AR_BufLen) == 0);
 
 	if (firstSeg) {
@@ -671,3 +676,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;
+}

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211desc.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211desc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211desc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,13 +15,11 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211desc.h 243249 2012-11-18 20:41:46Z adrian $
  */
 #ifndef _DEV_ATH_AR5211DESC_H
 #define _DEV_ATH_AR5211DESC_H
 
-#include "ah_desc.h"
-
 /*
  * Defintions for the DMA descriptors used by the Atheros
  * AR5211 and AR5110 Wireless Lan controller parts.

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211phy.h 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #ifndef _DEV_ATH_AR5211PHY_H
 #define _DEV_ATH_AR5211PHY_H

Modified: trunk/sys/dev/ath/ath_hal/ar5211/ar5211reg.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/ar5211reg.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/ar5211reg.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2006 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/ar5211reg.h 228980 2011-12-30 02:58:37Z dim $
  */
 #ifndef _DEV_ATH_AR5211REG_H
 #define _DEV_ATH_AR5211REG_H

Modified: trunk/sys/dev/ath/ath_hal/ar5211/boss.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5211/boss.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5211/boss.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5211/boss.ini 245952 2013-01-26 22:08:21Z pfg $
  */
 /* Auto Generated PCI Register Writes.  Created: 09/12/02 */
 


Property changes on: trunk/sys/dev/ath/ath_hal/ar5211/boss.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar2316.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar2316.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar2316.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar2316.c 187831 2009-01-28 18:00:22Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar2317.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar2317.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar2317.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar2317.c 187831 2009-01-28 18:00:22Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar2413.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar2413.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar2413.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar2413.c 187831 2009-01-28 18:00:22Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar2425.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar2425.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar2425.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar2425.c 188979 2009-02-24 01:07:06Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5111.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5111.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5111.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5111.c 187831 2009-01-28 18:00:22Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5112.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5112.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5112.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5112.c 225883 2011-09-30 05:17:57Z adrian $
  */
 #include "opt_ah.h"
 
@@ -611,7 +612,7 @@
 	uint16_t    idxR = 1;
 
 	if (numPcdacs < 2) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		     "%s: at least 2 pcdac values needed [%d]\n",
 		     __func__, numPcdacs);
 		return AH_FALSE;

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212.h 256139 2013-10-08 11:28:59Z adrian $
  */
 #ifndef _ATH_AR5212_H_
 #define _ATH_AR5212_H_
@@ -252,6 +253,7 @@
 	uint8_t		ah_macaddr[IEEE80211_ADDR_LEN];
 	uint8_t		ah_bssid[IEEE80211_ADDR_LEN];
 	uint8_t		ah_bssidmask[IEEE80211_ADDR_LEN];
+	uint16_t	ah_assocId;
 
 	/*
 	 * Runtime state.
@@ -319,6 +321,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);
@@ -333,6 +336,16 @@
 
 	uint8_t		ah_txTrigLev;		/* current Tx trigger level */
 	uint8_t		ah_maxTxTrigLev;	/* max tx trigger level */
+
+	/*
+	 * Channel Tx, Rx, Rx Clear State
+	 */
+	uint32_t	ah_cycleCount;
+	uint32_t	ah_ctlBusy;
+	uint32_t	ah_rxBusy;
+	uint32_t	ah_txBusy;
+	uint32_t	ah_rx_chainmask;
+	uint32_t	ah_tx_chainmask;
 };
 #define	AH5212(_ah)	((struct ath_hal_5212 *)(_ah))
 
@@ -509,6 +522,9 @@
 		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	void ar5212SetChainMasks(struct ath_hal *, uint32_t, uint32_t);
 
 extern	HAL_BOOL ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode,
 		int setChip);
@@ -515,8 +531,8 @@
 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);
@@ -590,7 +606,8 @@
 		u_int txRate2, u_int txRetries2,
 		u_int txRate3, u_int txRetries3);
 extern	HAL_BOOL ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds,
-		u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
+		HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList,
+		u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
 		const struct ath_desc *ds0);
 extern	HAL_STATUS ar5212ProcTxDesc(struct ath_hal *ah,
 		struct ath_desc *, struct ath_tx_status *);
@@ -598,6 +615,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);
 
@@ -622,10 +645,13 @@
 extern	HAL_BOOL ar5212IsNFCalInProgress(struct ath_hal *ah);
 extern	HAL_BOOL ar5212WaitNFCalComplete(struct ath_hal *ah, int i);
 extern	void ar5212EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe);
+extern	HAL_BOOL ar5212GetDfsDefaultThresh(struct ath_hal *ah,
+	    HAL_PHYERR_PARAM *pe);
 extern	void ar5212GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe);
 extern	HAL_BOOL ar5212ProcessRadarEvent(struct ath_hal *ah,
 	    struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf,
 	    HAL_DFS_EVENT *event);
 extern	HAL_BOOL ar5212IsFastClockEnabled(struct ath_hal *ah);
+extern	uint32_t ar5212Get11nExtBusy(struct ath_hal *ah);
 
 #endif	/* _ATH_AR5212_H_ */

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212.ini 204644 2010-03-03 17:32:32Z rpaulo $
  */
 /* Auto Generated PCI Register Writes.  Created: 09/01/04 */
 


Property changes on: trunk/sys/dev/ath/ath_hal/ar5212/ar5212.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c 256139 2013-10-08 11:28:59Z adrian $
  */
 #include "opt_ah.h"
 
@@ -865,16 +866,40 @@
 ar5212AniGetListenTime(struct ath_hal *ah)
 {
 	struct ath_hal_5212 *ahp = AH5212(ah);
-	struct ar5212AniState *aniState;
-	uint32_t txFrameCount, rxFrameCount, cycleCount;
-	int32_t listenTime;
+	struct ar5212AniState *aniState = NULL;
+	int32_t listenTime = 0;
+	int good;
+	HAL_SURVEY_SAMPLE hs;
+	HAL_CHANNEL_SURVEY *cs = AH_NULL;
 
-	txFrameCount = OS_REG_READ(ah, AR_TFCNT);
-	rxFrameCount = OS_REG_READ(ah, AR_RFCNT);
-	cycleCount = OS_REG_READ(ah, AR_CCCNT);
+	/*
+	 * 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 (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
+	cs = &ahp->ah_chansurvey;
+
+	/*
+	 * Fetch the current statistics, squirrel away the current
+	 * sample, bump the sequence/sample counter.
+	 */
+	OS_MEMZERO(&hs, sizeof(hs));
+	good = ar5212GetMibCycleCounts(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
@@ -882,15 +907,29 @@
 		 */
 		listenTime = 0;
 		ahp->ah_stats.ast_ani_lzero++;
-	} else {
-		int32_t ccdelta = cycleCount - aniState->cycleCount;
-		int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
-		int32_t tfdelta = txFrameCount - aniState->txFrameCount;
+	} else if (ANI_ENA(ah)) {
+		/*
+		 * Only calculate and update the cycle count if we have
+		 * an ANI state.
+		 */
+		int32_t ccdelta =
+		    AH5212(ah)->ah_cycleCount - aniState->cycleCount;
+		int32_t rfdelta =
+		    AH5212(ah)->ah_rxBusy - aniState->rxFrameCount;
+		int32_t tfdelta =
+		    AH5212(ah)->ah_txBusy - aniState->txFrameCount;
 		listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE;
 	}
-	aniState->cycleCount = cycleCount;
-	aniState->txFrameCount = txFrameCount;
-	aniState->rxFrameCount = rxFrameCount;
+
+	/*
+	 * Again, only update ANI state if we have it.
+	 */
+	if (ANI_ENA(ah)) {
+		aniState->cycleCount = AH5212(ah)->ah_cycleCount;
+		aniState->rxFrameCount = AH5212(ah)->ah_rxBusy;
+		aniState->txFrameCount = AH5212(ah)->ah_txBusy;
+	}
+
 	return listenTime;
 }
 
@@ -956,6 +995,9 @@
 	const struct ar5212AniParams *params;
 	int32_t listenTime;
 
+	/* Always update from the MIB, for statistics gathering */
+	listenTime = ar5212AniGetListenTime(ah);
+
 	/* XXX can aniState be null? */
 	if (aniState == AH_NULL)
 		return;
@@ -962,7 +1004,6 @@
 	if (!ANI_ENA(ah))
 		return;
 
-	listenTime = ar5212AniGetListenTime(ah);
 	if (listenTime < 0) {
 		ahp->ah_stats.ast_ani_lneg++;
 		/* restart ANI period if listenTime is invalid */

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c 305615 2016-09-08 15:06:28Z pfg $
  */
 #include "opt_ah.h"
 
@@ -29,7 +30,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 +72,9 @@
 	.ah_getTxIntrQueue		= ar5212GetTxIntrQueue,
 	.ah_reqTxIntrDesc 		= ar5212IntrReqTxDesc,
 	.ah_getTxCompletionRates	= ar5212GetTxCompletionRates,
+	.ah_setTxDescLink		= ar5212SetTxDescLink,
+	.ah_getTxDescLink		= ar5212GetTxDescLink,
+	.ah_getTxDescLinkPtr		= ar5212GetTxDescLinkPtr,
 
 	/* RX Functions */
 	.ah_getRxDP			= ar5212GetRxDP,
@@ -107,6 +112,7 @@
 	.ah_gpioSetIntr			= ar5212GpioSetIntr,
 	.ah_getTsf32			= ar5212GetTsf32,
 	.ah_getTsf64			= ar5212GetTsf64,
+	.ah_setTsf64			= ar5212SetTsf64,
 	.ah_resetTsf			= ar5212ResetTsf,
 	.ah_detectCardPresent		= ar5212DetectCardPresent,
 	.ah_updateMibCounters		= ar5212UpdateMibCounters,
@@ -125,15 +131,19 @@
 	.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,
+	.ah_setChainMasks		= ar5212SetChainMasks,
 
 	/* DFS Functions */
 	.ah_enableDfs			= ar5212EnableDfs,
 	.ah_getDfsThresh		= ar5212GetDfsThresh,
+	.ah_getDfsDefaultThresh		= ar5212GetDfsDefaultThresh,
 	.ah_procRadarEvent		= ar5212ProcessRadarEvent,
 	.ah_isFastClockEnabled		= ar5212IsFastClockEnabled,
+	.ah_get11nExtBusy		= ar5212Get11nExtBusy,
 
 	/* Key Cache Functions */
 	.ah_getKeyCacheSize		= ar5212GetKeyCacheSize,
@@ -261,8 +271,8 @@
 	ahp->ah_acktimeout = (u_int) -1;
 	ahp->ah_ctstimeout = (u_int) -1;
 	ahp->ah_sifstime = (u_int) -1;
-	ahp->ah_txTrigLev = INIT_TX_FIFO_THRESHOLD,
-	ahp->ah_maxTxTrigLev = MAX_TX_FIFO_THRESHOLD,
+	ahp->ah_txTrigLev = INIT_TX_FIFO_THRESHOLD;
+	ahp->ah_maxTxTrigLev = MAX_TX_FIFO_THRESHOLD;
 
 	OS_MEMCPY(&ahp->ah_bssidmask, defbssidmask, IEEE80211_ADDR_LEN);
 #undef N
@@ -319,13 +329,13 @@
 	uint16_t eeval;
 	HAL_STATUS ecode;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 	    __func__, sc, (void*) st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
 	ahp = ath_hal_malloc(sizeof (struct ath_hal_5212));
 	if (ahp == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		*status = HAL_ENOMEM;
 		return AH_NULL;
@@ -368,7 +378,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)) {
@@ -664,7 +674,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);
@@ -779,7 +789,29 @@
 	else
 		pCap->halHigh2GhzChan = 2732;
 
-	pCap->halLow5GhzChan = 4915;
+	/*
+	 * For AR5111 version < 4, the lowest centre frequency supported is
+	 * 5130MHz.  For AR5111 version 4, the 4.9GHz channels are supported
+	 * but only in 10MHz increments.
+	 *
+	 * In addition, the programming method is wrong - it uses the IEEE
+	 * channel number to calculate the frequency, rather than the
+	 * channel centre.  Since half/quarter rates re-use some of the
+	 * 5GHz channel IEEE numbers, this will result in a badly programmed
+	 * synth.
+	 *
+	 * Until the relevant support is written, just limit lower frequency
+	 * support for AR5111 so things aren't incorrectly programmed.
+	 *
+	 * XXX It's also possible this code doesn't correctly limit the
+	 * centre frequencies of potential channels; this is very important
+	 * for half/quarter rate!
+	 */
+	if (AH_RADIO_MAJOR(ah) == AR_RAD5111_SREV_MAJOR) {
+		pCap->halLow5GhzChan = 5120; /* XXX lowest centre = 5130MHz */
+	} else {
+		pCap->halLow5GhzChan = 4915;
+	}
 	pCap->halHigh5GhzChan = 6100;
 
 	pCap->halCipherCkipSupport = AH_FALSE;
@@ -818,6 +850,8 @@
 	pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G;
 
 	pCap->halPSPollBroken = AH_TRUE;	/* XXX fixed in later revs? */
+	pCap->halNumMRRetries = 4;		/* Hardware supports 4 MRR */
+	pCap->halNumTxMaps = 1;			/* Single TX ptr per descr */
 	pCap->halVEOLSupport = AH_TRUE;
 	pCap->halBssIdMaskSupport = AH_TRUE;
 	pCap->halMcastKeySrchSupport = AH_TRUE;
@@ -839,6 +873,12 @@
 	pCap->halChanHalfRate = AH_TRUE;
 	pCap->halChanQuarterRate = AH_TRUE;
 
+	/*
+	 * RSSI uses the combined field; some 11n NICs may use
+	 * the control chain RSSI.
+	 */
+	pCap->halUseCombinedRadarRssi = AH_TRUE;
+
 	if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) &&
 	    ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) {
 		/* NB: enabled by default */

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_beacon.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_beacon.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_beacon.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_beacon.c 243588 2012-11-27 02:18:41Z adrian $
  */
 #include "opt_ah.h"
 
@@ -44,12 +45,22 @@
 void
 ar5212SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt)
 {
+	struct ath_hal_5212 *ahp = AH5212(ah);
 
-	OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt);
-	OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba);
-	OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba);
-	OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim);
 	/*
+	 * Limit the timers to their specific resolutions:
+	 *
+	 * + Timer 0 - 0..15 0xffff TU
+	 * + Timer 1 - 0..18 0x7ffff TU/8
+	 * + Timer 2 - 0..24 0x1ffffff TU/8
+	 * + Timer 3 - 0..15 0xffff TU
+	 */
+	OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt & 0xffff);
+	OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba & 0x7ffff);
+	OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba & 0x1ffffff);
+	/* XXX force nextatim to be non-zero? */
+	OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim & 0xffff);
+	/*
 	 * Set the Beacon register after setting all timers.
 	 */
 	if (bt->bt_intval & AR_BEACON_RESET_TSF) {
@@ -65,6 +76,7 @@
 		OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_RESET_TSF);
 	}
 	OS_REG_WRITE(ah, AR_BEACON, bt->bt_intval);
+	ahp->ah_beaconInterval = (bt->bt_intval & HAL_BEACON_PERIOD);
 }
 
 /*

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_eeprom.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_eeprom.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_eeprom.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_eeprom.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_gpio.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_gpio.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_gpio.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_gpio.c 188974 2009-02-24 00:12:16Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_interrupts.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_interrupts.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_interrupts.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_interrupts.c 201758 2010-01-07 21:01:37Z mbr $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_keycache.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_keycache.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_keycache.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_keycache.c 218483 2011-02-09 15:23:16Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c 256139 2013-10-08 11:28:59Z adrian $
  */
 #include "opt_ah.h"
 
@@ -218,8 +219,9 @@
 {
 	struct ath_hal_5212 *ahp = AH5212(ah);
 
-	/* XXX save bssid for possible re-use on reset */
+	/* save bssid for possible re-use on reset */
 	OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN);
+	ahp->ah_assocId = assocId;
 	OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid));
 	OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) |
 				     ((assocId & 0x3fff)<<AR_BSS_ID1_AID_S));
@@ -842,6 +844,10 @@
 			return HAL_OK;
 		case 1:			/* current setting */
 			return ahp->ah_diversity ? HAL_OK : HAL_ENXIO;
+		case HAL_CAP_STRONG_DIV:
+			*result = OS_REG_READ(ah, AR_PHY_RESTART);
+			*result = MS(*result, AR_PHY_RESTART_DIV_GC);
+			return HAL_OK;
 		}
 		return HAL_EINVAL;
 	case HAL_CAP_DIAG:
@@ -949,16 +955,34 @@
 		OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode);
 		return AH_TRUE;
 	case HAL_CAP_DIVERSITY:
-		if (ahp->ah_phyPowerOn) {
-			v = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
-			if (setting)
-				v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
-			else
-				v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
-			OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
+		switch (capability) {
+		case 0:
+			return AH_FALSE;
+		case 1:	/* setting */
+			if (ahp->ah_phyPowerOn) {
+				if (capability == HAL_CAP_STRONG_DIV) {
+					v = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
+					if (setting)
+						v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
+					else
+						v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
+					OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
+				}
+			}
+			ahp->ah_diversity = (setting != 0);
+			return AH_TRUE;
+
+		case HAL_CAP_STRONG_DIV:
+			if (! ahp->ah_phyPowerOn)
+				return AH_FALSE;
+			v = OS_REG_READ(ah, AR_PHY_RESTART);
+			v &= ~AR_PHY_RESTART_DIV_GC;
+			v |= SM(setting, AR_PHY_RESTART_DIV_GC);
+			OS_REG_WRITE(ah, AR_PHY_RESTART, v);
+			return AH_TRUE;
+		default:
+			return AH_FALSE;
 		}
-		ahp->ah_diversity = (setting != 0);
-		return AH_TRUE;
 	case HAL_CAP_DIAG:		/* hardware diagnostic support */
 		/*
 		 * NB: could split this up into virtual capabilities,
@@ -1089,6 +1113,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;
 }
@@ -1154,9 +1183,122 @@
 		val &= ~AR_PHY_RADAR_0_INBAND;
 		val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND);
 	}
-	OS_REG_WRITE(ah, AR_PHY_RADAR_0, val | AR_PHY_RADAR_0_ENA);
+	if (pe->pe_enabled)
+		val |= AR_PHY_RADAR_0_ENA;
+	else
+		val &= ~ AR_PHY_RADAR_0_ENA;
+
+	if (IS_5413(ah)) {
+
+		if (pe->pe_blockradar == 1)
+			OS_REG_SET_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_BLOCKOFDMWEAK);
+		else
+			OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_BLOCKOFDMWEAK);
+
+		if (pe->pe_en_relstep_check == 1)
+			OS_REG_SET_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_ENRELSTEPCHK);
+		else
+			OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_ENRELSTEPCHK);
+
+		if (pe->pe_usefir128 == 1)
+			OS_REG_SET_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_USEFIR128);
+		else
+			OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_USEFIR128);
+
+		if (pe->pe_enmaxrssi == 1)
+			OS_REG_SET_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_ENMAXRSSI);
+		else
+			OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_ENMAXRSSI);
+
+		if (pe->pe_enrelpwr == 1)
+			OS_REG_SET_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_ENRELPWRCHK);
+		else
+			OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_ENRELPWRCHK);
+
+		if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL)
+			OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_RELPWR, pe->pe_relpwr);
+
+		if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL)
+			OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_RELSTEP, pe->pe_relstep);
+
+		if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL)
+			OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2,
+			    AR_PHY_RADAR_2_MAXLEN, pe->pe_maxlen);
+	}
+
+	OS_REG_WRITE(ah, AR_PHY_RADAR_0, val);
 }
 
+/*
+ * Parameters for the AR5212 PHY.
+ */
+#define	AR5212_DFS_FIRPWR	-35
+#define	AR5212_DFS_RRSSI	20
+#define	AR5212_DFS_HEIGHT	14
+#define	AR5212_DFS_PRSSI	6
+#define	AR5212_DFS_INBAND	4
+
+/*
+ * Default parameters for the AR5413 PHY.
+ */
+#define	AR5413_DFS_FIRPWR	-34
+#define	AR5413_DFS_RRSSI	20
+#define	AR5413_DFS_HEIGHT	10
+#define	AR5413_DFS_PRSSI	15
+#define	AR5413_DFS_INBAND	6
+#define	AR5413_DFS_RELPWR	8
+#define	AR5413_DFS_RELSTEP	31
+#define	AR5413_DFS_MAXLEN	255
+
+HAL_BOOL
+ar5212GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+
+	if (IS_5413(ah)) {
+		pe->pe_firpwr = AR5413_DFS_FIRPWR;
+		pe->pe_rrssi = AR5413_DFS_RRSSI;
+		pe->pe_height = AR5413_DFS_HEIGHT;
+		pe->pe_prssi = AR5413_DFS_PRSSI;
+		pe->pe_inband = AR5413_DFS_INBAND;
+		pe->pe_relpwr = AR5413_DFS_RELPWR;
+		pe->pe_relstep = AR5413_DFS_RELSTEP;
+		pe->pe_maxlen = AR5413_DFS_MAXLEN;
+		pe->pe_usefir128 = 0;
+		pe->pe_blockradar = 1;
+		pe->pe_enmaxrssi = 1;
+		pe->pe_enrelpwr = 1;
+		pe->pe_en_relstep_check = 0;
+	} else {
+		pe->pe_firpwr = AR5212_DFS_FIRPWR;
+		pe->pe_rrssi = AR5212_DFS_RRSSI;
+		pe->pe_height = AR5212_DFS_HEIGHT;
+		pe->pe_prssi = AR5212_DFS_PRSSI;
+		pe->pe_inband = AR5212_DFS_INBAND;
+		pe->pe_relpwr = 0;
+		pe->pe_relstep = 0;
+		pe->pe_maxlen = 0;
+		pe->pe_usefir128 = 0;
+		pe->pe_blockradar = 0;
+		pe->pe_enmaxrssi = 0;
+		pe->pe_enrelpwr = 0;
+		pe->pe_en_relstep_check = 0;
+	}
+
+	return (AH_TRUE);
+}
+
 void
 ar5212GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
 {
@@ -1171,11 +1313,31 @@
 	pe->pe_height =  MS(val, AR_PHY_RADAR_0_HEIGHT);
 	pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI);
 	pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND);
+	pe->pe_enabled = !! (val & AR_PHY_RADAR_0_ENA);
 
 	pe->pe_relpwr = 0;
 	pe->pe_relstep = 0;
 	pe->pe_maxlen = 0;
+	pe->pe_usefir128 = 0;
+	pe->pe_blockradar = 0;
+	pe->pe_enmaxrssi = 0;
+	pe->pe_enrelpwr = 0;
+	pe->pe_en_relstep_check = 0;
 	pe->pe_extchannel = AH_FALSE;
+
+	if (IS_5413(ah)) {
+		val = OS_REG_READ(ah, AR_PHY_RADAR_2);
+		pe->pe_relpwr = !! MS(val, AR_PHY_RADAR_2_RELPWR);
+		pe->pe_relstep = !! MS(val, AR_PHY_RADAR_2_RELSTEP);
+		pe->pe_maxlen = !! MS(val, AR_PHY_RADAR_2_MAXLEN);
+
+		pe->pe_usefir128 = !! (val & AR_PHY_RADAR_2_USEFIR128);
+		pe->pe_blockradar = !! (val & AR_PHY_RADAR_2_BLOCKOFDMWEAK);
+		pe->pe_enmaxrssi = !! (val & AR_PHY_RADAR_2_ENMAXRSSI);
+		pe->pe_enrelpwr = !! (val & AR_PHY_RADAR_2_ENRELPWRCHK);
+		pe->pe_en_relstep_check =
+		    !! (val & AR_PHY_RADAR_2_ENRELSTEPCHK);
+	}
 }
 
 /*
@@ -1232,3 +1394,63 @@
 {
 	return AH_FALSE;
 }
+
+/*
+ * Return what percentage of the extension channel is busy.
+ * This is always disabled for AR5212 series NICs.
+ */
+uint32_t
+ar5212Get11nExtBusy(struct ath_hal *ah)
+{
+	return 0;
+}
+
+/*
+ * Channel survey support.
+ */
+HAL_BOOL
+ar5212GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample)
+{
+	struct ath_hal_5212 *ahp = AH5212(ah);
+	u_int32_t good = AH_TRUE;
+
+	/* XXX freeze/unfreeze mib counters */
+	uint32_t rc = OS_REG_READ(ah, AR_RCCNT);
+	uint32_t rf = OS_REG_READ(ah, AR_RFCNT);
+	uint32_t tf = OS_REG_READ(ah, AR_TFCNT);
+	uint32_t cc = OS_REG_READ(ah, AR_CCCNT); /* read cycles last */
+
+	if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cc) {
+		/*
+		 * Cycle counter wrap (or initial call); it's not possible
+		 * to accurately calculate a value because the registers
+		 * right shift rather than wrap--so punt and return 0.
+		 */
+		HALDEBUG(ah, HAL_DEBUG_ANY,
+		    "%s: cycle counter wrap. ExtBusy = 0\n", __func__);
+		good = AH_FALSE;
+	} else {
+		hsample->cycle_count = cc - ahp->ah_cycleCount;
+		hsample->chan_busy = rc - ahp->ah_ctlBusy;
+		hsample->ext_chan_busy = 0;
+		hsample->rx_busy = rf - ahp->ah_rxBusy;
+		hsample->tx_busy = tf - ahp->ah_txBusy;
+	}
+
+	/*
+	 * 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;
+
+	return (good);
+}
+
+void
+ar5212SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask,
+    uint32_t rx_chainmask)
+{
+}

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_phy.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_phy.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_phy.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_phy.c 191022 2009-04-13 21:01:08Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_power.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_power.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_power.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_power.c 203159 2010-01-29 10:10:14Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,12 +15,13 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_recv.c 243169 2012-11-17 02:02:36Z adrian $
  */
 #include "opt_ah.h"
 
 #include "ah.h"
 #include "ah_internal.h"
+#include "ah_desc.h"
 
 #include "ar5212/ar5212.h"
 #include "ar5212/ar5212reg.h"
@@ -29,8 +31,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 +42,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);
 }
@@ -272,6 +278,14 @@
 	rs->rs_antenna  = MS(ads->ds_rxstatus0, AR_RcvAntenna);
 	rs->rs_more = (ads->ds_rxstatus0 & AR_More) ? 1 : 0;
 
+	/*
+	 * The AR5413 (at least) 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_rxstatus1 & AR_FrmRcvOK) == 0) {
 		/*
 		 * These four bits should not be set together.  The
@@ -282,9 +296,7 @@
 		 * Consequently we filter them out here so we don't
 		 * confuse and/or complicate drivers.
 		 */
-		if (ads->ds_rxstatus1 & AR_CRCErr)
-			rs->rs_status |= HAL_RXERR_CRC;
-		else if (ads->ds_rxstatus1 & AR_PHYErr) {
+		if (ads->ds_rxstatus1 & AR_PHYErr) {
 			u_int phyerr;
 
 			rs->rs_status |= HAL_RXERR_PHY;
@@ -293,7 +305,11 @@
 			if (!AH5212(ah)->ah_hasHwPhyCounters &&
 			    phyerr != HAL_PHYERR_RADAR)
 				ar5212AniPhyErrReport(ah, rs);
-		} else if (ads->ds_rxstatus1 & AR_DecryptCRCErr)
+		}
+
+		if (ads->ds_rxstatus1 & AR_CRCErr)
+			rs->rs_status |= HAL_RXERR_CRC;
+		else if (ads->ds_rxstatus1 & AR_DecryptCRCErr)
 			rs->rs_status |= HAL_RXERR_DECRYPT;
 		else if (ads->ds_rxstatus1 & AR_MichaelErr)
 			rs->rs_status |= HAL_RXERR_MIC;

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c 275471 2014-12-04 01:10:50Z dim $
  */
 #include "opt_ah.h"
 
@@ -96,6 +97,16 @@
 #define IS_DISABLE_FAST_ADC_CHAN(x) (((x) == 2462) || ((x) == 2467))
 
 /*
+ * XXX NDIS 5.x code had MAX_RESET_WAIT set to 2000 for AP code
+ * and 10 for Client code
+ */
+#define	MAX_RESET_WAIT			10
+
+#define	TX_QUEUEPEND_CHECK		1
+#define	TX_ENABLE_CHECK			2
+#define	RX_ENABLE_CHECK			4
+
+/*
  * Places the device in and out of reset and then places sane
  * values in the registers based on EEPROM config, initialization
  * vectors (as determined by the mode), and station configuration
@@ -185,6 +196,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
@@ -428,9 +442,10 @@
 	/* Restore previous antenna */
 	OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna);
 
-	/* then our BSSID */
+	/* then our BSSID and associate id */
 	OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid));
-	OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4));
+	OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) |
+	    (ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S);
 
 	/* Restore bmiss rssi & count thresholds */
 	OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr);
@@ -1104,6 +1119,76 @@
 	return AH_TRUE;
 }
 
+/**************************************************************
+ * ar5212MacStop
+ *
+ * Disables all active QCUs and ensure that the mac is in a
+ * quiessence state.
+ */
+static HAL_BOOL
+ar5212MacStop(struct ath_hal *ah)
+{
+	HAL_BOOL     status;
+	uint32_t    count;
+	uint32_t    pendFrameCount;
+	uint32_t    macStateFlag;
+	uint32_t    queue;
+
+	status = AH_FALSE;
+
+	/* Disable Rx Operation ***********************************/
+	OS_REG_SET_BIT(ah, AR_CR, AR_CR_RXD);
+
+	/* Disable TX Operation ***********************************/
+#ifdef NOT_YET
+	ar5212SetTxdpInvalid(ah);
+#endif
+	OS_REG_SET_BIT(ah, AR_Q_TXD, AR_Q_TXD_M);
+
+	/* Polling operation for completion of disable ************/
+	macStateFlag = TX_ENABLE_CHECK | RX_ENABLE_CHECK;
+
+	for (count = 0; count < MAX_RESET_WAIT; count++) {
+		if (macStateFlag & RX_ENABLE_CHECK) {
+			if (!OS_REG_IS_BIT_SET(ah, AR_CR, AR_CR_RXE)) {
+				macStateFlag &= ~RX_ENABLE_CHECK;
+			}
+		}
+
+		if (macStateFlag & TX_ENABLE_CHECK) {
+			if (!OS_REG_IS_BIT_SET(ah, AR_Q_TXE, AR_Q_TXE_M)) {
+				macStateFlag &= ~TX_ENABLE_CHECK;
+				macStateFlag |= TX_QUEUEPEND_CHECK;
+			}
+		}
+		if (macStateFlag & TX_QUEUEPEND_CHECK) {
+			pendFrameCount = 0;
+			for (queue = 0; queue < AR_NUM_DCU; queue++) {
+				pendFrameCount += OS_REG_READ(ah,
+				    AR_Q0_STS + (queue * 4)) &
+				    AR_Q_STS_PEND_FR_CNT;
+			}
+			if (pendFrameCount == 0) {
+				macStateFlag &= ~TX_QUEUEPEND_CHECK;
+			}
+		}
+		if (macStateFlag == 0) {
+			status = AH_TRUE;
+			break;
+		}
+		OS_DELAY(50);
+	}
+
+	if (status != AH_TRUE) {
+		HALDEBUG(ah, HAL_DEBUG_RESET,
+		    "%s:Failed to stop the MAC state 0x%x\n",
+		    __func__, macStateFlag);
+	}
+
+	return status;
+}
+
+
 /*
  * Write the given reset bit mask into the reset register
  */
@@ -1113,12 +1198,75 @@
 	uint32_t mask = resetMask ? resetMask : ~0;
 	HAL_BOOL rt;
 
-	/* XXX ar5212MacStop & co. */
-
+	/* Never reset the PCIE core */
 	if (AH_PRIVATE(ah)->ah_ispcie) {
 		resetMask &= ~AR_RC_PCI;
 	}
 
+	if (resetMask & (AR_RC_MAC | AR_RC_PCI)) {
+		/*
+		 * To ensure that the driver can reset the
+		 * MAC, wake up the chip
+		 */
+		rt = ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE);
+
+		if (rt != AH_TRUE) {
+			return rt;
+		}
+
+		/*
+		 * Disable interrupts
+		 */
+		OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
+		OS_REG_READ(ah, AR_IER);
+
+		if (ar5212MacStop(ah) != AH_TRUE) {
+			/*
+			 * Failed to stop the MAC gracefully; let's be more forceful then
+			 */
+
+			/* need some delay before flush any pending MMR writes */
+			OS_DELAY(15);
+			OS_REG_READ(ah, AR_RXDP);
+
+			resetMask |= AR_RC_MAC | AR_RC_BB;
+			/* _Never_ reset PCI Express core */
+			if (! AH_PRIVATE(ah)->ah_ispcie) {
+				resetMask |= AR_RC_PCI;
+			}
+#if 0
+			/*
+			 * Flush the park address of the PCI controller
+			*/
+			/* Read PCI slot information less than Hainan revision */
+			if (AH_PRIVATE(ah)->ah_bustype == HAL_BUS_TYPE_PCI) {
+				if (!IS_5112_REV5_UP(ah)) {
+#define PCI_COMMON_CONFIG_STATUS    0x06
+					u_int32_t    i;
+					u_int16_t    reg16;
+
+					for (i = 0; i < 32; i++) {
+						ath_hal_read_pci_config_space(ah,
+						    PCI_COMMON_CONFIG_STATUS,
+						    &reg16, sizeof(reg16));
+					}
+				}
+#undef PCI_COMMON_CONFIG_STATUS
+			}
+#endif
+		} else {
+			/*
+			 * MAC stopped gracefully; no need to warm-reset the PCI bus
+			 */
+
+			resetMask &= ~AR_RC_PCI;
+
+			/* need some delay before flush any pending MMR writes */
+			OS_DELAY(15);
+			OS_REG_READ(ah, AR_RXDP);
+		}
+	}
+
 	(void) OS_REG_READ(ah, AR_RXDP);/* flush any pending MMR writes */
 	OS_REG_WRITE(ah, AR_RC, resetMask);
 	OS_DELAY(15);			/* need to wait at least 128 clocks
@@ -1129,14 +1277,13 @@
         if ((resetMask & AR_RC_MAC) == 0) {
 		if (isBigEndian()) {
 			/*
-			 * Set CFG, little-endian for register
-			 * and descriptor accesses.
+			 * Set CFG, little-endian for descriptor accesses.
 			 */
-			mask = INIT_CONFIG_STATUS | AR_CFG_SWRD | AR_CFG_SWRG;
+			mask = INIT_CONFIG_STATUS | AR_CFG_SWRD;
 #ifndef AH_NEED_DESC_SWAP
 			mask |= AR_CFG_SWTD;
 #endif
-			OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask));
+			OS_REG_WRITE(ah, AR_CFG, mask);
 		} else
 			OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS);
 		if (ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
@@ -2459,6 +2606,12 @@
 		powInfo[ixlo].twicePwr54, powInfo[ixhi].twicePwr54);
 }
 
+static uint32_t
+udiff(uint32_t u, uint32_t v)
+{
+	return (u >= v ? u - v : v - u);
+}
+
 /*
  * Search a list for a specified value v that is within
  * EEP_DELTA of the search values.  Return the closest
@@ -2493,7 +2646,7 @@
 		 * If value is close to the current value of the list
 		 * then target is not between values, it is one of the values
 		 */
-		if (abs(lp[0] * EEP_SCALE - target) < EEP_DELTA) {
+		if (udiff(lp[0] * EEP_SCALE, target) < EEP_DELTA) {
 			*vlo = *vhi = lp[0];
 			return;
 		}

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_rfgain.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_rfgain.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_rfgain.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_rfgain.c 188197 2009-02-05 21:13:31Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,12 +15,13 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212_xmit.c 243169 2012-11-17 02:02:36Z adrian $
  */
 #include "opt_ah.h"
 
 #include "ah.h"
 #include "ah_internal.h"
+#include "ah_desc.h"
 
 #include "ar5212/ar5212.h"
 #include "ar5212/ar5212reg.h"
@@ -263,6 +265,7 @@
  * Assumes:
  *  phwChannel has been set to point to the current channel
  */
+#define	TU_TO_USEC(_tu)		((_tu) << 10)
 HAL_BOOL
 ar5212ResetTxQueue(struct ath_hal *ah, u_int q)
 {
@@ -270,7 +273,7 @@
 	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
 	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
 	HAL_TX_QUEUE_INFO *qi;
-	uint32_t cwMin, chanCwMin, value, qmisc, dmisc;
+	uint32_t cwMin, chanCwMin, qmisc, dmisc;
 
 	if (q >= pCap->halTotalQueues) {
 		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n",
@@ -409,17 +412,40 @@
 		      |  AR_Q_MISC_CBR_INCR_DIS1
 		      |  AR_Q_MISC_CBR_INCR_DIS0;
 
-		if (!qi->tqi_readyTime) {
+		if (qi->tqi_readyTime) {
+			HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
+			    "%s: using tqi_readyTime\n", __func__);
+			OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
+			    SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) |
+			    AR_Q_RDYTIMECFG_ENA);
+		} else {
+			int value;
 			/*
 			 * NB: don't set default ready time if driver
 			 * has explicitly specified something.  This is
 			 * here solely for backwards compatibility.
 			 */
-			value = (ahp->ah_beaconInterval
+			/*
+			 * XXX for now, hard-code a CAB interval of 70%
+			 * XXX of the total beacon interval.
+			 */
+
+			value = (ahp->ah_beaconInterval * 70 / 100)
 				- (ah->ah_config.ah_sw_beacon_response_time -
-					ah->ah_config.ah_dma_beacon_response_time)
-				- ah->ah_config.ah_additional_swba_backoff) * 1024;
-			OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_ENA);
+				+ ah->ah_config.ah_dma_beacon_response_time)
+				- ah->ah_config.ah_additional_swba_backoff;
+			/*
+			 * XXX Ensure it isn't too low - nothing lower
+			 * XXX than 10 TU
+			 */
+			if (value < 10)
+				value = 10;
+			HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
+			    "%s: defaulting to rdytime = %d uS\n",
+			    __func__, value);
+			OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
+			    SM(TU_TO_USEC(value), AR_Q_RDYTIMECFG_INT) |
+			    AR_Q_RDYTIMECFG_ENA);
 		}
 		dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
 			    AR_D_MISC_ARB_LOCKOUT_CNTRL);
@@ -481,6 +507,7 @@
 
 	return AH_TRUE;
 }
+#undef	TU_TO_USEC
 
 /*
  * Get the TXDP for the specified queue
@@ -777,13 +804,17 @@
 
 HAL_BOOL
 ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds,
-	u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
+	HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int qcuId,
+	u_int descId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
 	const struct ath_desc *ds0)
 {
 	struct ar5212_desc *ads = AR5212DESC(ds);
+	uint32_t segLen = segLenList[0];
 
 	HALASSERT((segLen &~ AR_BufLen) == 0);
 
+	ds->ds_data = bufAddrList[0];
+
 	if (firstSeg) {
 		/*
 		 * First descriptor, don't clobber xmit control data
@@ -796,12 +827,14 @@
 		 * copy the multi-rate transmit parameters from
 		 * the first frame for processing on completion. 
 		 */
-		ads->ds_ctl0 = 0;
 		ads->ds_ctl1 = segLen;
 #ifdef AH_NEED_DESC_SWAP
+		ads->ds_ctl0 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl0)
+		    & AR_TxInterReq;
 		ads->ds_ctl2 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl2);
 		ads->ds_ctl3 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl3);
 #else
+		ads->ds_ctl0 = AR5212DESC_CONST(ds0)->ds_ctl0 & AR_TxInterReq;
 		ads->ds_ctl2 = AR5212DESC_CONST(ds0)->ds_ctl2;
 		ads->ds_ctl3 = AR5212DESC_CONST(ds0)->ds_ctl3;
 #endif
@@ -809,7 +842,12 @@
 		/*
 		 * Intermediate descriptor in a multi-descriptor frame.
 		 */
-		ads->ds_ctl0 = 0;
+#ifdef AH_NEED_DESC_SWAP
+		ads->ds_ctl0 = __bswap32(AR5212DESC_CONST(ds0)->ds_ctl0)
+		    & AR_TxInterReq;
+#else
+		ads->ds_ctl0 = AR5212DESC_CONST(ds0)->ds_ctl0 & AR_TxInterReq;
+#endif
 		ads->ds_ctl1 = segLen | AR_More;
 		ads->ds_ctl2 = 0;
 		ads->ds_ctl3 = 0;
@@ -939,3 +977,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;
+}

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212desc.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212desc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212desc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212desc.h 243168 2012-11-17 02:00:33Z adrian $
  */
 #ifndef _ATH_AR5212_DESC_H_
 #define _ATH_AR5212_DESC_H_
@@ -22,7 +23,6 @@
 /*
  * Hardware-specific descriptor structures.
  */
-#include "ah_desc.h"
 
 /*
  * AR5212-specific tx/rx descriptor definition.

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212phy.h 239801 2012-08-29 03:58:13Z adrian $
  */
 #ifndef _DEV_ATH_AR5212PHY_H_
 #define _DEV_ATH_AR5212PHY_H_
@@ -221,6 +222,19 @@
 #define AR_PHY_RADAR_0_FIRPWR	0x7F000000	/* Radar firpwr threshold */
 #define AR_PHY_RADAR_0_FIRPWR_S	24
 
+/* ar5413 specific */
+#define	AR_PHY_RADAR_2		0x9958		/* radar detection settings */
+#define	AR_PHY_RADAR_2_ENRELSTEPCHK 0x00002000	/* Enable using max rssi */
+#define	AR_PHY_RADAR_2_ENMAXRSSI    0x00004000	/* Enable using max rssi */
+#define	AR_PHY_RADAR_2_BLOCKOFDMWEAK 0x00008000	/* En block OFDM weak sig as radar */
+#define	AR_PHY_RADAR_2_USEFIR128    0x00400000	/* En measuring pwr over 128 cycles */
+#define	AR_PHY_RADAR_2_ENRELPWRCHK  0x00800000	/* Enable using max rssi */
+#define	AR_PHY_RADAR_2_MAXLEN	0x000000FF	/* Max Pulse duration threshold */
+#define	AR_PHY_RADAR_2_MAXLEN_S	0
+#define	AR_PHY_RADAR_2_RELSTEP	0x00001F00	/* Pulse relative step threshold */
+#define	AR_PHY_RADAR_2_RELSTEP_S	8
+#define	AR_PHY_RADAR_2_RELPWR	0x003F0000	/* pulse relative power threshold */
+#define	AR_PHY_RADAR_2_RELPWR_S	16
 
 #define	AR_PHY_SIGMA_DELTA	0x996C      /* AR5312 only */
 #define	AR_PHY_SIGMA_DELTA_ADC_SEL	0x00000003

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5212reg.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5212reg.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5212reg.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5212reg.h 226489 2011-10-18 03:17:06Z adrian $
  */
 #ifndef _DEV_ATH_AR5212REG_H_
 #define _DEV_ATH_AR5212REG_H_
@@ -94,7 +95,9 @@
 #define	AR_QTXDP(_i)	(AR_Q0_TXDP + ((_i)<<2))
 
 #define	AR_Q_TXE	0x0840	/* MAC Transmit Queue enable */
+#define	AR_Q_TXE_M	0x000003FF	/* Mask for TXE (QCU 0-9) */
 #define	AR_Q_TXD	0x0880	/* MAC Transmit Queue disable */
+#define	AR_Q_TXD_M	0x000003FF	/* Mask for TXD (QCU 0-9) */
 
 #define	AR_Q0_CBRCFG	0x08c0	/* MAC CBR configuration */
 #define	AR_Q1_CBRCFG	0x08c4	/* MAC CBR configuration */

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5311reg.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5311reg.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5311reg.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5311reg.h 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #ifndef _DEV_ATH_AR5311REG_H_
 #define _DEV_ATH_AR5311REG_H_

Modified: trunk/sys/dev/ath/ath_hal/ar5212/ar5413.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5212/ar5413.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5212/ar5413.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5212/ar5413.c 188979 2009-02-24 01:07:06Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312.h 188974 2009-02-24 00:12:16Z sam $
  */
 #ifndef _ATH_AR5312_H_
 #define _ATH_AR5312_H_

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c 225883 2011-09-30 05:17:57Z adrian $
  */
 #include "opt_ah.h"
 
@@ -71,13 +72,13 @@
 	uint16_t eeval;
 	HAL_STATUS ecode;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 		 __func__, sc, st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
 	ahp = ath_hal_malloc(sizeof (struct ath_hal_5212));
 	if (ahp == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		*status = HAL_ENOMEM;
 		return AH_NULL;

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312_eeprom.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312_eeprom.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312_eeprom.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312_eeprom.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312_gpio.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312_gpio.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312_gpio.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312_gpio.c 188974 2009-02-24 00:12:16Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312_interrupts.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312_interrupts.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312_interrupts.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312_interrupts.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312_misc.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312_misc.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312_misc.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312_misc.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312_power.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312_power.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312_power.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312_power.c 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312_reset.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312_reset.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312_reset.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312_reset.c 234450 2012-04-19 03:26:21Z adrian $
  */
 #include "opt_ah.h"
 
@@ -740,8 +741,7 @@
         if ((resetMask & AR_RC_MAC) == 0) {
 		if (isBigEndian()) {
 			/*
-			 * Set CFG, little-endian for register
-			 * and descriptor accesses.
+			 * Set CFG, little-endian for descriptor accesses.
 			 */
 #ifdef AH_NEED_DESC_SWAP
 			mask = INIT_CONFIG_STATUS | AR_CFG_SWRD;

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312phy.h 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #ifndef _DEV_ATH_AR5312PHY_H_
 #define _DEV_ATH_AR5312PHY_H_

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5312reg.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5312reg.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5312reg.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5312reg.h 204644 2010-03-03 17:32:32Z rpaulo $
  */
 #ifndef _DEV_ATH_AR5312REG_H_
 #define _DEV_ATH_AR5312REG_H_

Modified: trunk/sys/dev/ath/ath_hal/ar5312/ar5315_gpio.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5312/ar5315_gpio.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5312/ar5315_gpio.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5312/ar5315_gpio.c 188974 2009-02-24 00:12:16Z sam $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar2133.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar2133.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar2133.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar2133.c 241195 2012-10-04 15:42:45Z adrian $
  */
 #include "opt_ah.h"
 
@@ -163,6 +164,33 @@
 			OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
  			txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN);
 		}
+	/*
+	 * Handle programming the RF synth for odd frequencies in the
+	 * 4.9->5GHz range.  This matches the programming from the
+	 * later model 802.11abg RF synths.
+	 *
+	 * This interoperates on the quarter rate channels with the
+	 * AR5112 and later RF synths.  Please note that the synthesiser
+	 * isn't able to completely accurately represent these frequencies
+	 * (as the resolution in this reference is 2.5MHz) and thus it will
+	 * be slightly "off centre."  This matches the same slightly
+	 * incorrect * centre frequency behaviour that the AR5112 and later
+	 * channel selection code has.
+	 *
+	 * This is disabled because it hasn't been tested for regulatory
+	 * compliance and neither have the NICs which would use it.
+	 * So if you enable this code, you must first ensure that you've
+	 * re-certified the NICs in question beforehand or you will be
+	 * violating your local regulatory rules and breaking the law.
+	 */
+#if 0
+	} else if (((freq % 5) == 2) && (freq <= 5435)) {
+		freq = freq - 2;
+		channelSel = ath_hal_reverseBits(
+		    (uint32_t) (((freq - 4800) * 10) / 25 + 1), 8);
+		/* XXX what about for Howl/Sowl? */
+		aModeRefSel = ath_hal_reverseBits(0, 2);
+#endif
 	} else if ((freq % 20) == 0 && freq >= 5120) {
 		channelSel = ath_hal_reverseBits(((freq - 4800) / 20 << 2), 8);
 		if (AR_SREV_HOWL(ah) || AR_SREV_SOWL_10_OR_LATER(ah))
@@ -179,7 +207,8 @@
 		channelSel = ath_hal_reverseBits((freq - 4800) / 5, 8);
 		aModeRefSel = ath_hal_reverseBits(1, 2);
 	} else {
-		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n",
+		HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
+		    "%s: invalid channel %u MHz\n",
 		    __func__, freq);
 		return AH_FALSE;
 	}
@@ -549,3 +578,11 @@
 
 	return AH_TRUE;
 }
+
+static HAL_BOOL
+ar2133Probe(struct ath_hal *ah)
+{
+	return (AR_SREV_OWL(ah) || AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah));
+}
+
+AH_RF(RF2133, ar2133Probe, ar2133RfAttach);

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416.h 249580 2013-04-17 07:31:53Z adrian $
  */
 #ifndef _ATH_AR5416_H_
 #define _ATH_AR5416_H_
@@ -107,16 +108,21 @@
 	void		(*ah_initPLL) (struct ath_hal *ah,
 			    const struct ieee80211_channel *chan);
 
+	/* bluetooth coexistence operations */
+	void		(*ah_btCoexSetDiversity)(struct ath_hal *ah);
+
 	u_int       	ah_globaltxtimeout;	/* global tx timeout */
 	u_int		ah_gpioMask;
 	int		ah_hangs;		/* h/w hangs state */
 	uint8_t		ah_keytype[AR5416_KEYTABLE_SIZE];
 	/*
-	 * Extension Channel Rx Clear State
+	 * Primary/Extension Channel Tx, Rx, Rx Clear State
 	 */
 	uint32_t	ah_cycleCount;
 	uint32_t	ah_ctlBusy;
 	uint32_t	ah_extBusy;
+	uint32_t	ah_rxBusy;
+	uint32_t	ah_txBusy;
 	uint32_t	ah_rx_chainmask;
 	uint32_t	ah_tx_chainmask;
 
@@ -127,9 +133,36 @@
 	struct ar5416NfLimits nf_2g;
 	struct ar5416NfLimits nf_5g;
 
+	/*
+	 * TX power configuration related structures
+	 */
 	int		initPDADC;
+	int		ah_ht40PowerIncForPdadc;
+	int16_t		ah_ratesArray[Ar5416RateSize];
 
 	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))
 
@@ -171,6 +204,22 @@
 		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 ar5416BTCoexAntennaDiversity(struct ath_hal *ah);
+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);
 
@@ -190,9 +239,13 @@
 extern	uint64_t ar5416GetTsf64(struct ath_hal *ah);
 extern	void ar5416SetTsf64(struct ath_hal *ah, uint64_t tsf64);
 extern	void ar5416ResetTsf(struct ath_hal *ah);
+extern	uint32_t ar5416GetCurRssi(struct ath_hal *ah);
 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	HAL_BOOL ar5416GetMibCycleCounts(struct ath_hal *ah,
+	    HAL_SURVEY_SAMPLE *hsample);
+extern	void ar5416SetChainMasks(struct ath_hal *ah, uint32_t, uint32_t);
 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);
@@ -201,12 +254,18 @@
 	    uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag);
 extern	HAL_STATUS ar5416GetCapability(struct ath_hal *ah,
 	    HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result);
+extern	HAL_BOOL ar5416SetCapability(struct ath_hal *ah,
+	    HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t val,
+	    HAL_STATUS *status);
 extern	HAL_BOOL ar5416GetDiagState(struct ath_hal *ah, int request,
 	    const void *args, uint32_t argsize,
 	    void **result, uint32_t *resultsize);
 extern	HAL_BOOL ar5416SetRifsDelay(struct ath_hal *ah,
 	    const struct ieee80211_channel *chan, HAL_BOOL enable);
+
 extern	void ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe);
+extern	HAL_BOOL ar5416GetDfsDefaultThresh(struct ath_hal *ah,
+	    HAL_PHYERR_PARAM *pe);
 extern	void ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe);
 extern	HAL_BOOL ar5416ProcessRadarEvent(struct ath_hal *ah,
 	    struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf,
@@ -213,6 +272,16 @@
 	    HAL_DFS_EVENT *event);
 extern	HAL_BOOL ar5416IsFastClockEnabled(struct ath_hal *ah);
 
+/* ar9280_spectral.c */
+extern	void ar5416ConfigureSpectralScan(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss);
+extern	void ar5416GetSpectralParams(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss);
+extern	HAL_BOOL ar5416IsSpectralActive(struct ath_hal *ah);
+extern	HAL_BOOL ar5416IsSpectralEnabled(struct ath_hal *ah);
+extern	void ar5416StartSpectralScan(struct ath_hal *ah);
+extern	void ar5416StopSpectralScan(struct ath_hal *ah);
+extern	uint32_t ar5416GetSpectralConfig(struct ath_hal *ah);
+extern	void ar5416RestoreSpectralConfig(struct ath_hal *ah, uint32_t restoreval);
+
 extern	HAL_BOOL ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode,
 		int setChip);
 extern	HAL_POWER_MODE ar5416GetPowerMode(struct ath_hal *ah);
@@ -224,6 +293,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 *,
@@ -313,7 +383,8 @@
 		u_int txRate2, u_int txRetries2,
 		u_int txRate3, u_int txRetries3);
 extern	HAL_BOOL ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds,
-		u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
+		HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList,
+		u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
 		const struct ath_desc *ds0);
 extern	HAL_STATUS ar5416ProcTxDesc(struct ath_hal *ah,
 		struct ath_desc *, struct ath_tx_status *);
@@ -325,9 +396,10 @@
 	        const HAL_TXQ_INFO *qInfo);
 
 extern	HAL_BOOL ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds,
+		HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList,
 		u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx,
-		HAL_CIPHER cipher, uint8_t delims, u_int segLen, HAL_BOOL firstSeg,
-		HAL_BOOL lastSeg);
+		HAL_CIPHER cipher, uint8_t delims,
+		HAL_BOOL firstSeg, HAL_BOOL lastSeg, HAL_BOOL lastAggr);
 extern	HAL_BOOL ar5416SetupFirstTxDesc(struct ath_hal *ah, struct ath_desc *ds,
 		u_int aggrLen, u_int flags, u_int txPower, u_int txRate0, u_int txTries0,
 		u_int antMode, u_int rtsctsRate, u_int rtsctsDuration);
@@ -338,8 +410,15 @@
 extern	void ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds,
 		u_int durUpdateEn, u_int rtsctsRate, HAL_11N_RATE_SERIES series[],
 		u_int nseries, u_int flags);
+
+extern void ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds,
+		u_int aggrLen, u_int numDelims);
 extern	void ar5416Set11nAggrMiddle(struct ath_hal *ah, struct ath_desc *ds, u_int numDelims);
+extern void ar5416Set11nAggrLast(struct ath_hal *ah, struct ath_desc *ds);
 extern	void ar5416Clr11nAggr(struct ath_hal *ah, struct ath_desc *ds);
+extern	void ar5416Set11nVirtualMoreFrag(struct ath_hal *ah,
+		struct ath_desc *ds, u_int vmf);
+
 extern	void ar5416Set11nBurstDuration(struct ath_hal *ah, struct ath_desc *ds, u_int burstDuration);
 
 extern	const HAL_RATE_TABLE *ar5416GetRateTable(struct ath_hal *, u_int mode);

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416.ini 225420 2011-09-06 10:49:05Z adrian $
  */
 /* Auto Generated PCI Register Writes.  Created: 09/20/06 */
 


Property changes on: trunk/sys/dev/ath/ath_hal/ar5416/ar5416.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c 240984 2012-09-27 06:05:54Z adrian $
  */
 #include "opt_ah.h"
 
@@ -227,7 +228,7 @@
 		u_int level = param;
 
 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level);
-		if (level >= params->maxNoiseImmunityLevel) {
+		if (level > params->maxNoiseImmunityLevel) {
 			HALDEBUG(ah, HAL_DEBUG_ANI,
 			    "%s: immunity level out of range (%u > %u)\n",
 			    __func__, level, params->maxNoiseImmunityLevel);
@@ -314,7 +315,7 @@
 		u_int level = param;
 
 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level);
-		if (level >= params->maxFirstepLevel) {
+		if (level > params->maxFirstepLevel) {
 			HALDEBUG(ah, HAL_DEBUG_ANI,
 			    "%s: firstep level out of range (%u > %u)\n",
 			    __func__, level, params->maxFirstepLevel);
@@ -333,7 +334,7 @@
 		u_int level = param;
 
 		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level);
-		if (level >= params->maxSpurImmunityLevel) {
+		if (level > params->maxSpurImmunityLevel) {
 			HALDEBUG(ah, HAL_DEBUG_ANI,
 			    "%s: spur immunity level out of range (%u > %u)\n",
 			    __func__, level, params->maxSpurImmunityLevel);
@@ -379,20 +380,30 @@
 	aniState = ahp->ah_curani;
 	params = aniState->params;
 	/* First, raise noise immunity level, up to max */
-	if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL)) &&
-	    (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) {
-		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 
-				 aniState->noiseImmunityLevel + 1);
-		return;
+	if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) {
+		if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 
+				 aniState->noiseImmunityLevel + 1))
+			return;
 	}
 	/* then, raise spur immunity level, up to max */
-	if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_SPUR_IMMUNITY_LEVEL)) &&
-	    (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel)) {
-		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
-				 aniState->spurImmunityLevel + 1);
-		return;
+	if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) {
+		if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
+				 aniState->spurImmunityLevel + 1))
+			return;
 	}
 
+	/*
+	 * In the case of AP mode operation, we cannot bucketize beacons
+	 * according to RSSI.  Instead, raise Firstep level, up to max, and
+	 * simply return.
+	 */
+	if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {
+		if (aniState->firstepLevel < params->maxFirstepLevel) {
+			if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
+			    aniState->firstepLevel + 1))
+				return;
+		}
+	}
 	if (ANI_ENA_RSSI(ah)) {
 		int32_t rssi = BEACON_RSSI(ahp);
 		if (rssi > params->rssiThrHigh) {
@@ -412,10 +423,10 @@
 			 * If weak sig detect is already off, as last resort,
 			 * raise firstep level 
 			 */
-			if (aniState->firstepLevel+1 < params->maxFirstepLevel) {
-				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
-						 aniState->firstepLevel + 1);
-				return;
+			if (aniState->firstepLevel < params->maxFirstepLevel) {
+				if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
+						 aniState->firstepLevel + 1))
+					return;
 			}
 		} else if (rssi > params->rssiThrLow) {
 			/* 
@@ -426,10 +437,10 @@
 				ar5416AniControl(ah,
 				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 				    AH_TRUE);
-			if (aniState->firstepLevel+1 < params->maxFirstepLevel)
-				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
-				     aniState->firstepLevel + 1);
-			return;
+			if (aniState->firstepLevel < params->maxFirstepLevel)
+				if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
+				     aniState->firstepLevel + 1))
+				return;
 		} else {
 			/* 
 			 * Beacon rssi is low, if in 11b/g mode, turn off ofdm
@@ -442,9 +453,9 @@
 					    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
 					    AH_FALSE);
 				if (aniState->firstepLevel > 0)
-					ar5416AniControl(ah,
-					     HAL_ANI_FIRSTEP_LEVEL, 0);
-				return;
+					if (ar5416AniControl(ah,
+					     HAL_ANI_FIRSTEP_LEVEL, 0))
+						return;
 			}
 		}
 	}
@@ -480,7 +491,7 @@
 			 * Beacon signal in mid and high range,
 			 * raise firstep level.
 			 */
-			if (aniState->firstepLevel+1 < params->maxFirstepLevel)
+			if (aniState->firstepLevel < params->maxFirstepLevel)
 				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
 						 aniState->firstepLevel + 1);
 		} else {
@@ -577,6 +588,16 @@
 		goto finish;
 	}
 
+	/*
+	 * Use a restrictive set of ANI parameters for hostap mode.
+	 */
+	if (opmode == HAL_M_HOSTAP) {
+		if (IEEE80211_IS_CHAN_2GHZ(chan))
+			AH5416(ah)->ah_ani_function =
+			    HAL_ANI_SPUR_IMMUNITY_LEVEL | HAL_ANI_FIRSTEP_LEVEL;
+		else
+			AH5416(ah)->ah_ani_function = 0;
+	}
 
 	/*
 	 * Automatic processing is done only in station mode right now.
@@ -606,7 +627,7 @@
 		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0);
 		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
 		ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
-			AH_TRUE);
+			AH_FALSE);
 		ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE);
 		ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0);
 		ichan->privFlags |= CHANNEL_ANI_SETUP;
@@ -710,6 +731,19 @@
 
 	aniState = ahp->ah_curani;
 	params = aniState->params;
+
+	/*
+	 * In the case of AP mode operation, we cannot bucketize beacons
+	 * according to RSSI.  Instead, lower Firstep level, down to min, and
+	 * simply return.
+	 */
+	if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {
+		if (aniState->firstepLevel > 0) {
+			if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
+			    aniState->firstepLevel - 1))
+				return;
+		}
+	}
 	if (ANI_ENA_RSSI(ah)) {
 		int32_t rssi = BEACON_RSSI(ahp);
 		if (rssi > params->rssiThrHigh) {
@@ -724,15 +758,15 @@
 			 * detection or lower firstep level.
 			 */
 			if (aniState->ofdmWeakSigDetectOff) {
-				ar5416AniControl(ah,
+				if (ar5416AniControl(ah,
 				    HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
-				    AH_TRUE);
-				return;
+				    AH_TRUE))
+					return;
 			}
 			if (aniState->firstepLevel > 0) {
-				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
-						 aniState->firstepLevel - 1);
-				return;
+				if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
+						 aniState->firstepLevel - 1))
+					return;
 			}
 		} else {
 			/*
@@ -739,17 +773,17 @@
 			 * Beacon rssi is low, reduce firstep level.
 			 */
 			if (aniState->firstepLevel > 0) {
-				ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
-						 aniState->firstepLevel - 1);
-				return;
+				if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
+						 aniState->firstepLevel - 1))
+					return;
 			}
 		}
 	}
 	/* then lower spur immunity level, down to zero */
 	if (aniState->spurImmunityLevel > 0) {
-		ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
-				 aniState->spurImmunityLevel - 1);
-		return;
+		if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
+				 aniState->spurImmunityLevel - 1))
+			return;
 	}
 	/* 
 	 * if all else fails, lower noise immunity level down to a min value
@@ -756,9 +790,9 @@
 	 * zero for now
 	 */
 	if (aniState->noiseImmunityLevel > 0) {
-		ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
-				 aniState->noiseImmunityLevel - 1);
-		return;
+		if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
+				 aniState->noiseImmunityLevel - 1))
+			return;
 	}
 }
 
@@ -771,21 +805,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 txFrameCount, rxFrameCount, cycleCount;
-	int32_t listenTime;
+	struct ar5212AniState *aniState = NULL;
+	int32_t listenTime = 0;
+	int good;
+	HAL_SURVEY_SAMPLE hs;
+	HAL_CHANNEL_SURVEY *cs = AH_NULL;
 
-	txFrameCount = OS_REG_READ(ah, AR_TFCNT);
-	rxFrameCount = OS_REG_READ(ah, AR_RFCNT);
-	cycleCount = OS_REG_READ(ah, AR_CCCNT);
+	/*
+	 * 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 (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
+	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
@@ -793,15 +856,29 @@
 		 */
 		listenTime = 0;
 		ahp->ah_stats.ast_ani_lzero++;
-	} else {
-		int32_t ccdelta = cycleCount - aniState->cycleCount;
-		int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
-		int32_t tfdelta = txFrameCount - 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 = cycleCount;
-	aniState->txFrameCount = txFrameCount;
-	aniState->rxFrameCount = rxFrameCount;
+
+	/*
+	 * Again, only update ANI state if we have it.
+	 */
+	if (ANI_ENA(ah)) {
+		aniState->cycleCount = AH5416(ah)->ah_cycleCount;
+		aniState->rxFrameCount = AH5416(ah)->ah_rxBusy;
+		aniState->txFrameCount = AH5416(ah)->ah_txBusy;
+	}
+
 	return listenTime;
 }
 
@@ -865,16 +942,21 @@
 	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;
+
 	if (!ANI_ENA(ah))
 		return;
 
-	listenTime = ar5416AniGetListenTime(ah);
 	if (listenTime < 0) {
 		ahp->ah_stats.ast_ani_lneg++;
 		/* restart ANI period if listenTime is invalid */
+		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid listenTime\n",
+		    __func__);
 		ar5416AniRestart(ah, aniState);
 	}
 	/* XXX beware of overflow? */
@@ -894,6 +976,8 @@
 		    aniState->cckPhyErrCount <= aniState->listenTime *
 		    params->cckTrigLow/1000)
 			ar5416AniLowerImmunity(ah);
+		HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower immunity\n",
+		    __func__);
 		ar5416AniRestart(ah, aniState);
 	} else if (aniState->listenTime > params->period) {
 		updateMIBStats(ah, aniState);
@@ -909,7 +993,7 @@
 			   params->cckTrigHigh / 1000) {
                         HALDEBUG(ah, HAL_DEBUG_ANI,
                             "%s: CCK err %u listenTime %u\n", __func__,
-                            aniState->ofdmPhyErrCount, aniState->listenTime);
+                            aniState->cckPhyErrCount, aniState->listenTime);
 			ar5416AniCckErrTrigger(ah);
 			ar5416AniRestart(ah, aniState);
 		}

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c 305615 2016-09-08 15:06:28Z pfg $
  */
 #include "opt_ah.h"
 
@@ -30,7 +31,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,
@@ -45,8 +48,8 @@
 		.coarseHigh		= { -14, -14, -14, -14, -12 },
 		.coarseLow		= { -64, -64, -64, -64, -70 },
 		.firpwr			= { -78, -78, -78, -78, -80 },
-		.maxSpurImmunityLevel	= 2,
-		.cycPwrThr1		= { 2, 4, 6 },
+		.maxSpurImmunityLevel	= 7,
+		.cycPwrThr1		= { 2, 4, 6, 8, 10, 12, 14, 16 },
 		.maxFirstepLevel	= 2,	/* levels 0..2 */
 		.firstep		= { 0, 4, 8 },
 		.ofdmTrigHigh		= 500,
@@ -99,9 +102,10 @@
 	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,
+	ah->ah_perCalibrationN		= ar5416PerCalibrationN;
+	ah->ah_resetCalValid		= ar5416ResetCalValid;
 	ah->ah_setTxPowerLimit		= ar5416SetTxPowerLimit;
 	ah->ah_setTxPower		= ar5416SetTransmitPower;
 	ah->ah_setBoardValues		= ar5416SetBoardValues;
@@ -119,6 +123,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;
@@ -129,6 +134,7 @@
 
 	/* Misc Functions */
 	ah->ah_getCapability		= ar5416GetCapability;
+	ah->ah_setCapability		= ar5416SetCapability;
 	ah->ah_getDiagState		= ar5416GetDiagState;
 	ah->ah_setLedState		= ar5416SetLedState;
 	ah->ah_gpioCfgOutput		= ar5416GpioCfgOutput;
@@ -137,6 +143,7 @@
 	ah->ah_gpioSet			= ar5416GpioSet;
 	ah->ah_gpioSetIntr		= ar5416GpioSetIntr;
 	ah->ah_getTsf64			= ar5416GetTsf64;
+	ah->ah_setTsf64			= ar5416SetTsf64;
 	ah->ah_resetTsf			= ar5416ResetTsf;
 	ah->ah_getRfGain		= ar5416GetRfgain;
 	ah->ah_setAntennaSwitch		= ar5416SetAntennaSwitch;
@@ -143,6 +150,8 @@
 	ah->ah_setDecompMask		= ar5416SetDecompMask;
 	ah->ah_setCoverageClass		= ar5416SetCoverageClass;
 	ah->ah_setQuiet			= ar5416SetQuiet;
+	ah->ah_getMibCycleCounts	= ar5416GetMibCycleCounts;
+	ah->ah_setChainMasks		= ar5416SetChainMasks;
 
 	ah->ah_resetKeyCacheEntry	= ar5416ResetKeyCacheEntry;
 	ah->ah_setKeyCacheEntry		= ar5416SetKeyCacheEntry;
@@ -150,9 +159,18 @@
 	/* DFS Functions */
 	ah->ah_enableDfs		= ar5416EnableDfs;
 	ah->ah_getDfsThresh		= ar5416GetDfsThresh;
+	ah->ah_getDfsDefaultThresh	= ar5416GetDfsDefaultThresh;
 	ah->ah_procRadarEvent		= ar5416ProcessRadarEvent;
 	ah->ah_isFastClockEnabled	= ar5416IsFastClockEnabled;
 
+	/* Spectral Scan Functions */
+	ah->ah_spectralConfigure	= ar5416ConfigureSpectralScan;
+	ah->ah_spectralGetConfig	= ar5416GetSpectralParams;
+	ah->ah_spectralStart		= ar5416StartSpectralScan;
+	ah->ah_spectralStop		= ar5416StopSpectralScan;
+	ah->ah_spectralIsEnabled	= ar5416IsSpectralEnabled;
+	ah->ah_spectralIsActive		= ar5416IsSpectralActive;
+
 	/* Power Management Functions */
 	ah->ah_setPowerMode		= ar5416SetPowerMode;
 
@@ -168,7 +186,9 @@
 	ah->ah_setupFirstTxDesc		= ar5416SetupFirstTxDesc;
 	ah->ah_setupLastTxDesc		= ar5416SetupLastTxDesc;
 	ah->ah_set11nRateScenario	= ar5416Set11nRateScenario;
+	ah->ah_set11nAggrFirst		= ar5416Set11nAggrFirst;
 	ah->ah_set11nAggrMiddle		= ar5416Set11nAggrMiddle;
+	ah->ah_set11nAggrLast		= ar5416Set11nAggrLast;
 	ah->ah_clr11nAggr		= ar5416Clr11nAggr;
 	ah->ah_set11nBurstDuration	= ar5416Set11nBurstDuration;
 	ah->ah_get11nExtBusy		= ar5416Get11nExtBusy;
@@ -175,6 +195,7 @@
 	ah->ah_set11nMac2040		= ar5416Set11nMac2040;
 	ah->ah_get11nRxClear		= ar5416Get11nRxClear;
 	ah->ah_set11nRxClear		= ar5416Set11nRxClear;
+	ah->ah_set11nVirtMoreFrag	= ar5416Set11nVirtualMoreFrag;
 
 	/* Interrupt functions */
 	ah->ah_isInterruptPending	= ar5416IsInterruptPending;
@@ -181,6 +202,17 @@
 	ah->ah_getPendingInterrupts	= ar5416GetPendingInterrupts;
 	ah->ah_setInterrupts		= ar5416SetInterrupts;
 
+	/* Bluetooth Coexistence functions */
+	ah->ah_btCoexSetInfo		= ar5416SetBTCoexInfo;
+	ah->ah_btCoexSetConfig		= ar5416BTCoexConfig;
+	ah->ah_btCoexSetQcuThresh	= ar5416BTCoexSetQcuThresh;
+	ah->ah_btCoexSetWeights		= ar5416BTCoexSetWeights;
+	ah->ah_btCoexSetBmissThresh	= ar5416BTCoexSetupBmissThresh;
+	ah->ah_btCoexSetParameter	= ar5416BTCoexSetParameter;
+	ah->ah_btCoexDisable		= ar5416BTCoexDisable;
+	ah->ah_btCoexEnable		= ar5416BTCoexEnable;
+	AH5416(ah)->ah_btCoexSetDiversity = ar5416BTCoexAntennaDiversity;
+
 	ahp->ah_priv.ah_getWirelessModes= ar5416GetWirelessModes;
 	ahp->ah_priv.ah_eepromRead	= ar5416EepromRead;
 #ifdef AH_SUPPORT_WRITE_EEPROM
@@ -212,8 +244,37 @@
 	/* Enable all ANI functions to begin with */
 	AH5416(ah)->ah_ani_function = 0xffffffff;
 
-        /* Set overridable ANI methods */
-        AH5212(ah)->ah_aniControl = ar5416AniControl;
+	/* Set overridable ANI methods */
+	AH5212(ah)->ah_aniControl = ar5416AniControl;
+
+	/*
+	 * Default FIFO Trigger levels
+	 *
+	 * These define how filled the TX FIFO needs to be before
+	 * the baseband begins to be given some data.
+	 *
+	 * To be paranoid, we ensure that the TX trigger level always
+	 * has at least enough space for two TX DMA to occur.
+	 * The TX DMA size is currently hard-coded to AR_TXCFG_DMASZ_128B.
+	 * That means we need to leave at least 256 bytes available in
+	 * the TX DMA FIFO.
+	 */
+#define	AR_FTRIG_512B	0x00000080 // 5 bits total
+	/*
+	 * AR9285/AR9271 have half the size TX FIFO compared to
+	 * other devices
+	 */
+	if (AR_SREV_KITE(ah) || AR_SREV_9271(ah)) {
+		AH5212(ah)->ah_txTrigLev = (AR_FTRIG_256B >> AR_FTRIG_S);
+		AH5212(ah)->ah_maxTxTrigLev = ((2048 / 64) - 1);
+	} else {
+		AH5212(ah)->ah_txTrigLev = (AR_FTRIG_512B >> AR_FTRIG_S);
+		AH5212(ah)->ah_maxTxTrigLev = ((4096 / 64) - 1);
+	}
+#undef	AR_FTRIG_512B
+
+	/* And now leave some headspace - 256 bytes */
+	AH5212(ah)->ah_maxTxTrigLev -= 4;
 }
 
 uint32_t
@@ -246,7 +307,7 @@
 	HAL_STATUS ecode;
 	HAL_BOOL rfStatus;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 	    __func__, sc, (void*) st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
@@ -255,7 +316,7 @@
 		sizeof(ar5416Addac)
 	);
 	if (ahp5416 == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		*status = HAL_ENOMEM;
 		return AH_NULL;
@@ -337,7 +398,7 @@
 	OS_REG_WRITE(ah, AR_PHY(0), 0x00000007);
 
 	/* Read Radio Chip Rev Extract */
-	AH_PRIVATE(ah)->ah_analog5GhzRev = ar5212GetRadioRev(ah);
+	AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah);
 	switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) {
         case AR_RAD5122_SREV_MAJOR:	/* Fowl: 5G/2x2 */
         case AR_RAD2122_SREV_MAJOR:	/* Fowl: 2+5G/2x2 */
@@ -452,23 +513,74 @@
 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)
 {
 	u_int modesIndex, freqIndex;
@@ -578,6 +690,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 |
@@ -762,6 +876,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);
 }
 
 /*
@@ -821,7 +938,13 @@
 
 	pCap->halCompressSupport = AH_FALSE;
 	pCap->halBurstSupport = AH_TRUE;
-	pCap->halFastFramesSupport = AH_FALSE;	/* XXX? */
+	/*
+	 * This is disabled for now; the net80211 layer needs to be
+	 * taught when it is and isn't appropriate to enable FF processing
+	 * with 802.11n NICs (it tries to enable both A-MPDU and
+	 * fast frames, with very tragic crash-y results.)
+	 */
+	pCap->halFastFramesSupport = AH_FALSE;
 	pCap->halChapTuningSupport = AH_TRUE;
 	pCap->halTurboPrimeSupport = AH_TRUE;
 
@@ -828,11 +951,14 @@
 	pCap->halTurboGSupport = pCap->halWirelessModes & HAL_MODE_108G;
 
 	pCap->halPSPollBroken = AH_TRUE;	/* XXX fixed in later revs? */
+	pCap->halNumMRRetries = 4;		/* Hardware supports 4 MRR */
+	pCap->halNumTxMaps = 1;			/* Single TX ptr per descr */
 	pCap->halVEOLSupport = AH_TRUE;
 	pCap->halBssIdMaskSupport = AH_TRUE;
 	pCap->halMcastKeySrchSupport = AH_TRUE;	/* Works on AR5416 and later */
 	pCap->halTsfAddSupport = AH_TRUE;
 	pCap->hal4AddrAggrSupport = AH_FALSE;	/* Broken in Owl */
+	pCap->halSpectralScanSupport = AH_FALSE;	/* AR9280 and later */
 
 	if (ath_hal_eepromGet(ah, AR_EEP_MAXQCU, &val) == HAL_OK)
 		pCap->halTotalQueues = val;
@@ -844,9 +970,9 @@
 	else
 		pCap->halKeyCacheSize = AR5416_KEYTABLE_SIZE;
 
-	/* XXX not needed */
-	pCap->halChanHalfRate = AH_FALSE;	/* XXX ? */
-	pCap->halChanQuarterRate = AH_FALSE;	/* XXX ? */
+	/* XXX Which chips? */
+	pCap->halChanHalfRate = AH_TRUE;
+	pCap->halChanQuarterRate = AH_TRUE;
 
 	pCap->halTstampPrecision = 32;
 	pCap->halHwPhyCounterSupport = AH_TRUE;
@@ -863,7 +989,7 @@
 			;
 
 	pCap->halFastCCSupport = AH_TRUE;
-	pCap->halNumGpioPins = 6;
+	pCap->halNumGpioPins = 14;
 	pCap->halWowSupport = AH_FALSE;
 	pCap->halWowMatchPatternExact = AH_FALSE;
 	pCap->halBtCoexSupport = AH_FALSE;	/* XXX need support */
@@ -882,6 +1008,16 @@
 	/* AR5416 may have 3 antennas but is a 2x2 stream device */
 	pCap->halTxStreams = 2;
 	pCap->halRxStreams = 2;
+
+	/*
+	 * If the TX or RX chainmask has less than 2 chains active,
+	 * mark it as a 1-stream device for the relevant stream.
+	 */
+	if (owl_get_ntxchains(pCap->halTxChainMask) == 1)
+		pCap->halTxStreams = 1;
+	/* XXX Eww */
+	if (owl_get_ntxchains(pCap->halRxChainMask) == 1)
+		pCap->halRxStreams = 1;
 	pCap->halRtsAggrLimit = 8*1024;		/* Owl 2.0 limit */
 	pCap->halMbssidAggrSupport = AH_FALSE;	/* Broken on Owl */
 	pCap->halForcePpmSupport = AH_TRUE;
@@ -892,6 +1028,12 @@
 	pCap->halEnhancedDfsSupport = AH_FALSE;
 	/* Hardware supports 32 bit TSF values in the RX descriptor */
 	pCap->halHasLongRxDescTsf = AH_TRUE;
+	/*
+	 * BB Read WAR: this is only for AR5008/AR9001 NICs
+	 * It is also set individually in the AR91xx attach functions.
+	 */
+	if (AR_SREV_OWL(ah))
+		pCap->halHasBBReadWar = AH_TRUE;
 
 	if (ath_hal_eepromGetFlag(ah, AR_EEP_RFKILL) &&
 	    ath_hal_eepromGet(ah, AR_EEP_RFSILENT, &ahpriv->ah_rfsilent) == HAL_OK) {
@@ -900,8 +1042,24 @@
 		pCap->halRfSilentSupport = AH_TRUE;
 	}
 
+	/*
+	 * The MAC will mark frames as RXed if there's a descriptor
+	 * to write them to. So if it hits a self-linked final descriptor,
+	 * it'll keep ACKing frames even though they're being silently
+	 * dropped. Thus, this particular feature of the driver can't
+	 * be used for 802.11n devices.
+	 */
 	ahpriv->ah_rxornIsFatal = AH_FALSE;
 
+	/*
+	 * If it's a PCI NIC, ask the HAL OS layer to serialise
+	 * register access, or SMP machines may cause the hardware
+	 * to hang. This is applicable to AR5416 and AR9220; I'm not
+	 * sure about AR9160 or AR9227.
+	 */
+	if (! AH_PRIVATE(ah)->ah_ispcie)
+		pCap->halSerialiseRegWar = 1;
+
 	return AH_TRUE;
 }
 
@@ -908,9 +1066,12 @@
 static const char*
 ar5416Probe(uint16_t vendorid, uint16_t devid)
 {
-	if (vendorid == ATHEROS_VENDOR_ID &&
-	    (devid == AR5416_DEVID_PCI || devid == AR5416_DEVID_PCIE))
-		return "Atheros 5416";
+	if (vendorid == ATHEROS_VENDOR_ID) {
+		if (devid == AR5416_DEVID_PCI)
+			return "Atheros 5416";
+		if (devid == AR5416_DEVID_PCIE)
+			return "Atheros 5418";
+	}
 	return AH_NULL;
 }
 AH_CHIP(AR5416, ar5416Probe, ar5416Attach);

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c 225819 2011-09-28 03:03:23Z adrian $
  */
 #include "opt_ah.h"
 
@@ -46,6 +47,7 @@
 ar5416SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt)
 {
 	uint32_t bperiod;
+	struct ath_hal_5212 *ahp = AH5212(ah);
 
 	OS_REG_WRITE(ah, AR_NEXT_TBTT, TU_TO_USEC(bt->bt_nexttbtt));
 	OS_REG_WRITE(ah, AR_NEXT_DBA, ONE_EIGHTH_TU_TO_USEC(bt->bt_nextdba));
@@ -53,6 +55,7 @@
 	OS_REG_WRITE(ah, AR_NEXT_NDP, TU_TO_USEC(bt->bt_nextatim));
 
 	bperiod = TU_TO_USEC(bt->bt_intval & HAL_BEACON_PERIOD);
+	ahp->ah_beaconInterval = bt->bt_intval & HAL_BEACON_PERIOD;
 	OS_REG_WRITE(ah, AR5416_BEACON_PERIOD, bperiod);
 	OS_REG_WRITE(ah, AR_DBA_PERIOD, bperiod);
 	OS_REG_WRITE(ah, AR_SWBA_PERIOD, bperiod);

Added: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c	                        (rev 0)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,395 @@
+/* $MidnightBSD$ */
+/*
+ * 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: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c 251483 2013-06-07 05:17:58Z 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.
+ */
+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);
+
+	/* Added Select GPIO5~8 instaed SPI */
+	if (AR_SREV_9271(ah)) {
+		uint32_t val;
+
+		val = OS_REG_READ(ah, AR9271_CLOCK_CONTROL);
+		val &= 0xFFFFFEFF;
+		OS_REG_WRITE(ah, AR9271_CLOCK_CONTROL, val);
+	}
+
+	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);
+
+	HALDEBUG(ah, HAL_DEBUG_BT_COEX,
+	    "%s: called; configType=%d\n",
+	    __func__,
+	    ahp->ah_btCoexConfigType);
+
+	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);
+
+		/*
+		 * Configure the antenna diversity setup.
+		 * It's a no-op for AR9287; AR9285 overrides this
+		 * as required.
+		 */
+		AH5416(ah)->ah_btCoexSetDiversity(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);
+		}
+	}
+}


Property changes on: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h	                        (rev 0)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,32 @@
+/* $MidnightBSD$ */
+/*
+ * 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: stable/10/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__ */


Property changes on: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_btcoex.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c 227411 2011-11-09 23:28:47Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_cal.h 219480 2011-03-11 11:35:36Z adrian $
  */
 #ifndef _ATH_AR5416_CAL_H_
 #define _ATH_AR5416_CAL_H_

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c 203158 2010-01-29 10:07:17Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c 203158 2010-01-29 10:07:17Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_cal_iq.c 211306 2010-08-14 15:28:15Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_eeprom.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_eeprom.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_eeprom.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_eeprom.c 203158 2010-01-29 10:07:17Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c 237611 2012-06-26 22:16:53Z adrian $
  */
 #include "opt_ah.h"
 
@@ -35,8 +36,11 @@
 cfgOutputMux(struct ath_hal *ah, uint32_t gpio, uint32_t type)
 {
 	int addr;
-	uint32_t gpio_shift, reg;
+	uint32_t gpio_shift, tmp;
 
+	HALDEBUG(ah, HAL_DEBUG_GPIO, "%s: gpio=%d, type=%d\n",
+	    __func__, gpio, type);
+
 	/* each MUX controls 6 GPIO pins */
 	if (gpio > 11)
 		addr = AR_GPIO_OUTPUT_MUX3;
@@ -61,12 +65,17 @@
 	 * will never be used.  So it should be fine that bit 4 won't be
 	 * able to recover.
 	 */
-	reg = OS_REG_READ(ah, addr);
-	if (addr == AR_GPIO_OUTPUT_MUX1 && !AR_SREV_MERLIN_20_OR_LATER(ah))
-		reg = ((reg & 0x1F0) << 1) | (reg & ~0x1F0);
-	reg &= ~(0x1f << gpio_shift);
-	reg |= type << gpio_shift;
-	OS_REG_WRITE(ah, addr, reg);
+	if (AR_SREV_MERLIN_20_OR_LATER(ah) ||
+	    (addr != AR_GPIO_OUTPUT_MUX1)) {
+		OS_REG_RMW(ah, addr, (type << gpio_shift),
+		    (0x1f << gpio_shift));
+	} else {
+		tmp = OS_REG_READ(ah, addr);
+		tmp = ((tmp & 0x1F0) << 1) | (tmp & ~0x1F0);
+		tmp &= ~(0x1f << gpio_shift);
+		tmp |= type << gpio_shift;
+		OS_REG_WRITE(ah, addr, tmp);
+	}
 }
 
 /*
@@ -77,12 +86,42 @@
 {
 	uint32_t gpio_shift, reg;
 
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+
 	HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins);
 
-	/* NB: type maps directly to hardware */
-	cfgOutputMux(ah, gpio, type);
-	gpio_shift = gpio << 1;			/* 2 bits per output mode */
+	/*
+	 * 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);
+
+	/*
+	 * 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);
 	reg |= AR_GPIO_OE_OUT_DRV_ALL << gpio_shift;
@@ -89,6 +128,7 @@
 	OS_REG_WRITE(ah, AR_GPIO_OE_OUT, reg);
 
 	return AH_TRUE;
+#undef	N
 }
  
 /*
@@ -101,6 +141,8 @@
 
 	HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins);
 
+	HALDEBUG(ah, HAL_DEBUG_GPIO, "%s: gpio=%d\n", __func__, gpio);
+
 	/* TODO: configure input mux for AR5416 */
 	/* If configured as input, set output to tristate */
 	gpio_shift = gpio << 1;
@@ -122,6 +164,8 @@
 	uint32_t reg;
 
 	HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins);
+	HALDEBUG(ah, HAL_DEBUG_GPIO,
+	   "%s: gpio=%d, val=%d\n", __func__, gpio, val);
 
 	reg = OS_REG_READ(ah, AR_GPIO_IN_OUT);
 	if (val & 1)
@@ -146,6 +190,8 @@
 	 * Read output value for all gpio's, shift it,
 	 * and verify whether the specific bit is set.
 	 */
+	if (AR_SREV_KIWI_10_OR_LATER(ah))
+		bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9287_GPIO_IN_VAL);
 	if (AR_SREV_KITE_10_OR_LATER(ah))
 		bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9285_GPIO_IN_VAL);
 	else if (AR_SREV_MERLIN_10_OR_LATER(ah))
@@ -165,6 +211,8 @@
 	uint32_t val, mask;
 
 	HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins);
+	HALDEBUG(ah, HAL_DEBUG_GPIO,
+	    "%s: gpio=%d, ilevel=%d\n", __func__, gpio, ilevel);
 
 	if (ilevel == HAL_GPIO_INTR_DISABLE) {
 		val = MS(OS_REG_READ(ah, AR_INTR_ASYNC_ENABLE),

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c 247030 2013-02-20 11:24:11Z adrian $
  */
 #include "opt_ah.h"
 
@@ -67,9 +68,18 @@
 HAL_BOOL
 ar5416GetPendingInterrupts(struct ath_hal *ah, HAL_INT *masked)
 {
-	uint32_t isr, isr0, isr1, sync_cause = 0;
+	uint32_t isr, isr0, isr1, sync_cause = 0, o_sync_cause = 0;
+	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
 
+#ifdef	AH_INTERRUPT_DEBUGGING
 	/*
+	 * Blank the interrupt debugging area regardless.
+	 */
+	bzero(&ah->ah_intrstate, sizeof(ah->ah_intrstate));
+	ah->ah_syncstate = 0;
+#endif
+
+	/*
 	 * Verify there's a mac interrupt and the RTC is on.
 	 */
 	if (AR_SREV_HOWL(ah)) {
@@ -81,7 +91,10 @@
 			isr = OS_REG_READ(ah, AR_ISR);
 		else
 			isr = 0;
-		sync_cause = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
+#ifdef	AH_INTERRUPT_DEBUGGING
+		ah->ah_syncstate =
+#endif
+		o_sync_cause = sync_cause = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
 		sync_cause &= AR_INTR_SYNC_DEFAULT;
 		*masked = 0;
 
@@ -89,6 +102,16 @@
 			return AH_FALSE;
 	}
 
+#ifdef	AH_INTERRUPT_DEBUGGING
+	ah->ah_intrstate[0] = isr;
+	ah->ah_intrstate[1] = OS_REG_READ(ah, AR_ISR_S0);
+	ah->ah_intrstate[2] = OS_REG_READ(ah, AR_ISR_S1);
+	ah->ah_intrstate[3] = OS_REG_READ(ah, AR_ISR_S2);
+	ah->ah_intrstate[4] = OS_REG_READ(ah, AR_ISR_S3);
+	ah->ah_intrstate[5] = OS_REG_READ(ah, AR_ISR_S4);
+	ah->ah_intrstate[6] = OS_REG_READ(ah, AR_ISR_S5);
+#endif
+
 	if (isr != 0) {
 		struct ath_hal_5212 *ahp = AH5212(ah);
 		uint32_t mask2;
@@ -110,9 +133,15 @@
 				mask2 |= HAL_INT_CST;	
 			if (isr2 & AR_ISR_S2_TSFOOR)
 				mask2 |= HAL_INT_TSFOOR;
+
+			/*
+			 * Don't mask out AR_BCNMISC; instead mask
+			 * out what causes it.
+			 */
+			OS_REG_WRITE(ah, AR_ISR_S2, isr2);
+			isr &= ~AR_ISR_BCNMISC;
 		}
 
-		isr = OS_REG_READ(ah, AR_ISR_RAC);
 		if (isr == 0xffffffff) {
 			*masked = 0;
 			return AH_FALSE;
@@ -119,39 +148,87 @@
 		}
 
 		*masked = isr & HAL_INT_COMMON;
+
+		if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
+			*masked |= HAL_INT_RX;
+		if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM))
+			*masked |= HAL_INT_TX;
+
+		/*
+		 * When doing RX interrupt mitigation, the RXOK bit is set
+		 * in AR_ISR even if the relevant bit in AR_IMR is clear.
+		 * Since this interrupt may be due to another source, don't
+		 * just automatically set HAL_INT_RX if it's set, otherwise
+		 * we could prematurely service the RX queue.
+		 *
+		 * In some cases, the driver can even handle all the RX
+		 * frames just before the mitigation interrupt fires.
+		 * The subsequent RX processing trip will then end up
+		 * processing 0 frames.
+		 */
+#ifdef	AH_AR5416_INTERRUPT_MITIGATION
+		if (isr & AR_ISR_RXERR)
+			*masked |= HAL_INT_RX;
+#else
 		if (isr & (AR_ISR_RXOK | AR_ISR_RXERR))
 			*masked |= HAL_INT_RX;
-		if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | AR_ISR_TXEOL)) {
+#endif
+
+		if (isr & (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR |
+		    AR_ISR_TXEOL)) {
 			*masked |= HAL_INT_TX;
-			isr0 = OS_REG_READ(ah, AR_ISR_S0_S);
+
+			isr0 = OS_REG_READ(ah, AR_ISR_S0);
+			OS_REG_WRITE(ah, AR_ISR_S0, isr0);
+			isr1 = OS_REG_READ(ah, AR_ISR_S1);
+			OS_REG_WRITE(ah, AR_ISR_S1, isr1);
+
+			/*
+			 * Don't clear the primary ISR TX bits, clear
+			 * what causes them (S0/S1.)
+			 */
+			isr &= ~(AR_ISR_TXOK | AR_ISR_TXDESC |
+			    AR_ISR_TXERR | AR_ISR_TXEOL);
+
 			ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXOK);
 			ahp->ah_intrTxqs |= MS(isr0, AR_ISR_S0_QCU_TXDESC);
-			isr1 = OS_REG_READ(ah, AR_ISR_S1_S);
 			ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXERR);
 			ahp->ah_intrTxqs |= MS(isr1, AR_ISR_S1_QCU_TXEOL);
 		}
 
-		if (AR_SREV_MERLIN(ah) || AR_SREV_KITE(ah)) {
+		if ((isr & AR_ISR_GENTMR) || (! pCap->halAutoSleepSupport)) {
 			uint32_t isr5;
-			isr5 = OS_REG_READ(ah, AR_ISR_S5_S);
-			if (isr5 & AR_ISR_S5_TIM_TIMER)
-				*masked |= HAL_INT_TIM_TIMER;
+			isr5 = OS_REG_READ(ah, AR_ISR_S5);
+			OS_REG_WRITE(ah, AR_ISR_S5, isr5);
+			isr &= ~AR_ISR_GENTMR;
+
+			if (! pCap->halAutoSleepSupport)
+				if (isr5 & AR_ISR_S5_TIM_TIMER)
+					*masked |= HAL_INT_TIM_TIMER;
 		}
-
-		/* Interrupt Mitigation on AR5416 */
-#ifdef	AH_AR5416_INTERRUPT_MITIGATION
-		if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
-			*masked |= HAL_INT_RX;
-		if (isr & (AR_ISR_TXMINTR | AR_ISR_TXINTM))
-			*masked |= HAL_INT_TX;
-#endif
 		*masked |= mask2;
 	}
 
+	/*
+	 * Since we're not using AR_ISR_RAC, clear the status bits
+	 * for handled interrupts here. For bits whose interrupt
+	 * source is a secondary register, those bits should've been
+	 * masked out - instead of those bits being written back,
+	 * their source (ie, the secondary status registers) should
+	 * be cleared. That way there are no race conditions with
+	 * new triggers coming in whilst they've been read/cleared.
+	 */
+	OS_REG_WRITE(ah, AR_ISR, isr);
+	/* Flush previous write */
+	OS_REG_READ(ah, AR_ISR);
+
 	if (AR_SREV_HOWL(ah))
 		return AH_TRUE;
 
 	if (sync_cause != 0) {
+		HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: sync_cause=0x%x\n",
+		    __func__,
+		    o_sync_cause);
 		if (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) {
 			*masked |= HAL_INT_FATAL;
 		}
@@ -216,18 +293,12 @@
 	 * Overwrite default mask if Interrupt mitigation
 	 * is specified for AR5416
 	 */
-	mask = ints & HAL_INT_COMMON;
-	if (ints & HAL_INT_TX)
-		mask |= AR_IMR_TXMINTR | AR_IMR_TXINTM;
 	if (ints & HAL_INT_RX)
 		mask |= AR_IMR_RXERR | AR_IMR_RXMINTR | AR_IMR_RXINTM;
-	if (ints & HAL_INT_TX) {
-		if (ahp->ah_txErrInterruptMask)
-			mask |= AR_IMR_TXERR;
-		if (ahp->ah_txEolInterruptMask)
-			mask |= AR_IMR_TXEOL;
-	}
 #else
+	if (ints & HAL_INT_RX)
+		mask |= AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXDESC;
+#endif
 	if (ints & HAL_INT_TX) {
 		if (ahp->ah_txOkInterruptMask)
 			mask |= AR_IMR_TXOK;
@@ -237,10 +308,9 @@
 			mask |= AR_IMR_TXDESC;
 		if (ahp->ah_txEolInterruptMask)
 			mask |= AR_IMR_TXEOL;
+		if (ahp->ah_txUrnInterruptMask)
+			mask |= AR_IMR_TXURN;
 	}
-	if (ints & HAL_INT_RX)
-		mask |= AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXDESC;
-#endif
 	if (ints & (HAL_INT_BMISC)) {
 		mask |= AR_IMR_BCNMISC;
 		if (ints & HAL_INT_TIM)

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_keycache.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_keycache.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_keycache.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_keycache.c 203158 2010-01-29 10:07:17Z rpaulo $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c 250856 2013-05-21 14:28:05Z adrian $
  */
 #include "opt_ah.h"
 
@@ -21,14 +22,14 @@
 #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 "ah_eeprom_v14.h"	/* for owl_get_ntxchains() */
+
 /*
  * Return the wireless modes (a,b,g,n,t) supported by hardware.
  *
@@ -75,21 +76,29 @@
 		AR_MAC_LED_ASSOC_NONE,
 		AR_MAC_LED_ASSOC_NONE,
 	};
-	uint32_t bits;
 
 	if (AR_SREV_HOWL(ah))
 		return;
 
-	bits = OS_REG_READ(ah, AR_MAC_LED);
-	bits = (bits &~ AR_MAC_LED_MODE)
-	     | SM(AR_MAC_LED_MODE_POWON, AR_MAC_LED_MODE)
-#if 1
-	     | SM(AR_MAC_LED_MODE_NETON, AR_MAC_LED_MODE)
-#endif
-	     ;
-	bits = (bits &~ AR_MAC_LED_ASSOC)
-	     | SM(ledbits[state & 0x7], AR_MAC_LED_ASSOC);
-	OS_REG_WRITE(ah, AR_MAC_LED, bits);
+	/*
+	 * Set the blink operating mode.
+	 */
+	OS_REG_RMW_FIELD(ah, AR_MAC_LED,
+	    AR_MAC_LED_ASSOC, ledbits[state & 0x7]);
+
+	/* XXX Blink slow mode? */
+	/* XXX Blink threshold? */
+	/* XXX Blink sleep hystersis? */
+
+	/*
+	 * Set the LED blink configuration to be proportional
+	 * to the current TX and RX filter bytes.  (Ie, RX'ed
+	 * frames that don't match the filter are ignored.)
+	 * This means that higher TX/RX throughput will result
+	 * in the blink rate increasing.
+	 */
+	OS_REG_RMW_FIELD(ah, AR_MAC_LED, AR_MAC_LED_MODE,
+	    AR_MAC_LED_MODE_PROP);
 }
 
 /*
@@ -120,9 +129,36 @@
 	return (((uint64_t) u32) << 32) | ((uint64_t) low2);
 }
 
+/*
+ * Update the TSF.
+ *
+ * The full TSF is only updated once the upper 32 bits have
+ * been written.  Writing only the lower 32 bits of the TSF
+ * will not actually correctly update the TSF.
+ *
+ * The #if 0'ed code is to check whether the previous TSF
+ * reset or write has completed before writing to the
+ * TSF.  Strictly speaking, it should be also checked before
+ * reading the TSF as the write/reset may not have completed.
+ */
 void
 ar5416SetTsf64(struct ath_hal *ah, uint64_t tsf64)
 {
+	/* XXX check if this is correct! */
+#if 0
+	int i;
+	uint32_t v;
+
+	for (i = 0; i < 10; i++) {
+		v = OS_REG_READ(ah, AR_SLP32_MODE);
+		if ((v & AR_SLP32_TSF_WRITE_STATUS) == 0)
+			break;
+		OS_DELAY(10);
+	}
+	if (i == 10)
+		ath_hal_printf(ah, "%s: couldn't slew things right!\n", __func__);
+#endif
+
 	OS_REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff);
 	OS_REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff);
 }
@@ -145,6 +181,14 @@
 	OS_REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE);	
 }
 
+uint32_t
+ar5416GetCurRssi(struct ath_hal *ah)
+{
+	if (AR_SREV_OWL(ah))
+		return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff);
+	return (OS_REG_READ(ah, AR9130_PHY_CURRENT_RSSI) & 0xff);
+}
+
 HAL_BOOL
 ar5416SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings)
 {
@@ -162,13 +206,76 @@
 void
 ar5416SetCoverageClass(struct ath_hal *ah, uint8_t coverageclass, int now)
 {
-	AH_PRIVATE(ah)->ah_coverageClass = coverageclass;
+
+	ar5212SetCoverageClass(ah, coverageclass, now);
 }
 
 /*
+ * Return the busy for rx_frame, rx_clear, and tx_frame
+ */
+HAL_BOOL
+ar5416GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample)
+{
+	struct ath_hal_5416 *ahp = AH5416(ah);
+	u_int32_t good = AH_TRUE;
+
+	/* XXX freeze/unfreeze mib counters */
+	uint32_t rc = OS_REG_READ(ah, AR_RCCNT);
+	uint32_t ec = OS_REG_READ(ah, AR_EXTRCCNT);
+	uint32_t rf = OS_REG_READ(ah, AR_RFCNT);
+	uint32_t tf = OS_REG_READ(ah, AR_TFCNT);
+	uint32_t cc = OS_REG_READ(ah, AR_CCCNT); /* read cycles last */
+
+	if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cc) {
+		/*
+		 * Cycle counter wrap (or initial call); it's not possible
+		 * to accurately calculate a value because the registers
+		 * right shift rather than wrap--so punt and return 0.
+		 */
+		HALDEBUG(ah, HAL_DEBUG_ANY,
+			    "%s: cycle counter wrap. ExtBusy = 0\n", __func__);
+			good = AH_FALSE;
+	} else {
+		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;
+	}
+
+	/*
+	 * 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);
+}
+
+/*
+ * Setup the TX/RX chainmasks - this needs to be done before a call
+ * to the reset method as it doesn't update the hardware.
+ */
+void
+ar5416SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask,
+    uint32_t rx_chainmask)
+{
+	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
+
+	AH5416(ah)->ah_tx_chainmask = tx_chainmask & pCap->halTxChainMask;
+	AH5416(ah)->ah_rx_chainmask = rx_chainmask & pCap->halRxChainMask;
+}
+
+/*
  * 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)
@@ -278,7 +385,7 @@
         rxclear |= HAL_RX_CLEAR_CTL_LOW;
     }
     /* extension channel */
-    if (val & AR_DIAG_RXCLEAR_CTL_LOW) {
+    if (val & AR_DIAG_RXCLEAR_EXT_LOW) {
         rxclear |= HAL_RX_CLEAR_EXT_LOW;
     }
     return rxclear;
@@ -359,6 +466,14 @@
 			HAL_OK : HAL_ENOTSUPP;
 	case HAL_CAP_DIVERSITY:		/* disable classic fast diversity */
 		return HAL_ENXIO;
+	case HAL_CAP_ENFORCE_TXOP:
+		if (capability == 0)
+			return (HAL_OK);
+		if (capability != 1)
+			return (HAL_ENOTSUPP);
+		(*result) =
+		    !! (AH5212(ah)->ah_miscMode & AR_PCU_TXOP_TBTT_LIMIT_ENA);
+		return (HAL_OK);
 	default:
 		break;
 	}
@@ -365,6 +480,50 @@
 	return ar5212GetCapability(ah, type, capability, result);
 }
 
+HAL_BOOL
+ar5416SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
+    u_int32_t capability, u_int32_t setting, HAL_STATUS *status)
+{
+	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
+
+	switch (type) {
+	case HAL_CAP_RX_CHAINMASK:
+		setting &= ath_hal_eepromGet(ah, AR_EEP_RXMASK, NULL);
+		pCap->halRxChainMask = setting;
+		if (owl_get_ntxchains(setting) > 2)
+			pCap->halRxStreams = 2;
+		else
+			pCap->halRxStreams = 1;
+		return AH_TRUE;
+	case HAL_CAP_TX_CHAINMASK:
+		setting &= ath_hal_eepromGet(ah, AR_EEP_TXMASK, NULL);
+		pCap->halTxChainMask = setting;
+		if (owl_get_ntxchains(setting) > 2)
+			pCap->halTxStreams = 2;
+		else
+			pCap->halTxStreams = 1;
+		return AH_TRUE;
+	case HAL_CAP_ENFORCE_TXOP:
+		if (capability != 1)
+			return AH_FALSE;
+		if (setting) {
+			AH5212(ah)->ah_miscMode
+			    |= AR_PCU_TXOP_TBTT_LIMIT_ENA;
+			OS_REG_SET_BIT(ah, AR_MISC_MODE,
+			    AR_PCU_TXOP_TBTT_LIMIT_ENA);
+		} else {
+			AH5212(ah)->ah_miscMode
+			    &= ~AR_PCU_TXOP_TBTT_LIMIT_ENA;
+			OS_REG_CLR_BIT(ah, AR_MISC_MODE,
+			    AR_PCU_TXOP_TBTT_LIMIT_ENA);
+		}
+		return AH_TRUE;
+	default:
+		break;
+	}
+	return ar5212SetCapability(ah, type, capability, setting, status);
+}
+
 static int ar5416DetectMacHang(struct ath_hal *ah);
 static int ar5416DetectBBHang(struct ath_hal *ah);
 
@@ -400,36 +559,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)
@@ -624,157 +753,3 @@
 #undef N
 }
 #undef NUM_STATUS_READS
-
-/*
- * Get the radar parameter values and return them in the pe
- * structure
- */
-void
-ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
-{
-	uint32_t val, temp;
-
-	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
-
-	temp = MS(val,AR_PHY_RADAR_0_FIRPWR);
-	temp |= 0xFFFFFF80;
-	pe->pe_firpwr = temp;
-	pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI);
-	pe->pe_height =  MS(val, AR_PHY_RADAR_0_HEIGHT);
-	pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI);
-	pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND);
-
-	val = OS_REG_READ(ah, AR_PHY_RADAR_1);
-	temp = val & AR_PHY_RADAR_1_RELPWR_ENA;
-	pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH);
-	if (temp)
-		pe->pe_relpwr |= HAL_PHYERR_PARAM_ENABLE;
-	temp = val & AR_PHY_RADAR_1_RELSTEP_CHECK;
-	pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH);
-	if (temp)
-		pe->pe_enabled = 1;
-	else
-		pe->pe_enabled = 0;
-
-	pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN);
-	pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) &
-	    AR_PHY_RADAR_EXT_ENA);
-
-	pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
-	    AR_PHY_RADAR_1_USE_FIR128);
-	pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
-	    AR_PHY_RADAR_1_BLOCK_CHECK);
-	pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
-	    AR_PHY_RADAR_1_MAX_RRSSI);
-}
-
-/*
- * Enable radar detection and set the radar parameters per the
- * values in pe
- */
-void
-ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
-{
-	uint32_t val;
-
-	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
-
-	if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) {
-		val &= ~AR_PHY_RADAR_0_FIRPWR;
-		val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR);
-	}
-	if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) {
-		val &= ~AR_PHY_RADAR_0_RRSSI;
-		val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI);
-	}
-	if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) {
-		val &= ~AR_PHY_RADAR_0_HEIGHT;
-		val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT);
-	}
-	if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) {
-		val &= ~AR_PHY_RADAR_0_PRSSI;
-		val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI);
-	}
-	if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) {
-		val &= ~AR_PHY_RADAR_0_INBAND;
-		val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND);
-	}
-
-	/*Enable FFT data*/
-	val |= AR_PHY_RADAR_0_FFT_ENA;
-
-	OS_REG_WRITE(ah, AR_PHY_RADAR_0, val | AR_PHY_RADAR_0_ENA);
-
-	if (pe->pe_usefir128 == 1)
-		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
-	else if (pe->pe_usefir128 == 0)
-		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
-
-	if (pe->pe_enmaxrssi == 1)
-		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
-	else if (pe->pe_enmaxrssi == 0)
-		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
-
-	if (pe->pe_blockradar == 1)
-		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
-	else if (pe->pe_blockradar == 0)
-		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
-
-	if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) {
-		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
-		val &= ~AR_PHY_RADAR_1_MAXLEN;
-		val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN);
-		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
-	}
-
-	/*
-	 * Enable HT/40 if the upper layer asks;
-	 * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS
-	 * is available.
-	 */
-	if (pe->pe_extchannel == 1)
-		OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
-	else if (pe->pe_extchannel == 0)
-		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
-
-	if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) {
-		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
-		val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH;
-		val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH);
-		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
-	}
-	if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) {
-		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
-		val &= ~AR_PHY_RADAR_1_RELPWR_THRESH;
-		val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH);
-		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
-	}
-}
-
-/*
- * Extract the radar event information from the given phy error.
- *
- * Returns AH_TRUE if the phy error was actually a phy error,
- * AH_FALSE if the phy error wasn't a phy error.
- */
-HAL_BOOL
-ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs,
-    uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event)
-{
-	/*
-	 * For now, this isn't implemented.
-	 */
-	return AH_FALSE;
-}
-
-/*
- * Return whether fast-clock is currently enabled for this
- * channel.
- */
-HAL_BOOL
-ar5416IsFastClockEnabled(struct ath_hal *ah)
-{
-	struct ath_hal_private *ahp = AH_PRIVATE(ah);
-
-	return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan);
-}

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_phy.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_phy.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_phy.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_phy.c 218908 2011-02-21 05:10:34Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_power.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_power.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_power.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_power.c 234269 2012-04-14 04:40:11Z adrian $
  */
 #include "opt_ah.h"
 
@@ -50,6 +51,7 @@
 			& AR_RTC_PM_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) {
 			if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON))
 				goto bad;			
+			AH5416(ah)->ah_initPLL(ah, AH_NULL);
 		}
 
 		if (AR_SREV_HOWL(ah))

Added: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c	                        (rev 0)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,433 @@
+/* $MidnightBSD$ */
+/*
+ * Copyright (c) 2010-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: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c 239638 2012-08-24 01:29:46Z adrian $
+ */
+#include "opt_ah.h"
+
+#include "ah.h"
+#include "ah_internal.h"
+#include "ah_devid.h"
+#include "ah_desc.h"                    /* NB: for HAL_PHYERR* */
+
+#include "ar5416/ar5416.h"
+#include "ar5416/ar5416reg.h"
+#include "ar5416/ar5416phy.h"
+
+#include "ah_eeprom_v14.h"	/* for owl_get_ntxchains() */
+
+/*
+ * These are default parameters for the AR5416 and
+ * later 802.11n NICs.  They simply enable some
+ * radar pulse event generation.
+ *
+ * These are very likely not valid for the AR5212 era
+ * NICs.
+ *
+ * Since these define signal sizing and threshold
+ * parameters, they may need changing based on the
+ * specific antenna and receive amplifier
+ * configuration.
+ */
+#define	AR5416_DFS_FIRPWR	-33
+#define	AR5416_DFS_RRSSI	20
+#define	AR5416_DFS_HEIGHT	10
+#define	AR5416_DFS_PRSSI	15
+#define	AR5416_DFS_INBAND	15
+#define	AR5416_DFS_RELPWR	8
+#define	AR5416_DFS_RELSTEP	12
+#define	AR5416_DFS_MAXLEN	255
+
+HAL_BOOL
+ar5416GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+
+	/*
+	 * These are general examples of the parameter values
+	 * to use when configuring radar pulse detection for
+	 * the AR5416, AR91xx, AR92xx NICs.  They are only
+	 * for testing and do require tuning depending upon the
+	 * hardware and deployment specifics.
+	 */
+	pe->pe_firpwr = AR5416_DFS_FIRPWR;
+	pe->pe_rrssi = AR5416_DFS_RRSSI;
+	pe->pe_height = AR5416_DFS_HEIGHT;
+	pe->pe_prssi = AR5416_DFS_PRSSI;
+	pe->pe_inband = AR5416_DFS_INBAND;
+	pe->pe_relpwr = AR5416_DFS_RELPWR;
+	pe->pe_relstep = AR5416_DFS_RELSTEP;
+	pe->pe_maxlen = AR5416_DFS_MAXLEN;
+
+	return (AH_TRUE);
+}
+
+/*
+ * Get the radar parameter values and return them in the pe
+ * structure
+ */
+void
+ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+	uint32_t val, temp;
+
+	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
+
+	temp = MS(val,AR_PHY_RADAR_0_FIRPWR);
+	temp |= 0xFFFFFF80;
+	pe->pe_firpwr = temp;
+	pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI);
+	pe->pe_height =  MS(val, AR_PHY_RADAR_0_HEIGHT);
+	pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI);
+	pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND);
+
+	/* RADAR_1 values */
+	val = OS_REG_READ(ah, AR_PHY_RADAR_1);
+	pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH);
+	pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH);
+	pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN);
+
+	pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) &
+	    AR_PHY_RADAR_EXT_ENA);
+
+	pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
+	    AR_PHY_RADAR_1_USE_FIR128);
+	pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
+	    AR_PHY_RADAR_1_BLOCK_CHECK);
+	pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
+	    AR_PHY_RADAR_1_MAX_RRSSI);
+	pe->pe_enabled = !!
+	    (OS_REG_READ(ah, AR_PHY_RADAR_0) & AR_PHY_RADAR_0_ENA);
+	pe->pe_enrelpwr = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
+	    AR_PHY_RADAR_1_RELPWR_ENA);
+	pe->pe_en_relstep_check = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
+	    AR_PHY_RADAR_1_RELSTEP_CHECK);
+}
+
+/*
+ * Enable radar detection and set the radar parameters per the
+ * values in pe
+ */
+void
+ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
+{
+	uint32_t val;
+
+	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
+
+	if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) {
+		val &= ~AR_PHY_RADAR_0_FIRPWR;
+		val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR);
+	}
+	if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) {
+		val &= ~AR_PHY_RADAR_0_RRSSI;
+		val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI);
+	}
+	if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) {
+		val &= ~AR_PHY_RADAR_0_HEIGHT;
+		val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT);
+	}
+	if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) {
+		val &= ~AR_PHY_RADAR_0_PRSSI;
+		val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI);
+	}
+	if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) {
+		val &= ~AR_PHY_RADAR_0_INBAND;
+		val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND);
+	}
+
+	/*Enable FFT data*/
+	val |= AR_PHY_RADAR_0_FFT_ENA;
+	OS_REG_WRITE(ah, AR_PHY_RADAR_0, val);
+
+	/* Implicitly enable */
+	if (pe->pe_enabled == 1)
+		OS_REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
+	else if (pe->pe_enabled == 0)
+		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
+
+	if (pe->pe_usefir128 == 1)
+		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
+	else if (pe->pe_usefir128 == 0)
+		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
+
+	if (pe->pe_enmaxrssi == 1)
+		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
+	else if (pe->pe_enmaxrssi == 0)
+		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
+
+	if (pe->pe_blockradar == 1)
+		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
+	else if (pe->pe_blockradar == 0)
+		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
+
+	if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) {
+		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
+		val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH;
+		val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH);
+		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
+	}
+	if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) {
+		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
+		val &= ~AR_PHY_RADAR_1_RELPWR_THRESH;
+		val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH);
+		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
+	}
+
+	if (pe->pe_en_relstep_check == 1)
+		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1,
+		    AR_PHY_RADAR_1_RELSTEP_CHECK);
+	else if (pe->pe_en_relstep_check == 0)
+		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1,
+		    AR_PHY_RADAR_1_RELSTEP_CHECK);
+
+	if (pe->pe_enrelpwr == 1)
+		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1,
+		    AR_PHY_RADAR_1_RELPWR_ENA);
+	else if (pe->pe_enrelpwr == 0)
+		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1,
+		    AR_PHY_RADAR_1_RELPWR_ENA);
+
+	if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) {
+		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
+		val &= ~AR_PHY_RADAR_1_MAXLEN;
+		val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN);
+		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
+	}
+
+	/*
+	 * Enable HT/40 if the upper layer asks;
+	 * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS
+	 * is available.
+	 */
+	if (pe->pe_extchannel == 1)
+		OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
+	else if (pe->pe_extchannel == 0)
+		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
+}
+
+/*
+ * Extract the radar event information from the given phy error.
+ *
+ * Returns AH_TRUE if the phy error was actually a phy error,
+ * AH_FALSE if the phy error wasn't a phy error.
+ */
+
+/* Flags for pulse_bw_info */
+#define	PRI_CH_RADAR_FOUND		0x01
+#define	EXT_CH_RADAR_FOUND		0x02
+#define	EXT_CH_RADAR_EARLY_FOUND	0x04
+
+HAL_BOOL
+ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs,
+    uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event)
+{
+	HAL_BOOL doDfsExtCh;
+	HAL_BOOL doDfsEnhanced;
+	HAL_BOOL doDfsCombinedRssi;
+
+	uint8_t rssi = 0, ext_rssi = 0;
+	uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0;
+	uint32_t dur = 0;
+	int pri_found = 1, ext_found = 0;
+	int early_ext = 0;
+	int is_dc = 0;
+	uint16_t datalen;		/* length from the RX status field */
+
+	/* Check whether the given phy error is a radar event */
+	if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) &&
+	    (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) {
+		return AH_FALSE;
+	}
+
+	/* Grab copies of the capabilities; just to make the code clearer */
+	doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport;
+	doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport;
+	doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi;
+
+	datalen = rxs->rs_datalen;
+
+	/* If hardware supports it, use combined RSSI, else use chain 0 RSSI */
+	if (doDfsCombinedRssi)
+		rssi = (uint8_t) rxs->rs_rssi;
+	else		
+		rssi = (uint8_t) rxs->rs_rssi_ctl[0];
+
+	/* Set this; but only use it if doDfsExtCh is set */
+	ext_rssi = (uint8_t) rxs->rs_rssi_ext[0];
+
+	/* Cap it at 0 if the RSSI is a negative number */
+	if (rssi & 0x80)
+		rssi = 0;
+
+	if (ext_rssi & 0x80)
+		ext_rssi = 0;
+
+	/*
+	 * Fetch the relevant data from the frame
+	 */
+	if (doDfsExtCh) {
+		if (datalen < 3)
+			return AH_FALSE;
+
+		/* Last three bytes of the frame are of interest */
+		pulse_length_pri = *(buf + datalen - 3);
+		pulse_length_ext = *(buf + datalen - 2);
+		pulse_bw_info = *(buf + datalen - 1);
+		HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d,"
+		    " pulse_length_ext=%d, pulse_bw_info=%x\n",
+		    __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext,
+		    pulse_bw_info);
+	} else {
+		/* The pulse width is byte 0 of the data */
+		if (datalen >= 1)
+			dur = ((uint8_t) buf[0]) & 0xff;
+		else
+			dur = 0;
+
+		if (dur == 0 && rssi == 0) {
+			HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__);
+			return AH_FALSE;
+		}
+
+		HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur);
+
+		/* Single-channel only */
+		pri_found = 1;
+		ext_found = 0;
+	}
+
+	/*
+	 * If doing extended channel data, pulse_bw_info must
+	 * have one of the flags set.
+	 */
+	if (doDfsExtCh && pulse_bw_info == 0x0)
+		return AH_FALSE;
+		
+	/*
+	 * If the extended channel data is available, calculate
+	 * which to pay attention to.
+	 */
+	if (doDfsExtCh) {
+		/* If pulse is on DC, take the larger duration of the two */
+		if ((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
+		    (pulse_bw_info & PRI_CH_RADAR_FOUND)) {
+			is_dc = 1;
+			if (pulse_length_ext > pulse_length_pri) {
+				dur = pulse_length_ext;
+				pri_found = 0;
+				ext_found = 1;
+			} else {
+				dur = pulse_length_pri;
+				pri_found = 1;
+				ext_found = 0;
+			}
+		} else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) {
+			dur = pulse_length_ext;
+			pri_found = 0;
+			ext_found = 1;
+			early_ext = 1;
+		} else if (pulse_bw_info & PRI_CH_RADAR_FOUND) {
+			dur = pulse_length_pri;
+			pri_found = 1;
+			ext_found = 0;
+		} else if (pulse_bw_info & EXT_CH_RADAR_FOUND) {
+			dur = pulse_length_ext;
+			pri_found = 0;
+			ext_found = 1;
+		}
+		
+	}
+
+	/*
+	 * For enhanced DFS (Merlin and later), pulse_bw_info has
+	 * implications for selecting the correct RSSI value.
+	 */
+	if (doDfsEnhanced) {
+		switch (pulse_bw_info & 0x03) {
+		case 0:
+			/* No radar? */
+			rssi = 0;
+			break;
+		case PRI_CH_RADAR_FOUND:
+			/* Radar in primary channel */
+			/* Cannot use ctrl channel RSSI if ext channel is stronger */
+			if (ext_rssi >= (rssi + 3)) {
+				rssi = 0;
+			};
+			break;
+		case EXT_CH_RADAR_FOUND:
+			/* Radar in extended channel */
+			/* Cannot use ext channel RSSI if ctrl channel is stronger */
+			if (rssi >= (ext_rssi + 12)) {
+				rssi = 0;
+			} else {
+				rssi = ext_rssi;
+			}
+			break;
+		case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND):
+			/* When both are present, use stronger one */
+			if (rssi < ext_rssi)
+				rssi = ext_rssi;
+			break;
+		}
+	}
+
+	/*
+	 * If not doing enhanced DFS, choose the ext channel if
+	 * it is stronger than the main channel
+	 */
+	if (doDfsExtCh && !doDfsEnhanced) {
+		if ((ext_rssi > rssi) && (ext_rssi < 128))
+			rssi = ext_rssi;
+	}
+
+	/*
+	 * XXX what happens if the above code decides the RSSI
+	 * XXX wasn't valid, an sets it to 0?
+	 */
+
+	/*
+	 * Fill out dfs_event structure.
+	 */
+	event->re_full_ts = fulltsf;
+	event->re_ts = rxs->rs_tstamp;
+	event->re_rssi = rssi;
+	event->re_dur = dur;
+
+	event->re_flags = 0;
+	if (pri_found)
+		event->re_flags |= HAL_DFS_EVENT_PRICH;
+	if (ext_found)
+		event->re_flags |= HAL_DFS_EVENT_EXTCH;
+	if (early_ext)
+		event->re_flags |= HAL_DFS_EVENT_EXTEARLY;
+	if (is_dc)
+		event->re_flags |= HAL_DFS_EVENT_ISDC;
+
+	return AH_TRUE;
+}
+
+/*
+ * Return whether fast-clock is currently enabled for this
+ * channel.
+ */
+HAL_BOOL
+ar5416IsFastClockEnabled(struct ath_hal *ah)
+{
+	struct ath_hal_private *ahp = AH_PRIVATE(ah);
+
+	return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan);
+}


Property changes on: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_recv.c 250346 2013-05-08 01:11:25Z adrian $
  */
 #include "opt_ah.h"
 
@@ -67,6 +68,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
@@ -107,7 +142,6 @@
     uint32_t size, u_int flags)
 {
 	struct ar5416_desc *ads = AR5416DESC(ds);
-	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
 
 	HALASSERT((size &~ AR_BufLen) == 0);
 
@@ -119,8 +153,7 @@
 	ads->ds_rxstatus8 &= ~AR_RxDone;
 
 	/* clear the rest of the status fields */
-	if (! pCap->halAutoSleepSupport)
-        	OS_MEMZERO(&(ads->u), sizeof(ads->u));
+	OS_MEMZERO(&(ads->u), sizeof(ads->u));
 
 	return AH_TRUE;
 }
@@ -177,6 +210,14 @@
 	if (ads->ds_rxstatus3 & AR_2040)
 		rs->rs_flags |= HAL_RX_2040;
 
+	/*
+	 * Only the AR9280 and later chips support STBC RX, so
+	 * ensure we only set this bit for those chips.
+	 */
+	if (AR_SREV_MERLIN_10_OR_LATER(ah)
+	    && ads->ds_rxstatus3 & AR_STBCFrame)
+		rs->rs_flags |= HAL_RX_STBC;
+
 	if (ads->ds_rxstatus8 & AR_PreDelimCRCErr)
 		rs->rs_flags |= HAL_RX_DELIM_CRC_PRE;
 	if (ads->ds_rxstatus8 & AR_PostDelimCRCErr)
@@ -196,15 +237,40 @@
 		 * Consequently we filter them out here so we don't
 		 * confuse and/or complicate drivers.
 		 */
-		if (ads->ds_rxstatus8 & AR_CRCErr)
-			rs->rs_status |= HAL_RXERR_CRC;
-		else if (ads->ds_rxstatus8 & AR_PHYErr) {
+
+		/*
+		 * 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;
 
-			rs->rs_status |= HAL_RXERR_PHY;
+			/*
+			 * 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);
-			rs->rs_phyerr = phyerr;
-		} else if (ads->ds_rxstatus8 & AR_DecryptCRCErr)
+
+			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_DecryptCRCErr)
 			rs->rs_status |= HAL_RXERR_DECRYPT;
 		else if (ads->ds_rxstatus8 & AR_MichaelErr)
 			rs->rs_status |= HAL_RXERR_MIC;

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c 251483 2013-06-07 05:17:58Z adrian $
  */
 #include "opt_ah.h"
 
@@ -44,7 +45,6 @@
 static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode);
 static void ar5416InitQoS(struct ath_hal *ah);
 static void ar5416InitUserSettings(struct ath_hal *ah);
-static void ar5416UpdateChainMasks(struct ath_hal *ah, HAL_BOOL is_ht);
 static void ar5416OverrideIni(struct ath_hal *ah, const struct ieee80211_channel *);
 
 #if 0
@@ -62,6 +62,8 @@
 	uint16_t powerLimit);
 static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan);
 static void ar5416MarkPhyInactive(struct ath_hal *ah);
+static void ar5416SetIFSTiming(struct ath_hal *ah,
+   const struct ieee80211_channel *chan);
 
 /*
  * Places the device in and out of reset and then places sane
@@ -118,6 +120,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
@@ -132,7 +137,20 @@
 	 * Preserve the antenna on a channel change
 	 */
 	saveDefAntenna = OS_REG_READ(ah, AR_DEF_ANTENNA);
-	if (saveDefAntenna == 0)		/* XXX magic constants */
+
+	/*
+	 * Don't do this for the AR9285 - it breaks RX for single
+	 * antenna designs when diversity is disabled.
+	 *
+	 * I'm not sure what this was working around; it may be
+	 * something to do with the AR5416.  Certainly this register
+	 * isn't supposed to be used by the MIMO chips for anything
+	 * except for defining the default antenna when an external
+	 * phase array / smart antenna is connected.
+	 *
+	 * See PR: kern/179269 .
+	 */
+	if ((! AR_SREV_KITE(ah)) && saveDefAntenna == 0)	/* XXX magic constants */
 		saveDefAntenna = 1;
 
 	/* Save hardware flag before chip reset clears the register */
@@ -146,7 +164,9 @@
 
 	/* For chips on which the RTC reset is done, save TSF before it gets cleared */
 	if (AR_SREV_HOWL(ah) ||
-	    (AR_SREV_MERLIN(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)))
+	    (AR_SREV_MERLIN(ah) &&
+	     ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) ||
+	    (ah->ah_config.ah_force_full_reset))
 		tsf = ar5416GetTsf64(ah);
 
 	/* Mark PHY as inactive; marked active in ar5416InitBB() */
@@ -203,11 +223,6 @@
 		__func__, OS_REG_READ(ah,AR_PHY_ADC_CTL));	
 
 	/*
-	 * Setup ah_tx_chainmask / ah_rx_chainmask before we fiddle
-	 * with enabling the TX/RX radio chains.
-	 */
-	ar5416UpdateChainMasks(ah, IEEE80211_IS_CHAN_HT(chan));
-	/*
 	 * This routine swaps the analog chains - it should be done
 	 * before any radio register twiddling is done.
 	 */
@@ -275,12 +290,19 @@
 		ar5416StartTsf2(ah);
 #endif
 
+	/*
+	 * Enable Bluetooth Coexistence if it's enabled.
+	 */
+	if (AH5416(ah)->ah_btCoexConfigType != HAL_BT_COEX_CFG_NONE)
+		ar5416InitBTCoex(ah);
+
 	/* Restore previous antenna */
 	OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna);
 
-	/* then our BSSID */
+	/* then our BSSID and associate id */
 	OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid));
-	OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4));
+	OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) |
+	    (ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S);
 
 	/* Restore bmiss rssi & count thresholds */
 	OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr);
@@ -304,11 +326,26 @@
 		ah->ah_resetTxQueue(ah, i);
 
 	ar5416InitIMR(ah, opmode);
-	ar5212SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1);
+	ar5416SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1);
 	ar5416InitQoS(ah);
 	/* This may override the AR_DIAG_SW register */
 	ar5416InitUserSettings(ah);
 
+	/* XXX this won't work for AR9287! */
+	if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) {
+		ar5416SetIFSTiming(ah, chan);
+#if 0
+			/*
+			 * AR5413?
+			 * Force window_length for 1/2 and 1/4 rate channels,
+			 * the ini file sets this to zero otherwise.
+			 */
+			OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL,
+			    AR_PHY_FRAME_CTL_WINLEN, 3);
+		}
+#endif
+	}
+
 	if (AR_SREV_KIWI_13_OR_LATER(ah)) {
 		/*
 		 * Enable ASYNC FIFO
@@ -357,13 +394,33 @@
 	 */
 	OS_REG_WRITE(ah, AR_OBS, 8);
 
-#ifdef	AH_AR5416_INTERRUPT_MITIGATION
+	/*
+	 * Disable the "general" TX/RX mitigation timers.
+	 */
 	OS_REG_WRITE(ah, AR_MIRT, 0);
 
-	OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500);
-	OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000);
-	OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300);
-	OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750);
+#ifdef	AH_AR5416_INTERRUPT_MITIGATION
+	/*
+	 * This initialises the RX interrupt mitigation timers.
+	 *
+	 * The mitigation timers begin at idle and are triggered
+	 * upon the RXOK of a single frame (or sub-frame, for A-MPDU.)
+	 * Then, the RX mitigation interrupt will fire:
+	 *
+	 * + 250uS after the last RX'ed frame, or
+	 * + 700uS after the first RX'ed frame
+	 *
+	 * Thus, the LAST field dictates the extra latency
+	 * induced by the RX mitigation method and the FIRST
+	 * field dictates how long to delay before firing an
+	 * RX mitigation interrupt.
+	 *
+	 * Please note this only seems to be for RXOK frames;
+	 * not CRC or PHY error frames.
+	 *
+	 */
+	OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 250);
+	OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 700);
 #endif
 	ar5416InitBB(ah, chan);
 
@@ -527,6 +584,10 @@
 	/*
 	 * let mac dma writes be in 128 byte chunks
 	 */
+	/*
+	 * XXX If you change this, you must change the headroom
+	 * assigned in ah_maxTxTrigLev - see ar5416InitState().
+	 */
 	OS_REG_WRITE(ah, AR_RXCFG, 
 		(OS_REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK) | AR_RXCFG_DMASZ_128B);
 
@@ -610,11 +671,11 @@
                         | AR_IMR_BCNMISC;
 
 #ifdef	AH_AR5416_INTERRUPT_MITIGATION
-	ahp->ah_maskReg |= AR_IMR_TXINTM | AR_IMR_RXINTM
-			|  AR_IMR_TXMINTR | AR_IMR_RXMINTR;
+	ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
 #else
-	ahp->ah_maskReg |= AR_IMR_TXOK | AR_IMR_RXOK;
+	ahp->ah_maskReg |= AR_IMR_RXOK;
 #endif	
+	ahp->ah_maskReg |= AR_IMR_TXOK;
 
 	if (opmode == HAL_M_HOSTAP)
 		ahp->ah_maskReg |= AR_IMR_MIB;
@@ -666,7 +727,8 @@
 
 	/* Restore user-specified settings */
 	if (ahp->ah_miscMode != 0)
-		OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode);
+		OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE)
+		    | ahp->ah_miscMode);
 	if (ahp->ah_sifstime != (u_int) -1)
 		ar5212SetSifsTime(ah, ahp->ah_sifstime);
 	if (ahp->ah_slottime != (u_int) -1)
@@ -701,6 +763,7 @@
 		rfMode |= IEEE80211_IS_CHAN_5GHZ(chan) ?
 			AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ;
 	}
+
 	OS_REG_WRITE(ah, AR_PHY_MODE, rfMode);
 }
 
@@ -712,12 +775,15 @@
 {
 	OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0);
 	/*
-	 * Warm reset is optimistic.
+	 * Warm reset is optimistic for open-loop TX power control.
 	 */
 	if (AR_SREV_MERLIN(ah) &&
 	    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
 		if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON))
 			return AH_FALSE;
+	} else if (ah->ah_config.ah_force_full_reset) {
+		if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON))
+			return AH_FALSE;
 	} else {
 		if (!ar5416SetResetReg(ah, HAL_RESET_WARM))
 			return AH_FALSE;
@@ -959,6 +1025,14 @@
               | POW_SM(ratesArray[rateDupCck], 0)
         );
     }
+
+    /*
+     * Set max power to 30 dBm and, optionally,
+     * enable TPC in tx descriptors.
+     */
+    OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER |
+      (AH5212(ah)->ah_tpcEnabled ? AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE : 0));
+#undef POW_SM
 }
 
 
@@ -973,12 +1047,11 @@
 	const struct ieee80211_channel *chan, uint16_t *rfXpdGain)
 {
 #define N(a)            (sizeof (a) / sizeof (a[0]))
+#define POW_SM(_r, _s)     (((_r) & 0x3f) << (_s))
 
     MODAL_EEP_HEADER	*pModal;
     struct ath_hal_5212 *ahp = AH5212(ah);
-    int16_t		ratesArray[Ar5416RateSize];
     int16_t		txPowerIndexOffset = 0;
-    uint8_t		ht40PowerIncForPdadc = 2;	
     int			i;
     
     uint16_t		cfgCtl;
@@ -991,8 +1064,13 @@
 
     HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1);
 
+    /*
+     * Default to 2, is overridden based on the EEPROM version / value.
+     */
+    AH5416(ah)->ah_ht40PowerIncForPdadc = 2;
+
     /* Setup info for the actual eeprom */
-    OS_MEMZERO(ratesArray, sizeof(ratesArray));
+    OS_MEMZERO(AH5416(ah)->ah_ratesArray, sizeof(AH5416(ah)->ah_ratesArray));
     cfgCtl = ath_hal_getctl(ah, chan);
     powerLimit = chan->ic_maxregpower * 2;
     twiceAntennaReduction = chan->ic_maxantgain;
@@ -1002,11 +1080,12 @@
 	__func__,chan->ic_freq, cfgCtl );      
   
     if (IS_EEP_MINOR_V2(ah)) {
-        ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
     }
  
     if (!ar5416SetPowerPerRateTable(ah, pEepData,  chan,
-                                    &ratesArray[0],cfgCtl,
+                                    &AH5416(ah)->ah_ratesArray[0],
+				    cfgCtl,
                                     twiceAntennaReduction,
 				    twiceMaxRegulatoryPower, powerLimit)) {
         HALDEBUG(ah, HAL_DEBUG_ANY,
@@ -1020,14 +1099,15 @@
         return AH_FALSE;
     }
   
-    maxPower = AH_MAX(ratesArray[rate6mb], ratesArray[rateHt20_0]);
+    maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb],
+      AH5416(ah)->ah_ratesArray[rateHt20_0]);
 
     if (IEEE80211_IS_CHAN_2GHZ(chan)) {
-        maxPower = AH_MAX(maxPower, ratesArray[rate1l]);
+        maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rate1l]);
     }
 
     if (IEEE80211_IS_CHAN_HT40(chan)) {
-        maxPower = AH_MAX(maxPower, ratesArray[rateHt40_0]);
+        maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rateHt40_0]);
     }
 
     ahp->ah_tx6PowerInHalfDbm = maxPower;   
@@ -1038,10 +1118,11 @@
      * txPowerIndexOffset is set by the SetPowerTable() call -
      *  adjust the rate table (0 offset if rates EEPROM not loaded)
      */
-    for (i = 0; i < N(ratesArray); i++) {
-        ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
-        if (ratesArray[i] > AR5416_MAX_RATE_POWER)
-            ratesArray[i] = AR5416_MAX_RATE_POWER;
+    for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) {
+        AH5416(ah)->ah_ratesArray[i] =
+          (int16_t)(txPowerIndexOffset + AH5416(ah)->ah_ratesArray[i]);
+        if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER)
+            AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER;
     }
 
 #ifdef AH_EEPROM_DUMP
@@ -1052,7 +1133,7 @@
      * this debugging; the values won't necessarily be what's being
      * programmed into the hardware.
      */
-    ar5416PrintPowerPerRate(ah, ratesArray);
+    ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray);
 #endif
 
     /*
@@ -1068,16 +1149,16 @@
 	    &pwr_table_offset);
 	/* Underflow power gets clamped at raw value 0 */
 	/* Overflow power gets camped at AR5416_MAX_RATE_POWER */
-	for (i = 0; i < N(ratesArray); i++) {
+	for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) {
 		/*
 		 * + pwr_table_offset is in dBm
 		 * + ratesArray is in 1/2 dBm
 		 */
-		ratesArray[i] -= (pwr_table_offset * 2);
-		if (ratesArray[i] < 0)
-			ratesArray[i] = 0;
-		else if (ratesArray[i] > AR5416_MAX_RATE_POWER)
-		    ratesArray[i] = AR5416_MAX_RATE_POWER;
+		AH5416(ah)->ah_ratesArray[i] -= (pwr_table_offset * 2);
+		if (AH5416(ah)->ah_ratesArray[i] < 0)
+			AH5416(ah)->ah_ratesArray[i] = 0;
+		else if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER)
+		    AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER;
 	}
     }
 
@@ -1104,9 +1185,9 @@
         int cck_ofdm_delta = 2;
 	int i;
 	for (i = 0; i < N(adj); i++) {
-            ratesArray[adj[i]] -= cck_ofdm_delta;
-	    if (ratesArray[adj[i]] < 0)
-	        ratesArray[adj[i]] = 0;
+            AH5416(ah)->ah_ratesArray[adj[i]] -= cck_ofdm_delta;
+	    if (AH5416(ah)->ah_ratesArray[adj[i]] < 0)
+	        AH5416(ah)->ah_ratesArray[adj[i]] = 0;
         }
     }
 
@@ -1118,18 +1199,20 @@
      * XXX handle overflow/too high power level?
      */
     if (IEEE80211_IS_CHAN_HT40(chan)) {
-	ratesArray[rateHt40_0] += ht40PowerIncForPdadc;
-	ratesArray[rateHt40_1] += ht40PowerIncForPdadc;
-	ratesArray[rateHt40_2] += ht40PowerIncForPdadc;
-	ratesArray[rateHt40_3] += ht40PowerIncForPdadc;
-	ratesArray[rateHt40_4] += ht40PowerIncForPdadc;
-	ratesArray[rateHt40_5] += ht40PowerIncForPdadc;
-	ratesArray[rateHt40_6] += ht40PowerIncForPdadc;
-	ratesArray[rateHt40_7] += ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ratesArray[rateHt40_0] +=
+	  AH5416(ah)->ah_ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ratesArray[rateHt40_1] +=
+	  AH5416(ah)->ah_ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ratesArray[rateHt40_2] += AH5416(ah)->ah_ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ratesArray[rateHt40_3] += AH5416(ah)->ah_ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ratesArray[rateHt40_4] += AH5416(ah)->ah_ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ratesArray[rateHt40_5] += AH5416(ah)->ah_ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ratesArray[rateHt40_6] += AH5416(ah)->ah_ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ratesArray[rateHt40_7] += AH5416(ah)->ah_ht40PowerIncForPdadc;
     }
 
     /* Write the TX power rate registers */
-    ar5416WriteTxPowerRateRegisters(ah, chan, ratesArray);
+    ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray);
 
     /* Write the Power subtraction for dynamic chain changing, for per-packet powertx */
     OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
@@ -1148,7 +1231,8 @@
 HAL_RFGAIN
 ar5416GetRfgain(struct ath_hal *ah)
 {
-	return HAL_RFGAIN_INACTIVE;
+
+	return (HAL_RFGAIN_INACTIVE);
 }
 
 /*
@@ -1157,13 +1241,14 @@
 HAL_BOOL
 ar5416Disable(struct ath_hal *ah)
 {
-	if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
+
+	if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
 		return AH_FALSE;
 	if (! ar5416SetResetReg(ah, HAL_RESET_COLD))
 		return AH_FALSE;
 
 	AH5416(ah)->ah_initPLL(ah, AH_NULL);
-	return AH_TRUE;
+	return (AH_TRUE);
 }
 
 /*
@@ -1175,11 +1260,12 @@
 HAL_BOOL
 ar5416PhyDisable(struct ath_hal *ah)
 {
+
 	if (! ar5416SetResetReg(ah, HAL_RESET_WARM))
 		return AH_FALSE;
 
 	AH5416(ah)->ah_initPLL(ah, AH_NULL);
-	return AH_TRUE;
+	return (AH_TRUE);
 }
 
 /*
@@ -1188,6 +1274,12 @@
 HAL_BOOL
 ar5416SetResetReg(struct ath_hal *ah, uint32_t type)
 {
+	/*
+	 * Set force wake
+	 */
+	OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+	    AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT);
+
 	switch (type) {
 	case HAL_RESET_POWER_ON:
 		return ar5416SetResetPowerOn(ah);
@@ -1218,10 +1310,15 @@
             AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT);    
 
     /*
-     * RTC reset and clear
+     * PowerOn reset can be used in open loop power control or failure recovery.
+     * If we do RTC reset while DMA is still running, hardware may corrupt memory.
+     * Therefore, we need to reset AHB first to stop DMA.
      */
     if (! AR_SREV_HOWL(ah))
     	OS_REG_WRITE(ah, AR_RC, AR_RC_AHB);
+    /*
+     * RTC reset and clear
+     */
     OS_REG_WRITE(ah, AR_RTC_RESET, 0);
     OS_DELAY(20);
 
@@ -1272,6 +1369,11 @@
 #endif	/* AH_SUPPORT_AR9130 */
         /*
          * Reset AHB
+         *
+         * (In case the last interrupt source was a bus timeout.)
+         * XXX TODO: this is not the way to do it! It should be recorded
+         * XXX by the interrupt handler and passed _into_ the
+         * XXX reset path routine so this occurs.
          */
         tmpReg = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE);
         if (tmpReg & (AR_INTR_SYNC_LOCAL_TIMEOUT|AR_INTR_SYNC_RADM_CPL_TIMEOUT)) {
@@ -1327,16 +1429,15 @@
 	if (type == HAL_RESET_COLD) {
 		if (isBigEndian()) {
 			/*
-			 * Set CFG, little-endian for register
-			 * and descriptor accesses.
+			 * Set CFG, little-endian for descriptor accesses.
 			 */
-			mask = INIT_CONFIG_STATUS | AR_CFG_SWRD | AR_CFG_SWRG;
+			mask = INIT_CONFIG_STATUS | AR_CFG_SWRD;
 #ifndef AH_NEED_DESC_SWAP
 			mask |= AR_CFG_SWTD;
 #endif
 			HALDEBUG(ah, HAL_DEBUG_RESET,
 			    "%s Applying descriptor swap\n", __func__);
-			OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask));
+			OS_REG_WRITE(ah, AR_CFG, mask);
 		} else
 			OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS);
 	}
@@ -1394,31 +1495,6 @@
 	}
 }
 
-/*
- * Update the chainmask based on the current channel configuration.
- *
- * XXX ath9k checks bluetooth co-existence here
- * XXX ath9k checks whether the current state is "off-channel".
- * XXX ath9k sticks the hardware into 1x1 mode for legacy;
- *     we're going to leave multi-RX on for multi-path cancellation.
- */
-static void
-ar5416UpdateChainMasks(struct ath_hal *ah, HAL_BOOL is_ht)
-{
-	struct ath_hal_private *ahpriv = AH_PRIVATE(ah);
-	HAL_CAPABILITIES *pCap = &ahpriv->ah_caps;
-
-	if (is_ht) {
-		AH5416(ah)->ah_tx_chainmask = pCap->halTxChainMask;
-	} else {
-		AH5416(ah)->ah_tx_chainmask = 1;
-	}
-	AH5416(ah)->ah_rx_chainmask = pCap->halRxChainMask;
-	HALDEBUG(ah, HAL_DEBUG_RESET, "TX chainmask: 0x%x; RX chainmask: 0x%x\n",
-	    AH5416(ah)->ah_tx_chainmask,
-	    AH5416(ah)->ah_rx_chainmask);
-}
-
 void
 ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan)
 {
@@ -1453,6 +1529,7 @@
     const struct ar5416eeprom *eep,
     uint8_t txRxAttenLocal, int regChainOffset, int i)
 {
+
 	if (IS_EEP_MINOR_V3(ah)) {
 		txRxAttenLocal = pModal->txRxAttenCh[i];
 
@@ -1649,7 +1726,7 @@
 		    eep->baseEepHeader.desiredScaleCCK);
         }
 
-    return AH_TRUE;
+    return (AH_TRUE);
 }
 
 /*
@@ -1683,7 +1760,8 @@
 
 	/* Set rates Array from collected data */
 	ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] =
-	    ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm->tPow2x[0];
+	ratesArray[rate18mb] = ratesArray[rate24mb] =
+	    targetPowerOfdm->tPow2x[0];
 	ratesArray[rate36mb] = targetPowerOfdm->tPow2x[1];
 	ratesArray[rate48mb] = targetPowerOfdm->tPow2x[2];
 	ratesArray[rate54mb] = targetPowerOfdm->tPow2x[3];
@@ -2587,7 +2665,7 @@
 		if (!AR_SREV_9271(ah))
 			val &= ~AR_PCU_MISC_MODE2_HWWAR1;
 
-		if (AR_SREV_KIWI_11_OR_LATER(ah))
+		if (AR_SREV_KIWI_10_OR_LATER(ah))
 			val = val & (~AR_PCU_MISC_MODE2_HWWAR2);
 
 		OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, val);
@@ -2687,3 +2765,125 @@
 {
 	OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
 }
+
+#define	AR5416_IFS_SLOT_FULL_RATE_40	0x168	/* 9 us half, 40 MHz core clock (9*40) */
+#define	AR5416_IFS_SLOT_HALF_RATE_40	0x104	/* 13 us half, 20 MHz core clock (13*20) */
+#define	AR5416_IFS_SLOT_QUARTER_RATE_40	0xD2	/* 21 us quarter, 10 MHz core clock (21*10) */
+
+#define	AR5416_IFS_EIFS_FULL_RATE_40	0xE60	/* (74 + (2 * 9)) * 40MHz core clock */
+#define	AR5416_IFS_EIFS_HALF_RATE_40	0xDAC	/* (149 + (2 * 13)) * 20MHz core clock */
+#define	AR5416_IFS_EIFS_QUARTER_RATE_40	0xD48	/* (298 + (2 * 21)) * 10MHz core clock */
+
+#define	AR5416_IFS_SLOT_FULL_RATE_44	0x18c	/* 9 us half, 44 MHz core clock (9*44) */
+#define	AR5416_IFS_SLOT_HALF_RATE_44	0x11e	/* 13 us half, 22 MHz core clock (13*22) */
+#define	AR5416_IFS_SLOT_QUARTER_RATE_44	0xe7	/* 21 us quarter, 11 MHz core clock (21*11) */
+
+#define	AR5416_IFS_EIFS_FULL_RATE_44	0xfd0	/* (74 + (2 * 9)) * 44MHz core clock */
+#define	AR5416_IFS_EIFS_HALF_RATE_44	0xf0a	/* (149 + (2 * 13)) * 22MHz core clock */
+#define	AR5416_IFS_EIFS_QUARTER_RATE_44	0xe9c	/* (298 + (2 * 21)) * 11MHz core clock */
+
+#define	AR5416_INIT_USEC_40		40
+#define	AR5416_HALF_RATE_USEC_40	19 /* ((40 / 2) - 1 ) */
+#define	AR5416_QUARTER_RATE_USEC_40	9  /* ((40 / 4) - 1 ) */
+
+#define	AR5416_INIT_USEC_44		44
+#define	AR5416_HALF_RATE_USEC_44	21 /* ((44 / 2) - 1 ) */
+#define	AR5416_QUARTER_RATE_USEC_44	10  /* ((44 / 4) - 1 ) */
+
+
+/* XXX What should these be for 40/44MHz clocks (and half/quarter) ? */
+#define	AR5416_RX_NON_FULL_RATE_LATENCY		63
+#define	AR5416_TX_HALF_RATE_LATENCY		108
+#define	AR5416_TX_QUARTER_RATE_LATENCY		216
+
+/*
+ * Adjust various register settings based on half/quarter rate clock setting.
+ * This includes:
+ *
+ * + USEC, TX/RX latency,
+ * + IFS params: slot, eifs, misc etc.
+ *
+ * TODO:
+ *
+ * + Verify which other registers need to be tweaked;
+ * + Verify the behaviour of this for 5GHz fast and non-fast clock mode;
+ * + This just plain won't work for long distance links - the coverage class
+ *   code isn't aware of the slot/ifs/ACK/RTS timeout values that need to
+ *   change;
+ * + Verify whether the 32KHz USEC value needs to be kept for the 802.11n
+ *   series chips?
+ * + Calculate/derive values for 2GHz, 5GHz, 5GHz fast clock
+ */
+static void
+ar5416SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan)
+{
+	uint32_t txLat, rxLat, usec, slot, refClock, eifs, init_usec;
+	int clk_44 = 0;
+
+	HALASSERT(IEEE80211_IS_CHAN_HALF(chan) ||
+	    IEEE80211_IS_CHAN_QUARTER(chan));
+
+	/* 2GHz and 5GHz fast clock - 44MHz; else 40MHz */
+	if (IEEE80211_IS_CHAN_2GHZ(chan))
+		clk_44 = 1;
+	else if (IEEE80211_IS_CHAN_5GHZ(chan) &&
+	    IS_5GHZ_FAST_CLOCK_EN(ah, chan))
+		clk_44 = 1;
+
+	/* XXX does this need save/restoring for the 11n chips? */
+	refClock = OS_REG_READ(ah, AR_USEC) & AR_USEC_USEC32;
+
+	/*
+	 * XXX This really should calculate things, not use
+	 * hard coded values! Ew.
+	 */
+	if (IEEE80211_IS_CHAN_HALF(chan)) {
+		if (clk_44) {
+			slot = AR5416_IFS_SLOT_HALF_RATE_44;
+			rxLat = AR5416_RX_NON_FULL_RATE_LATENCY <<
+			    AR5416_USEC_RX_LAT_S;
+			txLat = AR5416_TX_HALF_RATE_LATENCY <<
+			    AR5416_USEC_TX_LAT_S;
+			usec = AR5416_HALF_RATE_USEC_44;
+			eifs = AR5416_IFS_EIFS_HALF_RATE_44;
+			init_usec = AR5416_INIT_USEC_44 >> 1;
+		} else {
+			slot = AR5416_IFS_SLOT_HALF_RATE_40;
+			rxLat = AR5416_RX_NON_FULL_RATE_LATENCY <<
+			    AR5416_USEC_RX_LAT_S;
+			txLat = AR5416_TX_HALF_RATE_LATENCY <<
+			    AR5416_USEC_TX_LAT_S;
+			usec = AR5416_HALF_RATE_USEC_40;
+			eifs = AR5416_IFS_EIFS_HALF_RATE_40;
+			init_usec = AR5416_INIT_USEC_40 >> 1;
+		}
+	} else { /* quarter rate */
+		if (clk_44) {
+			slot = AR5416_IFS_SLOT_QUARTER_RATE_44;
+			rxLat = AR5416_RX_NON_FULL_RATE_LATENCY <<
+			    AR5416_USEC_RX_LAT_S;
+			txLat = AR5416_TX_QUARTER_RATE_LATENCY <<
+			    AR5416_USEC_TX_LAT_S;
+			usec = AR5416_QUARTER_RATE_USEC_44;
+			eifs = AR5416_IFS_EIFS_QUARTER_RATE_44;
+			init_usec = AR5416_INIT_USEC_44 >> 2;
+		} else {
+			slot = AR5416_IFS_SLOT_QUARTER_RATE_40;
+			rxLat = AR5416_RX_NON_FULL_RATE_LATENCY <<
+			    AR5416_USEC_RX_LAT_S;
+			txLat = AR5416_TX_QUARTER_RATE_LATENCY <<
+			    AR5416_USEC_TX_LAT_S;
+			usec = AR5416_QUARTER_RATE_USEC_40;
+			eifs = AR5416_IFS_EIFS_QUARTER_RATE_40;
+			init_usec = AR5416_INIT_USEC_40 >> 2;
+		}
+	}
+
+	/* XXX verify these! */
+	OS_REG_WRITE(ah, AR_USEC, (usec | refClock | txLat | rxLat));
+	OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot);
+	OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs);
+	OS_REG_RMW_FIELD(ah, AR_D_GBL_IFS_MISC,
+	    AR_D_GBL_IFS_MISC_USEC_DURATION, init_usec);
+}
+

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c 249580 2013-04-17 07:31:53Z adrian $
  */
 #include "opt_ah.h"
 
@@ -135,6 +136,7 @@
 #define set11nRateFlags(_series, _index) \
         ((_series)[_index].RateFlags & HAL_RATESERIES_2040 ? AR_2040_##_index : 0) \
         |((_series)[_index].RateFlags & HAL_RATESERIES_HALFGI ? AR_GI##_index : 0) \
+        |((_series)[_index].RateFlags & HAL_RATESERIES_STBC ? AR_STBC##_index : 0) \
         |SM((_series)[_index].ChSel, AR_ChainSel##_index)
 
 /*
@@ -154,6 +156,169 @@
 /* NB: accept HT rates */
 #define	isValidTxRate(_r)	((1<<((_r) & 0x7f)) & VALID_TX_RATES)
 
+static inline int
+ar5416RateToRateTable(struct ath_hal *ah, uint8_t rate, HAL_BOOL is_ht40)
+{
+
+	/*
+	 * Handle the non-MCS rates
+	 */
+	switch (rate) {
+	case /*   1 Mb */ 0x1b:
+	case /*   1 MbS*/ 0x1b | 0x4:
+		return (AH5416(ah)->ah_ratesArray[rate1l]);
+	case /*   2 Mb */ 0x1a:
+		return (AH5416(ah)->ah_ratesArray[rate2l]);
+	case /*   2 MbS*/ 0x1a | 0x4:
+		return (AH5416(ah)->ah_ratesArray[rate2s]);
+	case /* 5.5 Mb */ 0x19:
+		return (AH5416(ah)->ah_ratesArray[rate5_5l]);
+	case /* 5.5 MbS*/ 0x19 | 0x4:
+		return (AH5416(ah)->ah_ratesArray[rate5_5s]);
+	case /*  11 Mb */ 0x18:
+		return (AH5416(ah)->ah_ratesArray[rate11l]);
+	case /*  11 MbS*/ 0x18 | 0x4:
+		return (AH5416(ah)->ah_ratesArray[rate11s]);
+	}
+
+	/* OFDM rates */
+	switch (rate) {
+	case /*   6 Mb */ 0x0b:
+		return (AH5416(ah)->ah_ratesArray[rate6mb]);
+	case /*   9 Mb */ 0x0f:
+		return (AH5416(ah)->ah_ratesArray[rate9mb]);
+	case /*  12 Mb */ 0x0a:
+		return (AH5416(ah)->ah_ratesArray[rate12mb]);
+	case /*  18 Mb */ 0x0e:
+		return (AH5416(ah)->ah_ratesArray[rate18mb]);
+	case /*  24 Mb */ 0x09:
+		return (AH5416(ah)->ah_ratesArray[rate24mb]);
+	case /*  36 Mb */ 0x0d:
+		return (AH5416(ah)->ah_ratesArray[rate36mb]);
+	case /*  48 Mb */ 0x08:
+		return (AH5416(ah)->ah_ratesArray[rate48mb]);
+	case /*  54 Mb */ 0x0c:
+		return (AH5416(ah)->ah_ratesArray[rate54mb]);
+	}
+
+	/*
+	 * Handle HT20/HT40 - we only have to do MCS0-7;
+	 * there's no stream differences.
+	 */
+	if ((rate & 0x80) && is_ht40) {
+		return (AH5416(ah)->ah_ratesArray[rateHt40_0 + (rate & 0x7)]);
+	} else if (rate & 0x80) {
+		return (AH5416(ah)->ah_ratesArray[rateHt20_0 + (rate & 0x7)]);
+	}
+
+	/* XXX default (eg XR, bad bad person!) */
+	return (AH5416(ah)->ah_ratesArray[rate6mb]);
+}
+
+/*
+ * Return the TX power to be used for the given rate/chains/TX power.
+ *
+ * There are a bunch of tweaks to make to a given TX power based on
+ * the current configuration, so...
+ */
+static uint16_t
+ar5416GetTxRatePower(struct ath_hal *ah, uint8_t rate, uint8_t tx_chainmask,
+    uint16_t txPower, HAL_BOOL is_ht40)
+{
+	int n_txpower, max_txpower;
+	const int cck_ofdm_delta = 2;
+#define	EEP_MINOR(_ah) \
+	(AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
+#define	IS_EEP_MINOR_V2(_ah)	(EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
+
+	/* Take a copy ; we may underflow and thus need to clamp things */
+	n_txpower = txPower;
+
+	/* HT40? Need to adjust the TX power by this */
+	if (is_ht40)
+		n_txpower += AH5416(ah)->ah_ht40PowerIncForPdadc;
+
+	/*
+	 * Merlin? Offset the target TX power offset - it defaults to
+	 * starting at -5.0dBm, but that can change!
+	 *
+	 * Kiwi/Kite? Always -5.0dBm offset.
+	 */
+	if (AR_SREV_KIWI_10_OR_LATER(ah)) {
+		n_txpower -= (AR5416_PWR_TABLE_OFFSET_DB * 2);
+	} else if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
+		int8_t pwr_table_offset = 0;
+		/* This is in dBm, convert to 1/2 dBm */
+		(void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET,
+		    &pwr_table_offset);
+		n_txpower -= (pwr_table_offset * 2);
+	}
+
+	/*
+	 * If Open-loop TX power control is used, the CCK rates need
+	 * to be offset by that.
+	 *
+	 * Rates: 2S, 2L, 1S, 1L, 5.5S, 5.5L
+	 *
+	 * XXX Odd, we don't have a PHY table entry for long preamble
+	 * 1mbit CCK?
+	 */
+	if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
+	    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
+
+		if (rate == 0x19 || rate == 0x1a || rate == 0x1b ||
+		    rate == (0x19 | 0x04) || rate == (0x1a | 0x04) ||
+		    rate == (0x1b | 0x04)) {
+			n_txpower -= cck_ofdm_delta;
+		}
+	}
+
+	/*
+	 * We're now offset by the same amount that the static maximum
+	 * PHY power tables are.  So, clamp the value based on that rate.
+	 */
+	max_txpower = ar5416RateToRateTable(ah, rate, is_ht40);
+#if 0
+	ath_hal_printf(ah, "%s: n_txpower = %d, max_txpower = %d, "
+	    "rate = 0x%x , is_ht40 = %d\n",
+	    __func__,
+	    n_txpower,
+	    max_txpower,
+	    rate,
+	    is_ht40);
+#endif
+	n_txpower = MIN(max_txpower, n_txpower);
+
+	/*
+	 * We don't have to offset the TX power for two or three
+	 * chain operation here - it's done by the AR_PHY_POWER_TX_SUB
+	 * register setting via the EEPROM.
+	 *
+	 * So for vendors that programmed the maximum target power assuming
+	 * that 2/3 chains are always on, things will just plain work.
+	 * (They won't reach that target power if only one chain is on, but
+	 * that's a different problem.)
+	 */
+
+	/* Over/underflow? Adjust */
+	if (n_txpower < 0)
+		n_txpower = 0;
+	else if (n_txpower > 63)
+		n_txpower = 63;
+
+	/*
+	 * For some odd reason the AR9160 with txpower=0 results in a
+	 * much higher (max?) TX power.  So, if it's a chipset before
+	 * AR9220/AR9280, just clamp the minimum value at 1.
+	 */
+	if ((! AR_SREV_MERLIN_10_OR_LATER(ah)) && (n_txpower == 0))
+		n_txpower = 1;
+
+	return (n_txpower);
+#undef	EEP_MINOR
+#undef	IS_EEP_MINOR_V2
+}
+
 HAL_BOOL
 ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds,
 	u_int pktLen,
@@ -186,6 +351,16 @@
         if (txPower > 63)
 		txPower = 63;
 
+	/*
+	 * XXX For now, just assume that this isn't a HT40 frame.
+	 */
+	if (AH5212(ah)->ah_tpcEnabled) {
+		txPower = ar5416GetTxRatePower(ah, txRate0,
+		    ahp->ah_tx_chainmask,
+		    txPower,
+		    AH_FALSE);
+	}
+
 	ads->ds_ctl0 = (pktLen & AR_FrameLen)
 		     | (txPower << AR_XmitPower_S)
 		     | (flags & HAL_TXDESC_VEOL ? AR_VEOL : 0)
@@ -237,6 +412,8 @@
 	 * Set the TX antenna to 0 for Kite
 	 * To preserve existing behaviour, also set the TPC bits to 0;
 	 * when TPC is enabled these should be filled in appropriately.
+	 *
+	 * XXX TODO: when doing TPC, set the TX power up appropriately?
 	 */
 	if (AR_SREV_KITE(ah)) {
 		ads->ds_ctl8 = SM(0, AR_AntCtl0);
@@ -276,13 +453,17 @@
 
 HAL_BOOL
 ar5416FillTxDesc(struct ath_hal *ah, struct ath_desc *ds,
-	u_int segLen, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
+	HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int descId,
+	u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg,
 	const struct ath_desc *ds0)
 {
 	struct ar5416_desc *ads = AR5416DESC(ds);
+	uint32_t segLen = segLenList[0];
 
 	HALASSERT((segLen &~ AR_BufLen) == 0);
 
+	ds->ds_data = bufAddrList[0];
+
 	if (firstSeg) {
 		/*
 		 * First descriptor, don't clobber xmit control data
@@ -295,20 +476,33 @@
 		 * copy the multi-rate transmit parameters from
 		 * the first frame for processing on completion. 
 		 */
-		ads->ds_ctl0 = 0;
 		ads->ds_ctl1 = segLen;
 #ifdef AH_NEED_DESC_SWAP
+		ads->ds_ctl0 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl0)
+		    & AR_TxIntrReq;
 		ads->ds_ctl2 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl2);
 		ads->ds_ctl3 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl3);
+		/* ctl6 - we only need encrtype; the rest are blank */
+		ads->ds_ctl6 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType);
 #else
+		ads->ds_ctl0 = AR5416DESC_CONST(ds0)->ds_ctl0 & AR_TxIntrReq;
 		ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2;
 		ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3;
+		/* ctl6 - we only need encrtype; the rest are blank */
+		ads->ds_ctl6 = AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType;
 #endif
 	} else {			/* !firstSeg && !lastSeg */
 		/*
 		 * Intermediate descriptor in a multi-descriptor frame.
 		 */
-		ads->ds_ctl0 = 0;
+#ifdef AH_NEED_DESC_SWAP
+		ads->ds_ctl0 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl0)
+		    & AR_TxIntrReq;
+		ads->ds_ctl6 = __bswap32(AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType);
+#else
+		ads->ds_ctl0 = AR5416DESC_CONST(ds0)->ds_ctl0 & AR_TxIntrReq;
+		ads->ds_ctl6 = AR5416DESC_CONST(ds0)->ds_ctl6 & AR_EncrType;
+#endif
 		ads->ds_ctl1 = segLen | AR_TxMore;
 		ads->ds_ctl2 = 0;
 		ads->ds_ctl3 = 0;
@@ -318,8 +512,13 @@
 	return AH_TRUE;
 }
 
+/*
+ * NB: cipher is no longer used, it's calculated.
+ */
 HAL_BOOL
 ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds,
+	HAL_DMA_ADDR *bufAddrList,
+	uint32_t *segLenList,
 	u_int pktLen,
 	u_int hdrLen,
 	HAL_PKT_TYPE type,
@@ -326,34 +525,60 @@
 	u_int keyIx,
 	HAL_CIPHER cipher,
 	uint8_t delims,
-	u_int segLen,
 	HAL_BOOL firstSeg,
-	HAL_BOOL lastSeg)
+	HAL_BOOL lastSeg,
+	HAL_BOOL lastAggr)
 {
 	struct ar5416_desc *ads = AR5416DESC(ds);
 	uint32_t *ds_txstatus = AR5416_DS_TXSTATUS(ah,ads);
 	struct ath_hal_5416 *ahp = AH5416(ah);
+	u_int segLen = segLenList[0];
 
 	int isaggr = 0;
+	uint32_t last_aggr = 0;
 	
 	(void) hdrLen;
 	(void) ah;
 
 	HALASSERT((segLen &~ AR_BufLen) == 0);
+	ds->ds_data = bufAddrList[0];
 
 	HALASSERT(isValidPktType(type));
 	if (type == HAL_PKT_TYPE_AMPDU) {
 		type = HAL_PKT_TYPE_NORMAL;
 		isaggr = 1;
+		if (lastAggr == AH_FALSE)
+			last_aggr = AR_MoreAggr;
 	}
 
-	if (!firstSeg) {
-		OS_MEMZERO(ds->ds_hw, AR5416_DESC_TX_CTL_SZ);
-	}
+	/*
+	 * Since this function is called before any of the other
+	 * descriptor setup functions (at least in this particular
+	 * 802.11n aggregation implementation), always bzero() the
+	 * descriptor. Previously this would be done for all but
+	 * the first segment.
+	 * XXX TODO: figure out why; perhaps I'm using this slightly
+	 * XXX incorrectly.
+	 */
+	OS_MEMZERO(ds->ds_hw, AR5416_DESC_TX_CTL_SZ);
 
+	/*
+	 * Note: VEOL should only be for the last descriptor in the chain.
+	 */
 	ads->ds_ctl0 = (pktLen & AR_FrameLen);
+
+	/*
+	 * For aggregates:
+	 * + IsAggr must be set for all descriptors of all subframes of
+	 *   the aggregate
+	 * + MoreAggr must be set for all descriptors of all subframes
+	 *   of the aggregate EXCEPT the last subframe;
+	 * + MoreAggr must be _CLEAR_ for all descrpitors of the last
+	 *   subframe of the aggregate.
+	 */
 	ads->ds_ctl1 = (type << AR_FrameType_S)
-			| (isaggr ? (AR_IsAggr | AR_MoreAggr) : 0);
+			| (isaggr ? (AR_IsAggr | last_aggr) : 0);
+
 	ads->ds_ctl2 = 0;
 	ads->ds_ctl3 = 0;
 	if (keyIx != HAL_TXKEYIX_INVALID) {
@@ -362,7 +587,7 @@
 		ads->ds_ctl0 |= AR_DestIdxValid;
 	}
 
-	ads->ds_ctl6 = SM(ahp->ah_keytype[cipher], AR_EncrType);
+	ads->ds_ctl6 |= SM(ahp->ah_keytype[keyIx], AR_EncrType);
 	if (isaggr) {
 		ads->ds_ctl6 |= SM(delims, AR_PadDelim);
 	}
@@ -463,7 +688,6 @@
 	ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2;
 	ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3;
 #endif
-	
 	return AH_TRUE;
 }
 
@@ -504,6 +728,7 @@
 	/* Update software copies of the HW status */
 	ts->ts_seqnum = MS(ds_txstatus[9], AR_SeqNum);
 	ts->ts_tstamp = AR_SendTimestamp(ds_txstatus);
+	ts->ts_tid = MS(ds_txstatus[9], AR_TxTid);
 
 	ts->ts_status = 0;
 	if (ds_txstatus[1] & AR_ExcessiveRetries)
@@ -620,6 +845,26 @@
 	return MS(OS_REG_READ(ah, AR_GTXTO), AR_GTXTO_TIMEOUT_LIMIT);
 }
 
+#define	HT_RC_2_MCS(_rc)	((_rc) & 0x0f)
+static const u_int8_t baDurationDelta[] = {
+	24,	//  0: BPSK
+	12,	//  1: QPSK 1/2
+	12,	//  2: QPSK 3/4
+	4,	//  3: 16-QAM 1/2
+	4,	//  4: 16-QAM 3/4
+	4,	//  5: 64-QAM 2/3
+	4,	//  6: 64-QAM 3/4
+	4,	//  7: 64-QAM 5/6
+	24,	//  8: BPSK
+	12,	//  9: QPSK 1/2
+	12,	// 10: QPSK 3/4
+	4,	// 11: 16-QAM 1/2
+	4,	// 12: 16-QAM 3/4
+	4,	// 13: 64-QAM 2/3
+	4,	// 14: 64-QAM 3/4
+	4,	// 15: 64-QAM 5/6
+};
+
 void
 ar5416Set11nRateScenario(struct ath_hal *ah, struct ath_desc *ds,
         u_int durUpdateEn, u_int rtsctsRate,
@@ -632,20 +877,6 @@
 	(void)nseries;
 
 	/*
-	 * XXX since the upper layers doesn't know the current chainmask
-	 * XXX setup, just override its decisions here.
-	 * XXX The upper layers need to be taught this!
-	 */
-	if (series[0].Tries != 0)
-		series[0].ChSel = AH5416(ah)->ah_tx_chainmask;
-	if (series[1].Tries != 0)
-		series[1].ChSel = AH5416(ah)->ah_tx_chainmask;
-	if (series[2].Tries != 0)
-		series[2].ChSel = AH5416(ah)->ah_tx_chainmask;
-	if (series[3].Tries != 0)
-		series[3].ChSel = AH5416(ah)->ah_tx_chainmask;
-
-	/*
 	 * Only one of RTS and CTS enable must be set.
 	 * If a frame has both set, just do RTS protection -
 	 * that's enough to satisfy legacy protection.
@@ -689,9 +920,91 @@
 		     | set11nRateFlags(series, 2)
 		     | set11nRateFlags(series, 3)
 		     | SM(rtsctsRate, AR_RTSCTSRate);
+
+	/*
+	 * Doing per-packet TPC - update the TX power for the first
+	 * field; program in the other series.
+	 */
+	if (AH5212(ah)->ah_tpcEnabled) {
+		uint32_t ds_ctl0;
+		uint16_t txPower;
+
+		/* Modify the tx power field for rate 0 */
+		txPower = ar5416GetTxRatePower(ah, series[0].Rate,
+		    series[0].ChSel,
+		    series[0].tx_power_cap,
+		    !! (series[0].RateFlags & HAL_RATESERIES_2040));
+		ds_ctl0 = ads->ds_ctl0 & ~AR_XmitPower;
+		ds_ctl0 |= (txPower << AR_XmitPower_S);
+		ads->ds_ctl0 = ds_ctl0;
+
+		/*
+		 * Override the whole descriptor field for each TX power.
+		 *
+		 * This will need changing if we ever support antenna control
+		 * programming.
+		 */
+		txPower = ar5416GetTxRatePower(ah, series[1].Rate,
+		    series[1].ChSel,
+		    series[1].tx_power_cap,
+		    !! (series[1].RateFlags & HAL_RATESERIES_2040));
+		ads->ds_ctl9 = SM(0, AR_AntCtl1) | SM(txPower, AR_XmitPower1);
+
+		txPower = ar5416GetTxRatePower(ah, series[2].Rate,
+		    series[2].ChSel,
+		    series[2].tx_power_cap,
+		    !! (series[2].RateFlags & HAL_RATESERIES_2040));
+		ads->ds_ctl10 = SM(0, AR_AntCtl2) | SM(txPower, AR_XmitPower2);
+
+		txPower = ar5416GetTxRatePower(ah, series[3].Rate,
+		    series[3].ChSel,
+		    series[3].tx_power_cap,
+		    !! (series[3].RateFlags & HAL_RATESERIES_2040));
+		ads->ds_ctl11 = SM(0, AR_AntCtl3) | SM(txPower, AR_XmitPower3);
+	}
 }
 
+/*
+ * Note: this should be called before calling ar5416SetBurstDuration()
+ * (if it is indeed called) in order to ensure that the burst duration
+ * is correctly updated with the BA delta workaround.
+ */
 void
+ar5416Set11nAggrFirst(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen,
+    u_int numDelims)
+{
+	struct ar5416_desc *ads = AR5416DESC(ds);
+	uint32_t flags;
+	uint32_t burstDur;
+	uint8_t rate;
+
+	ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
+
+	ads->ds_ctl6 &= ~(AR_AggrLen | AR_PadDelim);
+	ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen);
+	ads->ds_ctl6 |= SM(numDelims, AR_PadDelim);
+
+	if (! AR_SREV_MERLIN_10_OR_LATER(ah)) {
+		/*
+		 * XXX It'd be nice if I were passed in the rate scenario
+		 * at this point..
+		 */
+		rate = MS(ads->ds_ctl3, AR_XmitRate0);
+		flags = ads->ds_ctl0 & (AR_CTSEnable | AR_RTSEnable);
+		/*
+		 * WAR - MAC assumes normal ACK time instead of
+		 * block ACK while computing packet duration.
+		 * Add this delta to the burst duration in the descriptor.
+		 */
+		if (flags && (ads->ds_ctl1 & AR_IsAggr)) {
+			burstDur = baDurationDelta[HT_RC_2_MCS(rate)];
+			ads->ds_ctl2 &= ~(AR_BurstDur);
+			ads->ds_ctl2 |= SM(burstDur, AR_BurstDur);
+		}
+	}
+}
+
+void
 ar5416Set11nAggrMiddle(struct ath_hal *ah, struct ath_desc *ds, u_int numDelims)
 {
 	struct ar5416_desc *ads = AR5416DESC(ds);
@@ -711,6 +1024,16 @@
 }
 
 void
+ar5416Set11nAggrLast(struct ath_hal *ah, struct ath_desc *ds)
+{
+	struct ar5416_desc *ads = AR5416DESC(ds);
+
+	ads->ds_ctl1 |= AR_IsAggr;
+	ads->ds_ctl1 &= ~AR_MoreAggr;
+	ads->ds_ctl6 &= ~AR_PadDelim;
+}
+
+void
 ar5416Clr11nAggr(struct ath_hal *ah, struct ath_desc *ds)
 {
 	struct ar5416_desc *ads = AR5416DESC(ds);
@@ -721,13 +1044,46 @@
 }
 
 void
+ar5416Set11nVirtualMoreFrag(struct ath_hal *ah, struct ath_desc *ds,
+    u_int vmf)
+{
+	struct ar5416_desc *ads = AR5416DESC(ds);
+	if (vmf)
+		ads->ds_ctl0 |= AR_VirtMoreFrag;
+	else
+		ads->ds_ctl0 &= ~AR_VirtMoreFrag;
+}
+
+/*
+ * Program the burst duration, with the included BA delta if it's
+ * applicable.
+ */
+void
 ar5416Set11nBurstDuration(struct ath_hal *ah, struct ath_desc *ds,
                                                   u_int burstDuration)
 {
 	struct ar5416_desc *ads = AR5416DESC(ds);
+	uint32_t burstDur = 0;
+	uint8_t rate;
 
+	if (! AR_SREV_MERLIN_10_OR_LATER(ah)) {
+		/*
+		 * XXX It'd be nice if I were passed in the rate scenario
+		 * at this point..
+		 */
+		rate = MS(ads->ds_ctl3, AR_XmitDataTries0);
+		/*
+		 * WAR - MAC assumes normal ACK time instead of
+		 * block ACK while computing packet duration.
+		 * Add this delta to the burst duration in the descriptor.
+		 */
+		if (ads->ds_ctl1 & AR_IsAggr) {
+			burstDur = baDurationDelta[HT_RC_2_MCS(rate)];
+		}
+	}
+
 	ads->ds_ctl2 &= ~AR_BurstDur;
-	ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur);
+	ads->ds_ctl2 |= SM(burstDur + burstDuration, AR_BurstDur);
 }
 
 /*
@@ -879,6 +1235,7 @@
  * Assumes:
  *  phwChannel has been set to point to the current channel
  */
+#define	TU_TO_USEC(_tu)		((_tu) << 10)
 HAL_BOOL
 ar5416ResetTxQueue(struct ath_hal *ah, u_int q)
 {
@@ -886,7 +1243,7 @@
 	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
 	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
 	HAL_TX_QUEUE_INFO *qi;
-	uint32_t cwMin, chanCwMin, value, qmisc, dmisc;
+	uint32_t cwMin, chanCwMin, qmisc, dmisc;
 
 	if (q >= pCap->halTotalQueues) {
 		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n",
@@ -956,7 +1313,8 @@
 		if (qi->tqi_cbrOverflowLimit)
 			qmisc |= AR_Q_MISC_CBR_EXP_CNTR_LIMIT;
 	}
-	if (qi->tqi_readyTime) {
+
+	if (qi->tqi_readyTime && (qi->tqi_type != HAL_TX_QUEUE_CAB)) {
 		OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
 			  SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT)
 			| AR_Q_RDYTIMECFG_ENA);
@@ -1027,18 +1385,46 @@
 		qmisc |= AR_Q_MISC_FSP_DBA_GATED
 		      |  AR_Q_MISC_CBR_INCR_DIS1
 		      |  AR_Q_MISC_CBR_INCR_DIS0;
-
-		if (!qi->tqi_readyTime) {
+		HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: CAB: tqi_readyTime = %d\n",
+		    __func__, qi->tqi_readyTime);
+		if (qi->tqi_readyTime) {
+			HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
+			    "%s: using tqi_readyTime\n", __func__);
+			OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
+			    SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT) |
+			    AR_Q_RDYTIMECFG_ENA);
+		} else {
+			int value;
 			/*
 			 * NB: don't set default ready time if driver
 			 * has explicitly specified something.  This is
 			 * here solely for backwards compatibility.
 			 */
-			value = (ahp->ah_beaconInterval
-				- (ah->ah_config.ah_sw_beacon_response_time -
-					ah->ah_config.ah_dma_beacon_response_time)
-				- ah->ah_config.ah_additional_swba_backoff) * 1024;
-			OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_ENA);
+			/*
+			 * XXX for now, hard-code a CAB interval of 70%
+			 * XXX of the total beacon interval.
+			 *
+			 * XXX This keeps Merlin and later based MACs
+			 * XXX quite a bit happier (stops stuck beacons,
+			 * XXX which I gather is because of such a long
+			 * XXX cabq time.)
+			 */
+			value = (ahp->ah_beaconInterval * 50 / 100)
+				- ah->ah_config.ah_additional_swba_backoff
+				- ah->ah_config.ah_sw_beacon_response_time
+				+ ah->ah_config.ah_dma_beacon_response_time;
+			/*
+			 * XXX Ensure it isn't too low - nothing lower
+			 * XXX than 10 TU
+			 */
+			if (value < 10)
+				value = 10;
+			HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
+			    "%s: defaulting to rdytime = %d uS\n",
+			    __func__, value);
+			OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
+			    SM(TU_TO_USEC(value), AR_Q_RDYTIMECFG_INT) |
+			    AR_Q_RDYTIMECFG_ENA);
 		}
 		dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
 			    AR_D_MISC_ARB_LOCKOUT_CNTRL);
@@ -1106,3 +1492,4 @@
 
 	return AH_TRUE;
 }
+#undef	TU_TO_USEC

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416desc.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416desc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416desc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,15 +15,14 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416desc.h 250346 2013-05-08 01:11:25Z adrian $
  */
-#ifndef _ATH_AR5416_DESC_H
-#define _ATH_AR5416_DESC_H
+#ifndef _ATH_AR5416_DESC_H_
+#define _ATH_AR5416_DESC_H_
 
 /*
  * Hardware-specific descriptor structures.
  */
-#include "ah_desc.h"
 
 /* XXX Need to replace this with a dynamic 
  * method of determining Owl2 if possible 
@@ -302,6 +302,8 @@
 #define AR_FinalTxIdx_S     21
 #define AR_TxStatusRsvd82   0x01800000
 #define AR_PowerMgmt        0x02000000
+#define AR_TxTid            0xf0000000
+#define AR_TxTid_S          28
 #define AR_TxStatusRsvd83   0xfc000000
 
 /***********
@@ -356,6 +358,7 @@
 #define AR_RxStatusRsvd30   0xfffff800
 /* Owl 2.x only */
 #define AR_DupFrame	    0x00000004
+#define AR_STBCFrame        0x00000008
 #define AR_RxAntenna        0xffffff00
 #define AR_RxAntenna_S      8
 

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416phy.h 244943 2013-01-02 00:38:01Z adrian $
  */
 #ifndef _DEV_ATH_AR5416PHY_H_
 #define _DEV_ATH_AR5416PHY_H_
@@ -21,6 +22,73 @@
 
 #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
+
+#define	AR_PHY_SPECTRAL_SCAN		0x9910
+#define	AR_PHY_SPECTRAL_SCAN_ENA	0x00000001
+#define	AR_PHY_SPECTRAL_SCAN_ENA_S	0
+#define	AR_PHY_SPECTRAL_SCAN_ACTIVE	0x00000002
+#define	AR_PHY_SPECTRAL_SCAN_ACTIVE_S	1
+#define	AR_PHY_SPECTRAL_SCAN_FFT_PERIOD	0x000000F0
+#define	AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S	4
+#define	AR_PHY_SPECTRAL_SCAN_PERIOD	0x0000FF00
+#define	AR_PHY_SPECTRAL_SCAN_PERIOD_S	8
+
+/* Scan count and Short repeat flags are different for Kiwi and Merlin */
+#define	AR_PHY_SPECTRAL_SCAN_COUNT	0x00FF0000
+#define	AR_PHY_SPECTRAL_SCAN_COUNT_S	16
+#define	AR_PHY_SPECTRAL_SCAN_COUNT_KIWI	0x0FFF0000
+#define	AR_PHY_SPECTRAL_SCAN_COUNT_KIWI_S	16
+
+#define	AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT	0x01000000
+#define	AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S	24
+#define	AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI	0x10000000
+#define	AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI_S	28
+
+/*
+ * Kiwi only, bit 30 is used to set the error type, if set it is 0x5 (HAL_PHYERR_RADAR)
+ * Else it is 38 (new error type)
+ */
+#define	AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT_KIWI	0x40000000  /* Spectral Error select bit mask */
+#define	AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT_KIWI_S	30  /* Spectral Error select bit 30 */
+
+#define	AR_PHY_SPECTRAL_SCAN_PRIORITY_SELECT_KIWI	0x20000000  /* Spectral Error select bit mask */
+#define	AR_PHY_SPECTRAL_SCAN_PRIORITY_SELECT_SELECT_KIWI_S	29  /* Spectral Error select bit 30 */
+
 /* For AR_PHY_RADAR0 */
 #define	AR_PHY_RADAR_0_FFT_ENA		0x80000000
 
@@ -28,6 +96,8 @@
 #define	AR_PHY_RADAR_EXT_ENA		0x00004000
 
 #define	AR_PHY_RADAR_1			0x9958
+#define	AR_PHY_RADAR_1_BIN_THRESH_SELECT	0x07000000
+#define	AR_PHY_RADAR_1_BIN_THRESH_SELECT_S	24
 #define	AR_PHY_RADAR_1_RELPWR_ENA	0x00800000
 #define	AR_PHY_RADAR_1_USE_FIR128	0x00400000
 #define	AR_PHY_RADAR_1_RELPWR_THRESH	0x003F0000
@@ -121,12 +191,6 @@
 #define AR_PHY_EXT_MINCCA_PWR_S 23
 #define AR_PHY_EXT_CCA_THRESH62	0x007F0000
 #define AR_PHY_EXT_CCA_THRESH62_S	16
-/*
- * This duplicates AR_PHY_EXT_CCA_CYCPWR_THR1; it reads more like
- * an ANI register this way.
- */
-#define	AR_PHY_EXT_TIMING5_CYCPWR_THR1		0x0000FE00
-#define	AR_PHY_EXT_TIMING5_CYCPWR_THR1_S	9
 
 #define AR9280_PHY_EXT_MINCCA_PWR       0x01FF0000
 #define AR9280_PHY_EXT_MINCCA_PWR_S     16
@@ -156,8 +220,14 @@
 #define	AR_PHY_CAL_MEAS_0(_i)	(0x9c10 + ((_i) << 12))
 #define	AR_PHY_CAL_MEAS_1(_i)	(0x9c14 + ((_i) << 12))
 #define	AR_PHY_CAL_MEAS_2(_i)	(0x9c18 + ((_i) << 12))
+/* This is AR9130 and later */
 #define	AR_PHY_CAL_MEAS_3(_i)	(0x9c1c + ((_i) << 12))
 
+/*
+ * AR5416 still uses AR_PHY(263) for current RSSI;
+ * AR9130 and later uses AR_PHY(271).
+ */
+#define	AR9130_PHY_CURRENT_RSSI	0x9c3c		/* rssi of current frame rx'd */
 
 #define AR_PHY_CCA          0x9864
 #define AR_PHY_MINCCA_PWR   0x0FF80000

Modified: trunk/sys/dev/ath/ath_hal/ar5416/ar5416reg.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar5416/ar5416reg.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar5416/ar5416reg.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar5416/ar5416reg.h 249516 2013-04-15 17:58:11Z adrian $
  */
 #ifndef _DEV_ATH_AR5416REG_H
 #define	_DEV_ATH_AR5416REG_H
@@ -47,14 +48,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
@@ -79,6 +124,12 @@
 #define	AR_RTC_DERIVED_CLK_PERIOD_S	1
 #endif	/* AH_SUPPORT_AR9130 */
 
+/* AR_USEC: 0x801c */
+#define	AR5416_USEC_TX_LAT	0x007FC000	/* tx latency to start of SIGNAL (usec) */
+#define	AR5416_USEC_TX_LAT_S	14		/* tx latency to start of SIGNAL (usec) */
+#define	AR5416_USEC_RX_LAT	0x1F800000	/* rx latency to start of SIGNAL (usec) */
+#define	AR5416_USEC_RX_LAT_S	23		/* rx latency to start of SIGNAL (usec) */
+
 #define	AR_RESET_TSF		0x8020
 
 /*
@@ -95,6 +146,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
@@ -128,6 +180,7 @@
 #define	AR_2040_MODE		0x8318
 #define	AR_EXTRCCNT		0x8328	/* extension channel rx clear count */
 #define	AR_SELFGEN_MASK		0x832c	/* rx and cal chain masks */
+#define	AR_PHY_ERR_MASK_REG	0x8338
 #define	AR_PCU_TXBUF_CTRL	0x8340
 #define	AR_PCU_MISC_MODE2	0x8344
 
@@ -203,18 +256,23 @@
 #define	AR_MAC_LED_MODE_POWON	5	/* Power LED on (s/w control) */
 #define	AR_MAC_LED_MODE_NETON	6	/* Network LED on (s/w control) */
 #define	AR_MAC_LED_ASSOC	0x00000c00
-#define	AR_MAC_LED_ASSOC_NONE	0x00000000 /* STA is not associated or trying */
-#define	AR_MAC_LED_ASSOC_ACTIVE	0x00000400 /* STA is associated */
-#define	AR_MAC_LED_ASSOC_PEND	0x00000800 /* STA is trying to associate */
+#define	AR_MAC_LED_ASSOC_NONE	0x0	/* STA is not associated or trying */
+#define	AR_MAC_LED_ASSOC_ACTIVE	0x1	/* STA is associated */
+#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_BIT22		0x00400000
+#define	AR_WA_BIT23		0x00800000
 
 #define	AR_WA_DEFAULT		0x0000073f
-#define	AR9280_WA_DEFAULT	0x0040073f
+#define	AR9280_WA_DEFAULT	0x0040073b	/* disable bit 2, see commit */
 #define	AR9285_WA_DEFAULT	0x004a05cb
 
 #define	AR_PCIE_PM_CTRL_ENA	0x00080000
@@ -250,6 +308,7 @@
 /* Interrupts */
 #define	AR_ISR_TXMINTR		0x00080000	/* Maximum interrupt tx rate */
 #define	AR_ISR_RXMINTR		0x01000000	/* Maximum interrupt rx rate */
+#define	AR_ISR_GENTMR		0x10000000	/* OR of generic timer bits in S5 */
 #define	AR_ISR_TXINTM		0x40000000	/* Tx int after mitigation */
 #define	AR_ISR_RXINTM		0x80000000	/* Rx int after mitigation */
 
@@ -259,7 +318,13 @@
 
 #define	AR_ISR_S5		0x0098
 #define	AR_ISR_S5_S		0x00d8
-#define	AR_ISR_S5_TIM_TIMER	0x00000010
+#define	AR_ISR_S5_GENTIMER7	0x00000080 // Mask for timer 7 trigger
+#define	AR_ISR_S5_TIM_TIMER	0x00000010 // TIM Timer ISR
+#define	AR_ISR_S5_DTIM_TIMER	0x00000020 // DTIM Timer ISR
+#define	AR_ISR_S5_GENTIMER_TRIG	0x0000FF80 // ISR for generic timer trigger 7-15
+#define	AR_ISR_S5_GENTIMER_TRIG_S	0
+#define	AR_ISR_S5_GENTIMER_THRESH	0xFF800000 // ISR for generic timer threshold 7-15
+#define	AR_ISR_S5_GENTIMER_THRESH_S	16
 
 #define	AR_INTR_SPURIOUS	0xffffffff
 #define	AR_INTR_RTC_IRQ		0x00000001	/* rtc in shutdown state */
@@ -452,9 +517,12 @@
 #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 */
+#define	AR_PCU_SEL_EVM			0x08000000 /* select EVM data or PLCP header */
 
 #define	AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE		0x00000002
 #define	AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT	0x00000004
@@ -497,6 +565,8 @@
 #define	AR928X_GPIO_IN_VAL_S	10
 #define	AR9285_GPIO_IN_VAL	0x00FFF000
 #define	AR9285_GPIO_IN_VAL_S	12
+#define	AR9287_GPIO_IN_VAL	0x003FF800
+#define	AR9287_GPIO_IN_VAL_S	11
 
 #define	AR_GPIO_OE_OUT_DRV	0x3	/* 2 bit mask shifted by 2*bitpos */
 #define	AR_GPIO_OE_OUT_DRV_NO	0x0	/* tristate */
@@ -534,6 +604,25 @@
 #define	AR_EEPROM_STATUS_DATA_PROT_ACCESS   0x00040000
 #define	AR_EEPROM_STATUS_DATA_ABSENT_ACCESS 0x00080000
 
+/* K2 (9271) */
+#define	AR9271_CLOCK_CONTROL		0x50040
+#define	AR9271_CLOCK_SELECTION_22	0x0
+#define	AR9271_CLOCK_SELECTION_88	0x1
+#define	AR9271_CLOCK_SELECTION_44	0x2
+#define	AR9271_CLOCK_SELECTION_117	0x4
+#define	AR9271_CLOCK_SELECTION_OSC_40	0x6
+#define	AR9271_CLOCK_SELECTION_RTC	0x7
+#define	AR9271_SPI_SEL			0x100
+#define	AR9271_UART_SEL			0x200
+
+#define	AR9271_RESET_POWER_DOWN_CONTROL	0x50044
+#define	AR9271_RADIO_RF_RST		0x20
+#define	AR9271_GATE_MAC_CTL		0x4000
+#define	AR9271_MAIN_PLL_PWD_CTL		0x40000
+
+#define	AR9271_CLKMISC			0x4090
+#define	AR9271_OSC_to_10M_EN		0x00000001
+
 /*
  * AR5212 defines the MAC revision mask as 0xF, but both ath9k and
  * the Atheros HAL define it as 0x7. 
@@ -605,10 +694,10 @@
 #define	AR_XSREV_REVISION_KITE_11	1	/* Kite 1.1 */
 #define	AR_XSREV_REVISION_KITE_12	2	/* Kite 1.2 */
 #define	AR_XSREV_VERSION_KIWI		0x180	/* Kiwi (AR9287) */
-#define	AR_XSREV_REVISION_KIWI_10	0
-#define	AR_XSREV_REVISION_KIWI_11	1
-#define	AR_XSREV_REVISION_KIWI_12	2
-#define	AR_XSREV_REVISION_KIWI_13	3
+#define	AR_XSREV_REVISION_KIWI_10	0	/* Kiwi 1.0 */
+#define	AR_XSREV_REVISION_KIWI_11	1	/* Kiwi 1.1 */
+#define	AR_XSREV_REVISION_KIWI_12	2	/* Kiwi 1.2 */
+#define	AR_XSREV_REVISION_KIWI_13	3	/* Kiwi 1.3 */
 
 /* Owl (AR5416) */
 #define	AR_SREV_OWL(_ah) \
@@ -694,6 +783,10 @@
 #define AR_SREV_KIWI(_ah) \
 	(AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_KIWI)
 
+#define AR_SREV_KIWI_10_OR_LATER(_ah) \
+	(AH_PRIVATE((_ah))->ah_macVersion >= AR_XSREV_VERSION_KIWI)
+
+/* XXX TODO: make these handle macVersion > Kiwi */
 #define AR_SREV_KIWI_11_OR_LATER(_ah) \
 	(AR_SREV_KIWI(_ah) && \
 	 AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_KIWI_11)

Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9130.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9130.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9130.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9130.ini 221163 2011-04-28 12:47:40Z adrian $
  */
 
 static const uint32_t ar5416Modes_9100[][6] = {


Property changes on: trunk/sys/dev/ath/ath_hal/ar9001/ar9130.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c 242412 2012-10-31 21:14:25Z adrian $
  */
 #include "opt_ah.h"
 
@@ -78,13 +79,13 @@
 	HAL_STATUS ecode;
 	HAL_BOOL rfStatus;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 	    __func__, sc, (void*) st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
 	ahp5416 = ath_hal_malloc(sizeof (struct ath_hal_5416));
 	if (ahp5416 == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		*status = HAL_ENOMEM;
 		return AH_NULL;
@@ -119,11 +120,11 @@
 
 	/*
 	 * Use the "local" EEPROM data given to us by the higher layers.
-	 * This is a private copy out of system flash. The Linux ath9k
-	 * commit for the initial AR9130 support mentions MMIO flash
-	 * access is "unreliable." -adrian
+	 * This is a private copy out of system flash.
+	 * By this stage the SoC SPI flash may have disabled the memory-
+	 * mapping and rely purely on port-based SPI IO.
 	 */
-	AH_PRIVATE((ah))->ah_eepromRead = ar9130EepromRead;
+	AH_PRIVATE((ah))->ah_eepromRead = ath_hal_EepromDataRead;
 	AH_PRIVATE((ah))->ah_eepromWrite = NULL;
 	ah->ah_eepromdata = eepromdata;
 
@@ -298,7 +299,16 @@
 	 */
 	pCap->halMbssidAggrSupport = AH_FALSE;
 	pCap->hal4AddrAggrSupport = AH_TRUE;
+	/* BB Read WAR */
+	pCap->halHasBBReadWar = AH_TRUE;
 
+	/*
+	 * Implement the PLL/config changes needed for half/quarter
+	 * rates before re-enabling them here.
+	 */
+	pCap->halChanHalfRate = AH_FALSE;
+	pCap->halChanQuarterRate = AH_FALSE;
+
 	return AH_TRUE;
 }
 

Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd
  * Copyright (c) 2010 Atheros Communications, Inc.
@@ -14,30 +15,5 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.c 230147 2012-01-15 19:22:34Z adrian $
  */
-#include "opt_ah.h"
-
-#include "ah.h"
-#include "ah_internal.h"
-
-#include "ar9001/ar9130_eeprom.h"
-
-/* XXX this shouldn't be done here */
-/* This is in 16 bit words; not bytes -adrian */
-#define ATH_DATA_EEPROM_SIZE    2048
-
-HAL_BOOL
-ar9130EepromRead(struct ath_hal *ah, u_int off, uint16_t *data)
-{
-	if (ah->ah_eepromdata == AH_NULL) {
-		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__);
-		return AH_FALSE;
-	}
-	if (off > ATH_DATA_EEPROM_SIZE) {
-		HALDEBUG(ah, HAL_DEBUG_ANY, "ar9130EepromRead: offset %x > %x\n", off, ATH_DATA_EEPROM_SIZE);
-		return AH_FALSE;
-	}
-	(*data) = ah->ah_eepromdata[off];
-	return AH_TRUE;
-}

Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010 Adrian Chadd, Xenion Pty Ltd
  * Copyright (c) 2010 Atheros Communications, Inc.
@@ -14,11 +15,9 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9130_eeprom.h 230147 2012-01-15 19:22:34Z adrian $
  */
 #ifndef	__AR9130_EEPROM_H__
 #define	__AR9130_EEPROM_H__
 
-extern	HAL_BOOL ar9130EepromRead(struct ath_hal *ah, u_int off, uint16_t *data);
-
 #endif

Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9130_phy.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9130_phy.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9130_phy.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd
  * Copyright (c) 2010 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9130_phy.c 242412 2012-10-31 21:14:25Z adrian $
  */
 #include "opt_ah.h"
 
@@ -33,6 +34,9 @@
 
 	uint32_t pll;
 
+	/*
+	 * XXX TODO: support half/quarter rates
+	 */
 	if (chan && IEEE80211_IS_CHAN_5GHZ(chan))
 		pll = 0x1450;
 	else

Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9130_phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9130_phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9130_phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010 Adrian Chadd, Xenion Pty Ltd
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9130_phy.h 221163 2011-04-28 12:47:40Z adrian $
  */
 #ifndef	__AR9130_PHY_H__
 #define	__AR9130_PHY_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9130reg.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9130reg.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9130reg.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010 Adrian Chadd, Xenion Pty Ltd
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
@@ -15,7 +16,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9130reg.h 221163 2011-04-28 12:47:40Z adrian $
  */
 #ifndef	__AR9130REG_H__
 #define	__AR9130REG_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9160.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9160.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9160.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9160.ini 220293 2011-04-03 11:59:52Z adrian $
  */
 /* Auto Generated PCI Register Writes.  Created: 05/22/08 */
 


Property changes on: trunk/sys/dev/ath/ath_hal/ar9001/ar9160.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c 249579 2013-04-17 07:22:23Z adrian $
  */
 #include "opt_ah.h"
 
@@ -68,8 +69,8 @@
 		.coarseHigh		= { -14, -14, -14, -14, -12 },
 		.coarseLow		= { -64, -64, -64, -64, -70 },
 		.firpwr			= { -78, -78, -78, -78, -80 },
-		.maxSpurImmunityLevel	= 2,
-		.cycPwrThr1		= { 2, 4, 6 },
+		.maxSpurImmunityLevel	= 7,
+		.cycPwrThr1		= { 2, 4, 6, 8, 10, 12, 14, 16 },
 		.maxFirstepLevel	= 2,	/* levels 0..2 */
 		.firstep		= { 0, 4, 8 },
 		.ofdmTrigHigh		= 500,
@@ -123,13 +124,13 @@
 	HAL_STATUS ecode;
 	HAL_BOOL rfStatus;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 	    __func__, sc, (void*) st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
 	ahp5416 = ath_hal_malloc(sizeof (struct ath_hal_5416));
 	if (ahp5416 == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		*status = HAL_ENOMEM;
 		return AH_NULL;
@@ -183,7 +184,7 @@
 	HAL_INI_INIT(&AH5416(ah)->ah_ini_bank1, ar9160Bank1, 2);
 	HAL_INI_INIT(&AH5416(ah)->ah_ini_bank2, ar9160Bank2, 2);
 	HAL_INI_INIT(&AH5416(ah)->ah_ini_bank3, ar9160Bank3, 3);
-	HAL_INI_INIT(&AH5416(ah)->ah_ini_bank6, ar9160Bank6, 3);
+	HAL_INI_INIT(&AH5416(ah)->ah_ini_bank6, ar9160Bank6TPC, 3);
 	HAL_INI_INIT(&AH5416(ah)->ah_ini_bank7, ar9160Bank7, 2);
 	if (AR_SREV_SOWL_11(ah))
 		HAL_INI_INIT(&AH5416(ah)->ah_ini_addac, ar9160Addac_1_1, 2);
@@ -320,6 +321,8 @@
 	pCap->halAutoSleepSupport = AH_FALSE;	/* XXX? */
 	pCap->halMbssidAggrSupport = AH_TRUE;
 	pCap->hal4AddrAggrSupport = AH_TRUE;
+	/* BB Read WAR */
+	pCap->halHasBBReadWar = AH_TRUE;
 
 	/* AR9160 is a 2x2 stream device */
 	pCap->halTxStreams = 2;

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9002phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9002phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9002phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd.
  *
@@ -22,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9002phy.h 220590 2011-04-13 04:40:59Z adrian $
  */
 #ifndef	__ATH_AR9002PHY_H__
 #define	__ATH_AR9002PHY_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9280.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9280.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9280.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9280.c 241195 2012-10-04 15:42:45Z adrian $
  */
 #include "opt_ah.h"
 
@@ -112,7 +113,17 @@
 
 		switch (frac_n_5g) {
 		case 0:
-			if ((freq % 20) == 0) {
+			/*
+			 * Enable fractional mode for half/quarter rate
+			 * channels.
+			 *
+			 * This is from the Linux ath9k code, rather than
+			 * the Atheros HAL code.
+			 */
+			if (IEEE80211_IS_CHAN_QUARTER(chan) ||
+			    IEEE80211_IS_CHAN_HALF(chan))
+				aModeRefSel = 0;
+			else if ((freq % 20) == 0) {
 				aModeRefSel = 3;
 			} else if ((freq % 10) == 0) {
 				aModeRefSel = 2;
@@ -122,10 +133,64 @@
 		default:
 			aModeRefSel = 0;
 			/* Enable 2G (fractional) mode for channels which are 5MHz spaced */
-			fracMode = 1;
-			refDivA = 1;
-			channelSel = (freq * 0x8000)/15;
 
+			/*
+			 * Workaround for talking on PSB non-5MHz channels;
+			 * the pre-Merlin chips only had a 2.5MHz channel
+			 * spacing so some channels aren't reachable.
+
+			 *
+			 * This interoperates on the quarter rate channels
+			 * with the AR5112 and later RF synths.  Please note
+			 * that the synthesiser isn't able to completely
+			 * accurately represent these frequencies (as the
+			 * resolution in this reference is 2.5MHz) and thus
+			 * it will be slightly "off centre."  This matches
+			 * the same slightly incorrect centre frequency
+			 * behaviour that the AR5112 and later channel
+			 * selection code has.
+			 *
+			 * This also interoperates with the AR5416
+			 * synthesiser modification for programming
+			 * fractional frequencies in 5GHz mode.  However
+			 * that modification is also disabled by default.
+			 *
+			 * This is disabled because it hasn't been tested for
+			 * regulatory compliance and neither have the NICs
+			 * which would use it.  So if you enable this code,
+			 * you must first ensure that you've re-certified the
+			 * NICs in question beforehand or you will be
+			 * violating your local regulatory rules and breaking
+			 * the law.
+			 */
+#if 0
+			if (freq % 5 == 0) {
+#endif
+				/* Normal */
+				fracMode = 1;
+				refDivA = 1;
+				channelSel = (freq * 0x8000)/15;
+#if 0
+			} else {
+				/* Offset by 500KHz */
+				uint32_t f, ch, ch2;
+
+				fracMode = 1;
+				refDivA = 1;
+
+				/* Calculate the "adjusted" frequency */
+				f = freq - 2;
+				ch = (((f - 4800) * 10) / 25) + 1;
+
+				ch2 = ((ch * 25) / 5) + 9600;
+				channelSel = (ch2 * 0x4000) / 15;
+				//ath_hal_printf(ah,
+				//    "%s: freq=%d, ch=%d, ch2=%d, "
+				//    "channelSel=%d\n",
+				//    __func__, freq, ch, ch2, channelSel);
+			}
+#endif
+
 			/* RefDivA setting */
 			OS_A_REG_RMW_FIELD(ah, AR_AN_SYNTH9,
 			    AR_AN_SYNTH9_REFDIVA, refDivA);
@@ -384,3 +449,11 @@
 
 	return AH_TRUE;
 }
+
+static HAL_BOOL
+ar9280RfProbe(struct ath_hal *ah)
+{
+	return (AR_SREV_MERLIN(ah));
+}
+
+AH_RF(RF9280, ar9280RfProbe, ar9280RfAttach);

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9280.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9280.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9280.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  *
@@ -13,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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9280.h 224243 2011-07-21 08:35:10Z adrian $
  */
 #ifndef _ATH_AR9280_H_
 #define _ATH_AR9280_H_

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c 244943 2013-01-02 00:38:01Z adrian $
  */
 #include "opt_ah.h"
 
@@ -61,7 +62,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);
@@ -80,8 +83,8 @@
                 .coarseHigh             = { -14, -14, -14, -14, -12 },
                 .coarseLow              = { -64, -64, -64, -64, -70 },
                 .firpwr                 = { -78, -78, -78, -78, -80 },
-                .maxSpurImmunityLevel   = 2,
-                .cycPwrThr1             = { 2, 4, 6 },
+                .maxSpurImmunityLevel   = 7,
+                .cycPwrThr1             = { 2, 4, 6, 8, 10, 12, 14, 16 },
                 .maxFirstepLevel        = 2,    /* levels 0..2 */
                 .firstep                = { 0, 4, 8 },
                 .ofdmTrigHigh           = 500,
@@ -112,6 +115,10 @@
 		 * Else, set PLL to 0x2850 to prevent reset-to-reset variation 
 		 */
 		pll = IS_5GHZ_FAST_CLOCK_EN(ah, chan) ? 0x142c : 0x2850;
+		if (IEEE80211_IS_CHAN_HALF(chan))
+			pll |= SM(0x1, AR_RTC_SOWL_PLL_CLKSEL);
+		else if (IEEE80211_IS_CHAN_QUARTER(chan))
+			pll |= SM(0x2, AR_RTC_SOWL_PLL_CLKSEL);
 	} else if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
 		pll = SM(0x5, AR_RTC_SOWL_PLL_REFDIV);
 		if (chan != AH_NULL) {
@@ -153,13 +160,13 @@
 	int8_t pwr_table_offset;
 	uint8_t pwr;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 	    __func__, sc, (void*) st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
 	ahp9280 = ath_hal_malloc(sizeof (struct ath_hal_9280));
 	if (ahp9280 == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		*status = HAL_ENOMEM;
 		return AH_NULL;
@@ -169,6 +176,18 @@
 
 	ar5416InitState(AH5416(ah), devid, sc, st, sh, status);
 
+	/*
+	 * Use the "local" EEPROM data given to us by the higher layers.
+	 * This is a private copy out of system flash. The Linux ath9k
+	 * commit for the initial AR9130 support mentions MMIO flash
+	 * access is "unreliable." -adrian
+	 */
+	if (eepromdata != AH_NULL) {
+		AH_PRIVATE((ah))->ah_eepromRead = ath_hal_EepromDataRead;
+		AH_PRIVATE((ah))->ah_eepromWrite = NULL;
+		ah->ah_eepromdata = eepromdata;
+	}
+
 	/* XXX override with 9280 specific state */
 	/* override 5416 methods for our needs */
 	AH5416(ah)->ah_initPLL = ar9280InitPLL;
@@ -175,6 +194,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;
@@ -403,17 +423,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;
@@ -809,10 +885,6 @@
 #if 0
 	pCap->halWowMatchPatternDword = AH_TRUE;
 #endif
-	/* AR9280 is a 2x2 stream device */
-	pCap->halTxStreams = 2;
-	pCap->halRxStreams = 2;
-
 	pCap->halCSTSupport = AH_TRUE;
 	pCap->halRifsRxSupport = AH_TRUE;
 	pCap->halRifsTxSupport = AH_TRUE;
@@ -829,6 +901,7 @@
 	pCap->halHasRxSelfLinkedTail = AH_FALSE;
 	pCap->halMbssidAggrSupport = AH_TRUE;
 	pCap->hal4AddrAggrSupport = AH_TRUE;
+	pCap->halSpectralScanSupport = AH_TRUE;
 
 	if (AR_SREV_MERLIN_20(ah)) {
 		pCap->halPSPollBroken = AH_FALSE;
@@ -892,9 +965,12 @@
 static const char*
 ar9280Probe(uint16_t vendorid, uint16_t devid)
 {
-	if (vendorid == ATHEROS_VENDOR_ID &&
-	    (devid == AR9280_DEVID_PCI || devid == AR9280_DEVID_PCIE))
-		return "Atheros 9280";
+	if (vendorid == ATHEROS_VENDOR_ID) {
+		if (devid == AR9280_DEVID_PCI)
+			return "Atheros 9220";
+		if (devid == AR9280_DEVID_PCIE)
+			return "Atheros 9280";
+	}
 	return AH_NULL;
 }
 AH_CHIP(AR9280, ar9280Probe, ar9280Attach);

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
  *
@@ -22,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c 221837 2011-05-13 14:33:45Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd.
  *
@@ -22,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h 219393 2011-03-08 06:59:59Z adrian $
  */
 
 #ifndef	__ATH_AR9280_OLC_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9280v1.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9280v1.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9280v1.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9280v1.ini 217631 2011-01-20 09:03:40Z adrian $
  */
 /* Auto Generated PCI Register Writes.  Created: 10/12/07 */
 


Property changes on: trunk/sys/dev/ath/ath_hal/ar9002/ar9280v1.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9280v2.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9280v2.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9280v2.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9280v2.ini 217751 2011-01-23 14:30:35Z adrian $
  */
 
 static const uint32_t ar9280Modes_v2[][6] = {


Property changes on: trunk/sys/dev/ath/ath_hal/ar9002/ar9280v2.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285.c 228517 2011-12-15 00:59:11Z adrian $
  */
 #include "opt_ah.h"
 
@@ -77,3 +78,11 @@
 
 	return AH_TRUE;
 }
+
+static HAL_BOOL
+ar9285RfProbe(struct ath_hal *ah)
+{
+	return (AR_SREV_KITE(ah));
+}
+
+AH_RF(RF9285, ar9285RfProbe, ar9285RfAttach);

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  *
@@ -13,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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285.h 251655 2013-06-12 14:52:57Z adrian $
  */
 #ifndef _ATH_AR9285_H_
 #define _ATH_AR9285_H_
@@ -20,41 +21,6 @@
 
 #include "ar5416/ar5416.h"
 
-enum ar9285_ant_div_comb_lna_conf {
-	ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2 = 0,
-	ATH_ANT_DIV_COMB_LNA2 = 1,
-	ATH_ANT_DIV_COMB_LNA1 = 2,
-	ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2 = 3,
-};
-
-struct ar9285_ant_comb {
-	uint16_t count;
-	uint16_t total_pkt_count;
-	HAL_BOOL scan;
-	HAL_BOOL scan_not_start;
-	int main_total_rssi;
-	int alt_total_rssi;
-	int alt_recv_cnt;
-	int main_recv_cnt;
-	int rssi_lna1;
-	int rssi_lna2;
-	int rssi_add;
-	int rssi_sub;
-	int rssi_first;
-	int rssi_second;
-	int rssi_third;
-	HAL_BOOL alt_good;
-	int quick_scan_cnt;
-	int main_conf;
-	enum ar9285_ant_div_comb_lna_conf first_quick_scan_conf;
-	enum ar9285_ant_div_comb_lna_conf second_quick_scan_conf;
-	int first_bias;
-	int second_bias;
-	HAL_BOOL first_ratio;
-	HAL_BOOL second_ratio;
-	unsigned long scan_start_time;
-};
-
 struct ath_hal_9285 {
 	struct ath_hal_5416 ah_5416;
 
@@ -61,8 +27,6 @@
 	HAL_INI_ARRAY	ah_ini_txgain;
 	HAL_INI_ARRAY	ah_ini_rxgain;
 
-	struct ar9285_ant_comb ant_comb;	/* Kite Antenna comb/diversity */
-
 	struct {
 		int32_t prev_offset;	/* Previous value of PA offset value */
 		int8_t max_skipcount;	/* Max No. of times PACAL can be skipped */
@@ -78,7 +42,6 @@
 #define	AR_PHY_CCA_MIN_GOOD_VAL_9285_2GHZ	-127
 #define	AR_PHY_CCA_MAX_GOOD_VAL_9285_2GHZ	-108
 
-HAL_BOOL ar9285SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING);
 HAL_BOOL ar9285RfAttach(struct ath_hal *, HAL_STATUS *);
 
 extern	HAL_BOOL ar9285SetTransmitPower(struct ath_hal *,
@@ -86,4 +49,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_ */

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285.ini 217631 2011-01-20 09:03:40Z adrian $
  */
 
 /* AR9285 Revsion 10 */


Property changes on: trunk/sys/dev/ath/ath_hal/ar9002/ar9285.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c 251655 2013-06-12 14:52:57Z adrian $
  */
 #include "opt_ah.h"
 
@@ -66,7 +67,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);
@@ -85,8 +88,8 @@
                 .coarseHigh             = { -14, -14, -14, -14, -12 },
                 .coarseLow              = { -64, -64, -64, -64, -70 },
                 .firpwr                 = { -78, -78, -78, -78, -80 },
-                .maxSpurImmunityLevel   = 2,
-                .cycPwrThr1             = { 2, 4, 6 },
+                .maxSpurImmunityLevel   = 7,
+                .cycPwrThr1             = { 2, 4, 6, 8, 10, 12, 14, 16 },
                 .maxFirstepLevel        = 2,    /* levels 0..2 */
                 .firstep                = { 0, 4, 8 },
                 .ofdmTrigHigh           = 500,
@@ -103,6 +106,28 @@
         ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE);
 }
 
+static const char * ar9285_lna_conf[] = {
+	"LNA1-LNA2",
+	"LNA2",
+	"LNA1",
+	"LNA1+LNA2",
+};
+
+static void
+ar9285_eeprom_print_diversity_settings(struct ath_hal *ah)
+{
+	const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
+	const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader;
+
+	ath_hal_printf(ah, "[ath] AR9285 Main LNA config: %s\n",
+	    ar9285_lna_conf[(pModal->antdiv_ctl2 >> 2) & 0x3]);
+	ath_hal_printf(ah, "[ath] AR9285 Alt LNA config: %s\n",
+	    ar9285_lna_conf[pModal->antdiv_ctl2 & 0x3]);
+	ath_hal_printf(ah, "[ath] LNA diversity %s, Diversity %s\n",
+	    ((pModal->antdiv_ctl1 & 0x1) ? "enabled" : "disabled"),
+	    ((pModal->antdiv_ctl1 & 0x8) ? "enabled" : "disabled"));
+}
+
 /*
  * Attach for an AR9285 part.
  */
@@ -118,13 +143,13 @@
 	HAL_STATUS ecode;
 	HAL_BOOL rfStatus;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 	    __func__, sc, (void*) st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
 	ahp9285 = ath_hal_malloc(sizeof (struct ath_hal_9285));
 	if (ahp9285 == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		*status = HAL_ENOMEM;
 		return AH_NULL;
@@ -134,14 +159,30 @@
 
 	ar5416InitState(AH5416(ah), devid, sc, st, sh, status);
 
-	/* XXX override with 9285 specific state */
-	/* override 5416 methods for our needs */
+	/*
+	 * Use the "local" EEPROM data given to us by the higher layers.
+	 * This is a private copy out of system flash. The Linux ath9k
+	 * commit for the initial AR9130 support mentions MMIO flash
+	 * access is "unreliable." -adrian
+	 */
+	if (eepromdata != AH_NULL) {
+		AH_PRIVATE(ah)->ah_eepromRead = ath_hal_EepromDataRead;
+		AH_PRIVATE(ah)->ah_eepromWrite = NULL;
+		ah->ah_eepromdata = eepromdata;
+	}
+
+	/* override with 9285 specific state */
 	AH5416(ah)->ah_initPLL = ar9280InitPLL;
+	AH5416(ah)->ah_btCoexSetDiversity = ar9285BTCoexAntennaDiversity;
 
 	ah->ah_setAntennaSwitch		= ar9285SetAntennaSwitch;
 	ah->ah_configPCIE		= ar9285ConfigPCIE;
+	ah->ah_disablePCIE		= ar9285DisablePCIE;
 	ah->ah_setTxPower		= ar9285SetTransmitPower;
 	ah->ah_setBoardValues		= ar9285SetBoardValues;
+	ah->ah_btCoexSetParameter	= ar9285BTCoexSetParameter;
+	ah->ah_divLnaConfGet		= ar9285_antdiv_comb_conf_get;
+	ah->ah_divLnaConfSet		= ar9285_antdiv_comb_conf_set;
 
 	AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal;
 	AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal;
@@ -293,10 +334,16 @@
 		goto bad;
 	}
 
+	/*
+	 * Print out the EEPROM antenna configuration mapping.
+	 * Some devices have a hard-coded LNA configuration profile;
+	 * others enable diversity.
+	 */
+	ar9285_eeprom_print_diversity_settings(ah);
+
 	/* Print out whether the EEPROM settings enable AR9285 diversity */
 	if (ar9285_check_div_comb(ah)) {
 		ath_hal_printf(ah, "[ath] Enabling diversity for Kite\n");
-		ah->ah_rxAntCombDiversity = ar9285_ant_comb_scan;
 	}
 
 	/* Disable 11n for the AR2427 */
@@ -350,17 +397,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;
@@ -411,6 +536,9 @@
 	pCap->halTxStreams = 1;
 	pCap->halRxStreams = 1;
 
+	if (ar9285_check_div_comb(ah))
+		pCap->halAntDivCombSupport = AH_TRUE;
+
 	pCap->halCSTSupport = AH_TRUE;
 	pCap->halRifsRxSupport = AH_TRUE;
 	pCap->halRifsTxSupport = AH_TRUE;
@@ -417,7 +545,7 @@
 	pCap->halRtsAggrLimit = 64*1024;	/* 802.11n max */
 	pCap->halExtChanDfsSupport = AH_TRUE;
 	pCap->halUseCombinedRadarRssi = AH_TRUE;
-#if 0
+#if 1
 	/* XXX bluetooth */
 	pCap->halBtCoexSupport = AH_TRUE;
 #endif
@@ -427,6 +555,8 @@
 	pCap->halHasRxSelfLinkedTail = AH_FALSE;
 	pCap->halMbssidAggrSupport = AH_TRUE;  
 	pCap->hal4AddrAggrSupport = AH_TRUE;
+	pCap->halSpectralScanSupport = AH_TRUE;
+	pCap->halRxUsingLnaMixing = AH_TRUE;
 
 	if (AR_SREV_KITE_12_OR_LATER(ah))
 		pCap->halPSPollBroken = AH_FALSE;

Added: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c	                        (rev 0)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,153 @@
+/* $MidnightBSD$ */
+/*
+ * 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: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c 251483 2013-06-07 05:17:58Z 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;
+
+	HALDEBUG(ah, HAL_DEBUG_BT_COEX,
+	    "%s: btCoexFlag: ALLOW=%d, ENABLE=%d\n",
+	    __func__,
+	    !! (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ALLOW),
+	    !! (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ENABLE));
+
+	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_antControl == 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_antControl == 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 table 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 table 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;
+	}
+}
+
+


Property changes on: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_btcoex.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_cal.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_cal.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_cal.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
@@ -23,7 +24,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_cal.c 221806 2011-05-12 10:11:24Z adrian $
  */
 #include "opt_ah.h"
 #include "ah.h"

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_cal.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_cal.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_cal.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
@@ -23,7 +24,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_cal.h 219481 2011-03-11 11:58:54Z adrian $
  */
 #ifndef	__AR9285_CAL_H__
 #define	__AR9285_CAL_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
@@ -23,7 +24,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.c 251655 2013-06-12 14:52:57Z adrian $
  */
 #include "opt_ah.h"
 
@@ -33,7 +34,6 @@
 #include "ah_eeprom_v4k.h"
 
 #include "ar9002/ar9280.h"
-#include "ar9002/ar9285_diversity.h"
 #include "ar9002/ar9285.h"
 #include "ar5416/ar5416reg.h"
 #include "ar5416/ar5416phy.h"
@@ -40,603 +40,9 @@
 #include "ar9002/ar9285phy.h"
 #include "ar9002/ar9285_phy.h"
 
+#include "ar9002/ar9285_diversity.h"
 
-/* Linux compability macros */
 /*
- * XXX these don't handle rounding, underflow, overflow, wrapping!
- */
-#define	msecs_to_jiffies(a)		( (a) * hz / 1000 )
-#define	time_after(a, b)		( (long) (b) - (long) (a) < 0 )
-
-static HAL_BOOL
-ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
-    int main_rssi_avg, int alt_rssi_avg, int pkt_count)
-{
-	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
-		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
-}
-
-static void
-ath_lnaconf_alt_good_scan(struct ar9285_ant_comb *antcomb,
-    struct ar9285_antcomb_conf ant_conf, int main_rssi_avg)
-{
-	antcomb->quick_scan_cnt = 0;
-
-	if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
-		antcomb->rssi_lna2 = main_rssi_avg;
-	else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
-		antcomb->rssi_lna1 = main_rssi_avg;
-
-	switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
-	case (0x10): /* LNA2 A-B */
-		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-		antcomb->first_quick_scan_conf =
-			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
-		break;
-	case (0x20): /* LNA1 A-B */
-		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-		antcomb->first_quick_scan_conf =
-			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
-		break;
-	case (0x21): /* LNA1 LNA2 */
-		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
-		antcomb->first_quick_scan_conf =
-			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-		antcomb->second_quick_scan_conf =
-			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-		break;
-	case (0x12): /* LNA2 LNA1 */
-		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
-		antcomb->first_quick_scan_conf =
-			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-		antcomb->second_quick_scan_conf =
-			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-		break;
-	case (0x13): /* LNA2 A+B */
-		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-		antcomb->first_quick_scan_conf =
-			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
-		break;
-	case (0x23): /* LNA1 A+B */
-		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-		antcomb->first_quick_scan_conf =
-			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-ath_select_ant_div_from_quick_scan(struct ar9285_ant_comb *antcomb,
-    struct ar9285_antcomb_conf *div_ant_conf, int main_rssi_avg,
-    int alt_rssi_avg, int alt_ratio)
-{
-	/* alt_good */
-	switch (antcomb->quick_scan_cnt) {
-	case 0:
-		/* set alt to main, and alt to first conf */
-		div_ant_conf->main_lna_conf = antcomb->main_conf;
-		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
-		break;
-	case 1:
-		/* set alt to main, and alt to first conf */
-		div_ant_conf->main_lna_conf = antcomb->main_conf;
-		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
-		antcomb->rssi_first = main_rssi_avg;
-		antcomb->rssi_second = alt_rssi_avg;
-
-		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
-			/* main is LNA1 */
-			if (ath_is_alt_ant_ratio_better(alt_ratio,
-						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
-						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
-						main_rssi_avg, alt_rssi_avg,
-						antcomb->total_pkt_count))
-				antcomb->first_ratio = AH_TRUE;
-			else
-				antcomb->first_ratio = AH_FALSE;
-		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
-			if (ath_is_alt_ant_ratio_better(alt_ratio,
-						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
-						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
-						main_rssi_avg, alt_rssi_avg,
-						antcomb->total_pkt_count))
-				antcomb->first_ratio = AH_TRUE;
-			else
-				antcomb->first_ratio = AH_FALSE;
-		} else {
-			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-			    (alt_rssi_avg > main_rssi_avg +
-			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
-			    (alt_rssi_avg > main_rssi_avg)) &&
-			    (antcomb->total_pkt_count > 50))
-				antcomb->first_ratio = AH_TRUE;
-			else
-				antcomb->first_ratio = AH_FALSE;
-		}
-		break;
-	case 2:
-		antcomb->alt_good = AH_FALSE;
-		antcomb->scan_not_start = AH_FALSE;
-		antcomb->scan = AH_FALSE;
-		antcomb->rssi_first = main_rssi_avg;
-		antcomb->rssi_third = alt_rssi_avg;
-
-		if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
-			antcomb->rssi_lna1 = alt_rssi_avg;
-		else if (antcomb->second_quick_scan_conf ==
-			 ATH_ANT_DIV_COMB_LNA2)
-			antcomb->rssi_lna2 = alt_rssi_avg;
-		else if (antcomb->second_quick_scan_conf ==
-			 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
-			if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
-				antcomb->rssi_lna2 = main_rssi_avg;
-			else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
-				antcomb->rssi_lna1 = main_rssi_avg;
-		}
-
-		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
-		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
-			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
-		else
-			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
-
-		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
-			if (ath_is_alt_ant_ratio_better(alt_ratio,
-						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
-						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
-						main_rssi_avg, alt_rssi_avg,
-						antcomb->total_pkt_count))
-				antcomb->second_ratio = AH_TRUE;
-			else
-				antcomb->second_ratio = AH_FALSE;
-		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
-			if (ath_is_alt_ant_ratio_better(alt_ratio,
-						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
-						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
-						main_rssi_avg, alt_rssi_avg,
-						antcomb->total_pkt_count))
-				antcomb->second_ratio = AH_TRUE;
-			else
-				antcomb->second_ratio = AH_FALSE;
-		} else {
-			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-			    (alt_rssi_avg > main_rssi_avg +
-			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
-			    (alt_rssi_avg > main_rssi_avg)) &&
-			    (antcomb->total_pkt_count > 50))
-				antcomb->second_ratio = AH_TRUE;
-			else
-				antcomb->second_ratio = AH_FALSE;
-		}
-
-		/* set alt to the conf with maximun ratio */
-		if (antcomb->first_ratio && antcomb->second_ratio) {
-			if (antcomb->rssi_second > antcomb->rssi_third) {
-				/* first alt*/
-				if ((antcomb->first_quick_scan_conf ==
-				    ATH_ANT_DIV_COMB_LNA1) ||
-				    (antcomb->first_quick_scan_conf ==
-				    ATH_ANT_DIV_COMB_LNA2))
-					/* Set alt LNA1 or LNA2*/
-					if (div_ant_conf->main_lna_conf ==
-					    ATH_ANT_DIV_COMB_LNA2)
-						div_ant_conf->alt_lna_conf =
-							ATH_ANT_DIV_COMB_LNA1;
-					else
-						div_ant_conf->alt_lna_conf =
-							ATH_ANT_DIV_COMB_LNA2;
-				else
-					/* Set alt to A+B or A-B */
-					div_ant_conf->alt_lna_conf =
-						antcomb->first_quick_scan_conf;
-			} else if ((antcomb->second_quick_scan_conf ==
-				   ATH_ANT_DIV_COMB_LNA1) ||
-				   (antcomb->second_quick_scan_conf ==
-				   ATH_ANT_DIV_COMB_LNA2)) {
-				/* Set alt LNA1 or LNA2 */
-				if (div_ant_conf->main_lna_conf ==
-				    ATH_ANT_DIV_COMB_LNA2)
-					div_ant_conf->alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-				else
-					div_ant_conf->alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-			} else {
-				/* Set alt to A+B or A-B */
-				div_ant_conf->alt_lna_conf =
-					antcomb->second_quick_scan_conf;
-			}
-		} else if (antcomb->first_ratio) {
-			/* first alt */
-			if ((antcomb->first_quick_scan_conf ==
-			    ATH_ANT_DIV_COMB_LNA1) ||
-			    (antcomb->first_quick_scan_conf ==
-			    ATH_ANT_DIV_COMB_LNA2))
-					/* Set alt LNA1 or LNA2 */
-				if (div_ant_conf->main_lna_conf ==
-				    ATH_ANT_DIV_COMB_LNA2)
-					div_ant_conf->alt_lna_conf =
-							ATH_ANT_DIV_COMB_LNA1;
-				else
-					div_ant_conf->alt_lna_conf =
-							ATH_ANT_DIV_COMB_LNA2;
-			else
-				/* Set alt to A+B or A-B */
-				div_ant_conf->alt_lna_conf =
-						antcomb->first_quick_scan_conf;
-		} else if (antcomb->second_ratio) {
-				/* second alt */
-			if ((antcomb->second_quick_scan_conf ==
-			    ATH_ANT_DIV_COMB_LNA1) ||
-			    (antcomb->second_quick_scan_conf ==
-			    ATH_ANT_DIV_COMB_LNA2))
-				/* Set alt LNA1 or LNA2 */
-				if (div_ant_conf->main_lna_conf ==
-				    ATH_ANT_DIV_COMB_LNA2)
-					div_ant_conf->alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-				else
-					div_ant_conf->alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-			else
-				/* Set alt to A+B or A-B */
-				div_ant_conf->alt_lna_conf =
-						antcomb->second_quick_scan_conf;
-		} else {
-			/* main is largest */
-			if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
-			    (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
-				/* Set alt LNA1 or LNA2 */
-				if (div_ant_conf->main_lna_conf ==
-				    ATH_ANT_DIV_COMB_LNA2)
-					div_ant_conf->alt_lna_conf =
-							ATH_ANT_DIV_COMB_LNA1;
-				else
-					div_ant_conf->alt_lna_conf =
-							ATH_ANT_DIV_COMB_LNA2;
-			else
-				/* Set alt to A+B or A-B */
-				div_ant_conf->alt_lna_conf = antcomb->main_conf;
-		}
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-ath_ant_div_conf_fast_divbias(struct ar9285_antcomb_conf *ant_conf)
-{
-	/* Adjust the fast_div_bias based on main and alt lna conf */
-	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
-	case (0x01): /* A-B LNA2 */
-		ant_conf->fast_div_bias = 0x3b;
-		break;
-	case (0x02): /* A-B LNA1 */
-		ant_conf->fast_div_bias = 0x3d;
-		break;
-	case (0x03): /* A-B A+B */
-		ant_conf->fast_div_bias = 0x1;
-		break;
-	case (0x10): /* LNA2 A-B */
-		ant_conf->fast_div_bias = 0x7;
-		break;
-	case (0x12): /* LNA2 LNA1 */
-		ant_conf->fast_div_bias = 0x2;
-		break;
-	case (0x13): /* LNA2 A+B */
-		ant_conf->fast_div_bias = 0x7;
-		break;
-	case (0x20): /* LNA1 A-B */
-		ant_conf->fast_div_bias = 0x6;
-		break;
-	case (0x21): /* LNA1 LNA2 */
-		ant_conf->fast_div_bias = 0x0;
-		break;
-	case (0x23): /* LNA1 A+B */
-		ant_conf->fast_div_bias = 0x6;
-		break;
-	case (0x30): /* A+B A-B */
-		ant_conf->fast_div_bias = 0x1;
-		break;
-	case (0x31): /* A+B LNA2 */
-		ant_conf->fast_div_bias = 0x3b;
-		break;
-	case (0x32): /* A+B LNA1 */
-		ant_conf->fast_div_bias = 0x3d;
-		break;
-	default:
-		break;
-	}
-}
-
-/* Antenna diversity and combining */
-void
-ar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs,
-    unsigned long ticks, int hz)
-{
-	struct ar9285_antcomb_conf div_ant_conf;
-	struct ar9285_ant_comb *antcomb = &AH9285(ah)->ant_comb;
-	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
-	int curr_main_set, curr_bias;
-	int main_rssi = rs->rs_rssi_ctl[0];
-	int alt_rssi = rs->rs_rssi_ctl[1];
-	int rx_ant_conf, main_ant_conf, alt_ant_conf;
-	HAL_BOOL short_scan = AH_FALSE;
-
-	rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
-	main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
-	alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
-
-#if 0
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; FastDiv: %d\n",
-	    __func__, main_rssi, alt_rssi, main_ant_conf,alt_ant_conf, rx_ant_conf,
-	    !!(rs->rs_rssi_ctl[2] & 0x80), !!(rs->rs_rssi_ctl[2] & 0x40), !!(rs->rs_rssi_ext[2] & 0x40));
-#endif
-
-	if (! ar9285_check_div_comb(ah))
-		return;
-
-	if (AH5212(ah)->ah_diversity == AH_FALSE)
-		return;
-
-#if 0
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main: %d, alt: %d, rx_ant_conf: %x, main_ant_conf: %x\n",
-	    __func__, main_rssi, alt_rssi, rx_ant_conf, main_ant_conf);
-#endif
-
-	/* Record packet only when alt_rssi is positive */
-	if (main_rssi > 0 && alt_rssi > 0) {
-		antcomb->total_pkt_count++;
-		antcomb->main_total_rssi += main_rssi;
-		antcomb->alt_total_rssi  += alt_rssi;
-		if (main_ant_conf == rx_ant_conf)
-			antcomb->main_recv_cnt++;
-		else
-			antcomb->alt_recv_cnt++;
-	}
-
-	/* Short scan check */
-	if (antcomb->scan && antcomb->alt_good) {
-		if (time_after(ticks, antcomb->scan_start_time +
-		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
-			short_scan = AH_TRUE;
-		else
-			if (antcomb->total_pkt_count ==
-			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
-				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
-					    antcomb->total_pkt_count);
-				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
-					short_scan = AH_TRUE;
-			}
-	}
-
-	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
-	    rs->rs_moreaggr) && !short_scan)
-		return;
-
-	if (antcomb->total_pkt_count) {
-		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
-			     antcomb->total_pkt_count);
-		main_rssi_avg = (antcomb->main_total_rssi /
-				 antcomb->total_pkt_count);
-		alt_rssi_avg = (antcomb->alt_total_rssi /
-				 antcomb->total_pkt_count);
-	}
-
-	ar9285_antdiv_comb_conf_get(ah, &div_ant_conf);
-	curr_alt_set = div_ant_conf.alt_lna_conf;
-	curr_main_set = div_ant_conf.main_lna_conf;
-	curr_bias = div_ant_conf.fast_div_bias;
-
-	antcomb->count++;
-
-	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
-		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
-			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
-						  main_rssi_avg);
-			antcomb->alt_good = AH_TRUE;
-		} else {
-			antcomb->alt_good = AH_FALSE;
-		}
-
-		antcomb->count = 0;
-		antcomb->scan = AH_TRUE;
-		antcomb->scan_not_start = AH_TRUE;
-	}
-
-	if (!antcomb->scan) {
-		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
-			if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
-				/* Switch main and alt LNA */
-				div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-				div_ant_conf.alt_lna_conf  =
-						ATH_ANT_DIV_COMB_LNA1;
-			} else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
-				div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-				div_ant_conf.alt_lna_conf  =
-						ATH_ANT_DIV_COMB_LNA2;
-			}
-
-			goto div_comb_done;
-		} else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
-			   (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
-			/* Set alt to another LNA */
-			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
-				div_ant_conf.alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-			else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
-				div_ant_conf.alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-
-			goto div_comb_done;
-		}
-
-		if ((alt_rssi_avg < (main_rssi_avg +
-		    ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA)))
-			goto div_comb_done;
-	}
-
-	if (!antcomb->scan_not_start) {
-		switch (curr_alt_set) {
-		case ATH_ANT_DIV_COMB_LNA2:
-			antcomb->rssi_lna2 = alt_rssi_avg;
-			antcomb->rssi_lna1 = main_rssi_avg;
-			antcomb->scan = AH_TRUE;
-			/* set to A+B */
-			div_ant_conf.main_lna_conf =
-				ATH_ANT_DIV_COMB_LNA1;
-			div_ant_conf.alt_lna_conf  =
-				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-			break;
-		case ATH_ANT_DIV_COMB_LNA1:
-			antcomb->rssi_lna1 = alt_rssi_avg;
-			antcomb->rssi_lna2 = main_rssi_avg;
-			antcomb->scan = AH_TRUE;
-			/* set to A+B */
-			div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
-			div_ant_conf.alt_lna_conf  =
-				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-			break;
-		case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
-			antcomb->rssi_add = alt_rssi_avg;
-			antcomb->scan = AH_TRUE;
-			/* set to A-B */
-			div_ant_conf.alt_lna_conf =
-				ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-			break;
-		case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
-			antcomb->rssi_sub = alt_rssi_avg;
-			antcomb->scan = AH_FALSE;
-			if (antcomb->rssi_lna2 >
-			    (antcomb->rssi_lna1 +
-			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
-				/* use LNA2 as main LNA */
-				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
-				    (antcomb->rssi_add > antcomb->rssi_sub)) {
-					/* set to A+B */
-					div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-					div_ant_conf.alt_lna_conf  =
-						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-				} else if (antcomb->rssi_sub >
-					   antcomb->rssi_lna1) {
-					/* set to A-B */
-					div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-					div_ant_conf.alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-				} else {
-					/* set to LNA1 */
-					div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-					div_ant_conf.alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-				}
-			} else {
-				/* use LNA1 as main LNA */
-				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
-				    (antcomb->rssi_add > antcomb->rssi_sub)) {
-					/* set to A+B */
-					div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-					div_ant_conf.alt_lna_conf  =
-						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-				} else if (antcomb->rssi_sub >
-					   antcomb->rssi_lna1) {
-					/* set to A-B */
-					div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-					div_ant_conf.alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-				} else {
-					/* set to LNA2 */
-					div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-					div_ant_conf.alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-				}
-			}
-			break;
-		default:
-			break;
-		}
-	} else {
-		if (!antcomb->alt_good) {
-			antcomb->scan_not_start = AH_FALSE;
-			/* Set alt to another LNA */
-			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
-				div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-				div_ant_conf.alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-			} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
-				div_ant_conf.main_lna_conf =
-						ATH_ANT_DIV_COMB_LNA1;
-				div_ant_conf.alt_lna_conf =
-						ATH_ANT_DIV_COMB_LNA2;
-			}
-			goto div_comb_done;
-		}
-	}
-
-	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
-					   main_rssi_avg, alt_rssi_avg,
-					   alt_ratio);
-
-	antcomb->quick_scan_cnt++;
-
-div_comb_done:
-	ath_ant_div_conf_fast_divbias(&div_ant_conf);
-
-	ar9285_antdiv_comb_conf_set(ah, &div_ant_conf);
-
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
-	   __func__, antcomb->total_pkt_count);
-
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
-	   __func__, antcomb->main_total_rssi);
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
-	   __func__, antcomb->alt_total_rssi);
-
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
-	   __func__, main_rssi_avg);
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
-	   __func__, alt_rssi_avg);
-
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
-	   __func__, antcomb->main_recv_cnt);
-	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
-	   __func__, antcomb->alt_recv_cnt);
-
-//	if (curr_alt_set != div_ant_conf.alt_lna_conf)
-		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
-		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
-//	if (curr_main_set != div_ant_conf.main_lna_conf)
-		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
-		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
-//	if (curr_bias != div_ant_conf.fast_div_bias)
-		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
-		    __func__, curr_bias, div_ant_conf.fast_div_bias);
-
-	antcomb->scan_start_time = ticks;
-	antcomb->total_pkt_count = 0;
-	antcomb->main_total_rssi = 0;
-	antcomb->alt_total_rssi = 0;
-	antcomb->main_recv_cnt = 0;
-	antcomb->alt_recv_cnt = 0;
-}
-
-/*
  * Set the antenna switch to control RX antenna diversity.
  *
  * If a fixed configuration is used, the LNA and div bias
@@ -697,8 +103,8 @@
 		/* Diversity disabled, RX = LNA1 */
 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_A\n",
 		    __func__);
-		regVal |= SM(ATH_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
-		regVal |= SM(ATH_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
+		regVal |= SM(HAL_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
+		regVal |= SM(HAL_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
 		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_ALT_GAINTB);
 		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
 	    }
@@ -706,8 +112,8 @@
 		/* Diversity disabled, RX = LNA2 */
 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_B\n",
 		    __func__);
-		regVal |= SM(ATH_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
-		regVal |= SM(ATH_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
+		regVal |= SM(HAL_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
+		regVal |= SM(HAL_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
 		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_ALT_GAINTB);
 		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
 	    }
@@ -740,8 +146,8 @@
 		    __func__);
 		regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
 		regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | AR_PHY_9285_ANT_DIV_ALT_LNACONF));
-		regVal |= (ATH_ANT_DIV_COMB_LNA1 << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
-		regVal |= (ATH_ANT_DIV_COMB_LNA2 << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
+		regVal |= (HAL_ANT_DIV_COMB_LNA1 << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
+		regVal |= (HAL_ANT_DIV_COMB_LNA2 << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
 		regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
 		regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S);
 		OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
@@ -23,31 +24,11 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.h 251655 2013-06-12 14:52:57Z adrian $
  */
 #ifndef	__AR9285_DIVERSITY_H__
 #define	__AR9285_DIVERSITY_H__
 
-/* Antenna diversity/combining */
-#define	ATH_ANT_RX_CURRENT_SHIFT	4
-#define	ATH_ANT_RX_MAIN_SHIFT		2
-#define	ATH_ANT_RX_MASK			0x3
+extern	HAL_BOOL ar9285SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING);
 
-#define	ATH_ANT_DIV_COMB_SHORT_SCAN_INTR	50
-#define	ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT	0x100
-#define	ATH_ANT_DIV_COMB_MAX_PKTCOUNT		0x200
-#define	ATH_ANT_DIV_COMB_INIT_COUNT		95
-#define	ATH_ANT_DIV_COMB_MAX_COUNT		100
-#define	ATH_ANT_DIV_COMB_ALT_ANT_RATIO		30
-#define	ATH_ANT_DIV_COMB_ALT_ANT_RATIO2		20
-
-#define	ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA	-3
-#define	ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA	-1
-#define	ATH_ANT_DIV_COMB_LNA1_DELTA_HI		-4
-#define	ATH_ANT_DIV_COMB_LNA1_DELTA_MID		-2
-#define	ATH_ANT_DIV_COMB_LNA1_DELTA_LOW		2
-
-extern	void ar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs,
-		unsigned long ticks, int hz);
-
 #endif

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_phy.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_phy.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_phy.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd.
@@ -23,7 +24,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_phy.c 251656 2013-06-12 15:18:10Z adrian $
  */
 #include "opt_ah.h"
 
@@ -40,8 +41,7 @@
 #include "ar9002/ar9285_phy.h"
 
 void
-ar9285_antdiv_comb_conf_get(struct ath_hal *ah,
-    struct ar9285_antcomb_conf *antconf)
+ar9285_antdiv_comb_conf_get(struct ath_hal *ah, HAL_ANT_COMB_CONFIG *antconf)
 {
 	uint32_t regval;
 
@@ -52,11 +52,11 @@
 				 AR_PHY_9285_ANT_DIV_ALT_LNACONF_S;
 	antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >>
 				  AR_PHY_9285_FAST_DIV_BIAS_S;
+	antconf->antdiv_configgroup = DEFAULT_ANTDIV_CONFIG_GROUP;
 }
 
 void
-ar9285_antdiv_comb_conf_set(struct ath_hal *ah,
-    struct ar9285_antcomb_conf *antconf)
+ar9285_antdiv_comb_conf_set(struct ath_hal *ah, HAL_ANT_COMB_CONFIG *antconf)
 {
 	uint32_t regval;
 
@@ -87,8 +87,10 @@
 	HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
         const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader;
 
+#if 0
 	/* For now, simply disable this until it's better debugged. -adrian */
 	return AH_FALSE;
+#endif
 
 	if (! AR_SREV_KITE(ah))
 		return AH_FALSE;

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd.
@@ -23,7 +24,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_phy.h 251643 2013-06-12 06:01:53Z adrian $
  */
 #ifndef	__AR9285_PHY_H__
 #define	__AR9285_PHY_H__
@@ -31,16 +32,11 @@
 /*
  * Manipulate AR9285 antenna diversity configuration
  */
-struct ar9285_antcomb_conf {
-	uint8_t main_lna_conf;
-	uint8_t alt_lna_conf;
-	uint8_t fast_div_bias;
-};
 
 extern	void ar9285_antdiv_comb_conf_set(struct ath_hal *ah,
-		struct ar9285_antcomb_conf *antconf);
+		HAL_ANT_COMB_CONFIG *antconf);
 extern	void ar9285_antdiv_comb_conf_get(struct ath_hal *ah,
-		struct ar9285_antcomb_conf *antconf);
+		HAL_ANT_COMB_CONFIG *antconf);
 extern	HAL_BOOL ar9285_check_div_comb(struct ath_hal *ah);
 
 #endif

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285_reset.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285_reset.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285_reset.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285_reset.c 251655 2013-06-12 14:52:57Z adrian $
  */
 
 /*
@@ -38,6 +39,7 @@
 #include "ar9002/ar9002phy.h"
 #include "ar9002/ar9285phy.h"
 #include "ar9002/ar9285an.h"
+#include "ar9002/ar9285_diversity.h"
 
 /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ 
 #define	EEP_MINOR(_ah) \
@@ -76,9 +78,7 @@
 
     MODAL_EEP4K_HEADER	*pModal;
     struct ath_hal_5212 *ahp = AH5212(ah);
-    int16_t		ratesArray[Ar5416RateSize];
     int16_t		txPowerIndexOffset = 0;
-    uint8_t		ht40PowerIncForPdadc = 2;	
     int			i;
     
     uint16_t		cfgCtl;
@@ -91,8 +91,10 @@
 
     HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1);
 
+    AH5416(ah)->ah_ht40PowerIncForPdadc = 2;
+
     /* Setup info for the actual eeprom */
-    OS_MEMZERO(ratesArray, sizeof(ratesArray));
+    OS_MEMZERO(AH5416(ah)->ah_ratesArray, sizeof(AH5416(ah)->ah_ratesArray));
     cfgCtl = ath_hal_getctl(ah, chan);
     powerLimit = chan->ic_maxregpower * 2;
     twiceAntennaReduction = chan->ic_maxantgain;
@@ -102,11 +104,11 @@
 	__func__,chan->ic_freq, cfgCtl );      
   
     if (IS_EEP_MINOR_V2(ah)) {
-        ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
     }
  
     if (!ar9285SetPowerPerRateTable(ah, pEepData,  chan,
-                                    &ratesArray[0],cfgCtl,
+                                    &AH5416(ah)->ah_ratesArray[0],cfgCtl,
                                     twiceAntennaReduction,
 				    twiceMaxRegulatoryPower, powerLimit)) {
         HALDEBUG(ah, HAL_DEBUG_ANY,
@@ -120,11 +122,12 @@
         return AH_FALSE;
     }
   
-    maxPower = AH_MAX(ratesArray[rate6mb], ratesArray[rateHt20_0]);
-    maxPower = AH_MAX(maxPower, ratesArray[rate1l]);
+    maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb],
+      AH5416(ah)->ah_ratesArray[rateHt20_0]);
+    maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rate1l]);
 
     if (IEEE80211_IS_CHAN_HT40(chan)) {
-        maxPower = AH_MAX(maxPower, ratesArray[rateHt40_0]);
+        maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rateHt40_0]);
     }
 
     ahp->ah_tx6PowerInHalfDbm = maxPower;   
@@ -135,18 +138,18 @@
      * txPowerIndexOffset is set by the SetPowerTable() call -
      *  adjust the rate table (0 offset if rates EEPROM not loaded)
      */
-    for (i = 0; i < N(ratesArray); i++) {
-        ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
+    for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) {
+        AH5416(ah)->ah_ratesArray[i] = (int16_t)(txPowerIndexOffset + AH5416(ah)->ah_ratesArray[i]);
 	/* -5 dBm offset for Merlin and later; this includes Kite */
-	ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
-        if (ratesArray[i] > AR5416_MAX_RATE_POWER)
-            ratesArray[i] = AR5416_MAX_RATE_POWER;
-	if (ratesArray[i] < 0)
-		ratesArray[i] = 0;
+	AH5416(ah)->ah_ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
+        if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER)
+            AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER;
+	if (AH5416(ah)->ah_ratesArray[i] < 0)
+		AH5416(ah)->ah_ratesArray[i] = 0;
     }
 
 #ifdef AH_EEPROM_DUMP
-    ar5416PrintPowerPerRate(ah, ratesArray);
+    ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray);
 #endif
 
     /*
@@ -157,18 +160,26 @@
      * XXX handle overflow/too high power level?
      */
     if (IEEE80211_IS_CHAN_HT40(chan)) {
-        ratesArray[rateHt40_0] += ht40PowerIncForPdadc;
-        ratesArray[rateHt40_1] += ht40PowerIncForPdadc;
-        ratesArray[rateHt40_2] += ht40PowerIncForPdadc;
-        ratesArray[rateHt40_3] += ht40PowerIncForPdadc;
-        ratesArray[rateHt40_4] += ht40PowerIncForPdadc;
-        ratesArray[rateHt40_5] += ht40PowerIncForPdadc;
-        ratesArray[rateHt40_6] += ht40PowerIncForPdadc;
-        ratesArray[rateHt40_7] += ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ratesArray[rateHt40_0] +=
+          AH5416(ah)->ah_ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ratesArray[rateHt40_1] +=
+          AH5416(ah)->ah_ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ratesArray[rateHt40_2] +=
+          AH5416(ah)->ah_ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ratesArray[rateHt40_3] +=
+          AH5416(ah)->ah_ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ratesArray[rateHt40_4] +=
+          AH5416(ah)->ah_ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ratesArray[rateHt40_5] +=
+          AH5416(ah)->ah_ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ratesArray[rateHt40_6] +=
+          AH5416(ah)->ah_ht40PowerIncForPdadc;
+        AH5416(ah)->ah_ratesArray[rateHt40_7] +=
+          AH5416(ah)->ah_ht40PowerIncForPdadc;
     }
 
     /* Write the TX power rate registers */
-    ar5416WriteTxPowerRateRegisters(ah, chan, ratesArray);
+    ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray);
 
     return AH_TRUE;
 #undef POW_SM

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285an.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285an.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285an.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285an.h 221806 2011-05-12 10:11:24Z adrian $
  */
 
 #ifndef	__AR9285_AN_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd.
@@ -23,7 +24,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285phy.h 219481 2011-03-11 11:58:54Z adrian $
  */
 #ifndef	__ATH_AR9285PHY_H__
 #define	__ATH_AR9285PHY_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9285v2.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9285v2.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9285v2.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9285v2.ini 217811 2011-01-25 05:36:29Z adrian $
  */
 
 static const u_int32_t ar9285Modes_v2[][6] = {


Property changes on: trunk/sys/dev/ath/ath_hal/ar9002/ar9285v2.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287.c 228517 2011-12-15 00:59:11Z adrian $
  */
 #include "opt_ah.h"
 
@@ -390,3 +391,11 @@
 
 	return AH_TRUE;
 }
+
+static HAL_BOOL
+ar9287RfProbe(struct ath_hal *ah)
+{
+	return (AR_SREV_KIWI(ah));
+}
+
+AH_RF(RF9287, ar9287RfProbe, ar9287RfAttach);

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010 Atheros Communications, Inc.
  *
@@ -13,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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287.h 228833 2011-12-23 04:05:39Z adrian $
  */
 
 #ifndef _ATH_AR9287_H_
@@ -45,9 +46,7 @@
 #define	AH9287(_ah)	((struct ath_hal_9287 *)(_ah))
 
 #define	AR9287_DEFAULT_RXCHAINMASK	3
-#define	AR9285_DEFAULT_RXCHAINMASK	1
 #define	AR9287_DEFAULT_TXCHAINMASK	3
-#define	AR9285_DEFAULT_TXCHAINMASK	1
 
 #define	AR_PHY_CCA_NOM_VAL_9287_2GHZ		-112
 #define	AR_PHY_CCA_NOM_VAL_9287_5GHZ		-112

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287.ini
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287.ini	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287.ini	2018-05-28 00:25:20 UTC (rev 10127)
@@ -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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287.ini 222301 2011-05-26 09:15:33Z adrian $
  */
 
 static const uint32_t ar9287Modes_9287_1_1[][6] = {


Property changes on: trunk/sys/dev/ath/ath_hal/ar9002/ar9287.ini
___________________________________________________________________
Added: mnbsd:nokeywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c 244943 2013-01-02 00:38:01Z adrian $
  */
 #include "opt_ah.h"
 
@@ -65,7 +66,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);
@@ -76,7 +79,7 @@
 	/*
 	 * These are the parameters from the AR5416 ANI code;
 	 * they likely need quite a bit of adjustment for the
-	 * AR9280.
+	 * AR9287.
 	 */
         static const struct ar5212AniParams aniparams = {
                 .maxNoiseImmunityLevel  = 4,    /* levels 0..4 */
@@ -84,8 +87,8 @@
                 .coarseHigh             = { -14, -14, -14, -14, -12 },
                 .coarseLow              = { -64, -64, -64, -64, -70 },
                 .firpwr                 = { -78, -78, -78, -78, -80 },
-                .maxSpurImmunityLevel   = 2,
-                .cycPwrThr1             = { 2, 4, 6 },
+                .maxSpurImmunityLevel   = 7,
+                .cycPwrThr1             = { 2, 4, 6, 8, 10, 12, 14, 16 },
                 .maxFirstepLevel        = 2,    /* levels 0..2 */
                 .firstep                = { 0, 4, 8 },
                 .ofdmTrigHigh           = 500,
@@ -119,13 +122,13 @@
 	HAL_BOOL rfStatus;
 	int8_t pwr_table_offset;
 
-	HALDEBUG_G(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
+	HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n",
 	    __func__, sc, (void*) st, (void*) sh);
 
 	/* NB: memory is returned zero'd */
 	ahp9287 = ath_hal_malloc(sizeof (struct ath_hal_9287));
 	if (ahp9287 == AH_NULL) {
-		HALDEBUG_G(AH_NULL, HAL_DEBUG_ANY,
+		HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
 		    "%s: cannot allocate memory for state block\n", __func__);
 		*status = HAL_ENOMEM;
 		return AH_NULL;
@@ -135,6 +138,13 @@
 
 	ar5416InitState(AH5416(ah), devid, sc, st, sh, status);
 
+	if (eepromdata != AH_NULL) {
+		AH_PRIVATE(ah)->ah_eepromRead = ath_hal_EepromDataRead;
+		AH_PRIVATE(ah)->ah_eepromWrite = NULL;
+		ah->ah_eepromdata = eepromdata;
+	}
+
+
 	/* XXX override with 9280 specific state */
 	/* override 5416 methods for our needs */
 	AH5416(ah)->ah_initPLL = ar9280InitPLL;
@@ -141,6 +151,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;
@@ -357,17 +368,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;
@@ -402,13 +420,6 @@
 	regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, 1, regWrites);
 }
 
-#define	AR_BASE_FREQ_2GHZ	2300
-#define	AR_BASE_FREQ_5GHZ	4900
-#define	AR_SPUR_FEEQ_BOUND_HT40	19
-#define	AR_SPUR_FEEQ_BOUND_HT20	10
-
-
-
 /*
  * Fill all software cached or static hardware state information.
  * Return failure if capabilities are to come from EEPROM and
@@ -443,6 +454,7 @@
 	/* Disable this so Block-ACK works correctly */
 	pCap->halHasRxSelfLinkedTail = AH_FALSE;
 	pCap->halPSPollBroken = AH_FALSE;
+	pCap->halSpectralScanSupport = AH_TRUE;
 
 	/* Hardware supports (at least) single-stream STBC TX/RX */
 	pCap->halRxStbcSupport = 1;
@@ -460,7 +472,7 @@
  * This has been disabled - having the HAL flip chainmasks on/off
  * when attempting to implement 11n disrupts things. For now, just
  * leave this flipped off and worry about implementing TX diversity
- * for legacy and MCS0-7 when 11n is fully functioning.
+ * for legacy and MCS0-15 when 11n is fully functioning.
  */
 HAL_BOOL
 ar9287SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings)
@@ -471,9 +483,12 @@
 static const char*
 ar9287Probe(uint16_t vendorid, uint16_t devid)
 {
-	if (vendorid == ATHEROS_VENDOR_ID &&
-	    (devid == AR9287_DEVID_PCI || devid == AR9287_DEVID_PCIE))
-		return "Atheros 9287";
+	if (vendorid == ATHEROS_VENDOR_ID) {
+		if (devid == AR9287_DEVID_PCI)
+			return "Atheros 9227";
+		if (devid == AR9287_DEVID_PCIE)
+			return "Atheros 9287";
+	}
 	return AH_NULL;
 }
 AH_CHIP(AR9287, ar9287Probe, ar9287Attach);

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287_cal.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287_cal.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287_cal.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
@@ -23,7 +24,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287_cal.c 222301 2011-05-26 09:15:33Z adrian $
  */
 #include "opt_ah.h"
 #include "ah.h"

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287_cal.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287_cal.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287_cal.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2008-2010 Atheros Communications Inc.
  *
@@ -22,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287_cal.h 222301 2011-05-26 09:15:33Z adrian $
  */
 #ifndef	__AR9287_CAL_H__
 #define	__AR9287_CAL_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287_olc.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287_olc.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287_olc.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
  *
@@ -22,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287_olc.c 222314 2011-05-26 16:52:37Z adrian $
  */
 #include "opt_ah.h"
 

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287_olc.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287_olc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287_olc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010 Atheros Communications, Inc.
  *
@@ -13,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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287_olc.h 222308 2011-05-26 14:29:05Z adrian $
  */
 
 #ifndef	__AR9287_OLC_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * Copyright (c) 2002-2008 Atheros Communications, Inc.
@@ -14,7 +15,7 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287_reset.c 249580 2013-04-17 07:31:53Z adrian $
  */
 
 #include "opt_ah.h"
@@ -45,70 +46,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,41 +330,44 @@
 	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	     txPowerIndexOffset = 0;
+	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;
 
+	AH5416(ah)->ah_ht40PowerIncForPdadc = 2;
+
 	/* Setup info for the actual eeprom */
-	OS_MEMZERO(ratesArray, sizeof(ratesArray));
+	OS_MEMZERO(AH5416(ah)->ah_ratesArray,
+	  sizeof(AH5416(ah)->ah_ratesArray));
 	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 );
 
 	/* XXX Assume Minor is v2 or later */
-	ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
+	AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
 
 	/* Fetch per-rate power table for the given channel */
 	if (! ar9287SetPowerPerRateTable(ah, pEepData,  chan,
-            &ratesArray[0],cfgCtl,
-            twiceAntennaReduction,
-            twiceMaxRegulatoryPower, powerLimit)) {
+	    &AH5416(ah)->ah_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;
@@ -373,11 +377,14 @@
 	ar9287SetPowerCalTable(ah, chan, &txPowerIndexOffset);
 
 	/* Calculate maximum power level */
-	maxPower = AH_MAX(ratesArray[rate6mb], ratesArray[rateHt20_0]);
-	maxPower = AH_MAX(maxPower, ratesArray[rate1l]);
+	maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb],
+	    AH5416(ah)->ah_ratesArray[rateHt20_0]);
+	maxPower = AH_MAX(maxPower,
+	    AH5416(ah)->ah_ratesArray[rate1l]);
 
 	if (IEEE80211_IS_CHAN_HT40(chan))
-		maxPower = AH_MAX(maxPower, ratesArray[rateHt40_0]);
+		maxPower = AH_MAX(maxPower,
+		    AH5416(ah)->ah_ratesArray[rateHt40_0]);
 
 	ahp->ah_tx6PowerInHalfDbm = maxPower;
 	AH_PRIVATE(ah)->ah_maxPowerLevel = maxPower;
@@ -388,18 +395,20 @@
 	 *  adjust the rate table (0 offset if rates EEPROM not loaded)
 	 */
 	/* XXX what about the pwrTableOffset? */
-	for (i = 0; i < N(ratesArray); i++) {
-		ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
+	for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) {
+		AH5416(ah)->ah_ratesArray[i] =
+		    (int16_t)(txPowerIndexOffset +
+		      AH5416(ah)->ah_ratesArray[i]);
 		/* -5 dBm offset for Merlin and later; this includes Kiwi */
-		ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
-		if (ratesArray[i] > AR5416_MAX_RATE_POWER)
-			ratesArray[i] = AR5416_MAX_RATE_POWER;
-		if (ratesArray[i] < 0)
-			ratesArray[i] = 0;
+		AH5416(ah)->ah_ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
+		if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER)
+			AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER;
+		if (AH5416(ah)->ah_ratesArray[i] < 0)
+			AH5416(ah)->ah_ratesArray[i] = 0;
 	}
 
 #ifdef AH_EEPROM_DUMP
-	ar5416PrintPowerPerRate(ah, ratesArray);
+	ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray);
 #endif
 
 	/*
@@ -410,18 +419,26 @@
 	 * XXX handle overflow/too high power level?
 	 */
 	if (IEEE80211_IS_CHAN_HT40(chan)) {
-		ratesArray[rateHt40_0] += ht40PowerIncForPdadc;
-		ratesArray[rateHt40_1] += ht40PowerIncForPdadc;
-		ratesArray[rateHt40_2] += ht40PowerIncForPdadc;
-		ratesArray[rateHt40_3] += ht40PowerIncForPdadc;
-		ratesArray[rateHt40_4] += ht40PowerIncForPdadc;
-		ratesArray[rateHt40_5] += ht40PowerIncForPdadc;
-		ratesArray[rateHt40_6] += ht40PowerIncForPdadc;
-		ratesArray[rateHt40_7] += ht40PowerIncForPdadc;
+		AH5416(ah)->ah_ratesArray[rateHt40_0] +=
+		  AH5416(ah)->ah_ht40PowerIncForPdadc;
+		AH5416(ah)->ah_ratesArray[rateHt40_1] +=
+		  AH5416(ah)->ah_ht40PowerIncForPdadc;
+		AH5416(ah)->ah_ratesArray[rateHt40_2] +=
+		  AH5416(ah)->ah_ht40PowerIncForPdadc;
+		AH5416(ah)->ah_ratesArray[rateHt40_3] +=
+		  AH5416(ah)->ah_ht40PowerIncForPdadc;
+		AH5416(ah)->ah_ratesArray[rateHt40_4] +=
+		  AH5416(ah)->ah_ht40PowerIncForPdadc;
+		AH5416(ah)->ah_ratesArray[rateHt40_5] +=
+		  AH5416(ah)->ah_ht40PowerIncForPdadc;
+		AH5416(ah)->ah_ratesArray[rateHt40_6] +=
+		  AH5416(ah)->ah_ht40PowerIncForPdadc;
+		AH5416(ah)->ah_ratesArray[rateHt40_7] +=
+		  AH5416(ah)->ah_ht40PowerIncForPdadc;
 	}
 
 	/* Write the TX power rate registers */
-	ar5416WriteTxPowerRateRegisters(ah, chan, ratesArray);
+	ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray);
 
 	return AH_TRUE;
 #undef POW_SM
@@ -476,7 +493,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 +518,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 +556,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 +573,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);

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287_reset.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287_reset.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287_reset.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010 Atheros Communications, Inc.
  *
@@ -13,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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287_reset.h 222301 2011-05-26 09:15:33Z adrian $
  */
 
 #ifndef	__AR9287_RESET_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287an.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287an.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287an.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010 Atheros Communications, Inc.
  *
@@ -13,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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287an.h 222301 2011-05-26 09:15:33Z adrian $
  */
 
 #ifndef	__AR9287AN_H__

Modified: trunk/sys/dev/ath/ath_hal/ar9002/ar9287phy.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9002/ar9287phy.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_hal/ar9002/ar9287phy.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2010 Atheros Communications, Inc.
  *
@@ -13,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$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_hal/ar9002/ar9287phy.h 222301 2011-05-26 09:15:33Z adrian $
  */
 
 #ifndef	__AR9287PHY_H__

Added: trunk/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h	                        (rev 0)
+++ trunk/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,60 @@
+/* $MidnightBSD$ */
+/*
+ * 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: stable/10/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__ */


Property changes on: trunk/sys/dev/ath/ath_hal/ar9003/ar9300_btcoex.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/ath_hal/ar9003/ar9300_devid.h
===================================================================
--- trunk/sys/dev/ath/ath_hal/ar9003/ar9300_devid.h	                        (rev 0)
+++ trunk/sys/dev/ath/ath_hal/ar9003/ar9300_devid.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,79 @@
+/* $MidnightBSD$ */
+/*
+ * Copyright (c) 2012, Qualcomm Atheros, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * 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: stable/10/sys/dev/ath/ath_hal/ar9003/ar9300_devid.h 250166 2013-05-02 00:59:39Z adrian $
+ *
+ */
+#ifndef	__AR9300_DEVID_H__
+#define	__AR9300_DEVID_H__
+
+/*
+ * AR9380 HAL device IDs.
+ */
+
+/*
+ * MAC Version and Revision
+ */
+#define	AR_SREV_VERSION_AR9380		0x1C0
+#define	AR_SREV_VERSION_AR9580		0x1C0
+#define	AR_SREV_VERSION_AR9460		0x280
+#define	AR_SREV_VERSION_QCA9565		0x2c0
+
+#define	AR_SREV_VERSION_AR9330		0x200
+#define	AR_SREV_VERSION_AR9340		0x300
+#define	AR_SREV_VERSION_QCA9550		0x400
+#define	AR_SREV_VERSION_AR9485		0x240
+
+#define	AR_SREV_REVISION_AR9380_10	0	/* AR9380 1.0 */
+#define	AR_SREV_REVISION_AR9380_20	2	/* AR9380 2.0/2.1 */
+#define	AR_SREV_REVISION_AR9380_22	3	/* AR9380 2.2 */
+#define	AR_SREV_REVISION_AR9580_10	4	/* AR9580/Peacock 1.0 */
+
+#define	AR_SREV_REVISION_AR9330_10	0	/* AR9330 1.0 */
+#define	AR_SREV_REVISION_AR9330_11	1	/* AR9330 1.1 */
+#define	AR_SREV_REVISION_AR9330_12	2	/* AR9330 1.2 */
+#define	AR_SREV_REVISION_AR9330_11_MASK	0xf	/* AR9330 1.1 revision mask */
+
+#define	AR_SREV_REVISION_AR9485_10	0	/* AR9485 1.0 */
+#define	AR_SREV_REVISION_AR9485_11	1	/* AR9485 1.1 */
+
+#define	AR_SREV_REVISION_AR9340_10	0	/* AR9340 1.0 */
+#define	AR_SREV_REVISION_AR9340_11	1	/* AR9340 1.1 */
+#define	AR_SREV_REVISION_AR9340_12	2	/* AR9340 1.2 */
+#define	AR_SREV_REVISION_AR9340_MASK	0xf	/* AR9340 revision mask */
+
+#define	AR_SREV_REVISION_AR9460_10	0	/* AR946x 1.0 */
+
+#endif	/* __AR9300_DEVID_H__ */


Property changes on: trunk/sys/dev/ath/ath_hal/ar9003/ar9300_devid.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/ath_rate/amrr/amrr.c
===================================================================
--- trunk/sys/dev/ath/ath_rate/amrr/amrr.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_rate/amrr/amrr.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2004 INRIA
  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
@@ -37,7 +38,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/ath_rate/amrr/amrr.c 238633 2012-07-20 01:36:02Z adrian $");
 
 /*
  * AMRR rate control. See:
@@ -45,6 +46,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"
 
@@ -122,19 +124,21 @@
  */
 void
 ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an,
-    uint8_t rix0, uint8_t *rix, uint8_t *try)
+    uint8_t rix0, struct ath_rc_series *rc)
 {
 	struct amrr_node *amn = ATH_NODE_AMRR(an);
 
-/*	rix[0] = amn->amn_tx_rate0; */
-	rix[1] = amn->amn_tx_rate1;
-	rix[2] = amn->amn_tx_rate2;
-	rix[3] = amn->amn_tx_rate3;
+	rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0;
 
-	try[0] = amn->amn_tx_try0;
-	try[1] = amn->amn_tx_try1;
-	try[2] = amn->amn_tx_try2;
-	try[3] = amn->amn_tx_try3;
+	rc[0].rix = amn->amn_tx_rate0;
+	rc[1].rix = amn->amn_tx_rate1;
+	rc[2].rix = amn->amn_tx_rate2;
+	rc[3].rix = amn->amn_tx_rate3;
+
+	rc[0].tries = amn->amn_tx_try0;
+	rc[1].tries = amn->amn_tx_try1;
+	rc[2].tries = amn->amn_tx_try2;
+	rc[3].tries = amn->amn_tx_try3;
 }
 
 
@@ -153,10 +157,10 @@
 
 void
 ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
-	const struct ath_buf *bf)
+	const struct ath_rc_series *rc, const struct ath_tx_status *ts,
+	int frame_size, int nframes, int nbad)
 {
 	struct amrr_node *amn = ATH_NODE_AMRR(an);
-	const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
 	int sr = ts->ts_shortretry;
 	int lr = ts->ts_longretry;
 	int retry_count = sr + lr;
@@ -418,6 +422,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)
 {

Modified: trunk/sys/dev/ath/ath_rate/amrr/amrr.h
===================================================================
--- trunk/sys/dev/ath/ath_rate/amrr/amrr.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_rate/amrr/amrr.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2004 INRIA
  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
@@ -34,7 +35,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_rate/amrr/amrr.h 178354 2008-04-20 20:35:46Z sam $
  */
 
 #ifndef _DEV_ATH_RATE_AMRR_H

Modified: trunk/sys/dev/ath/ath_rate/onoe/onoe.c
===================================================================
--- trunk/sys/dev/ath/ath_rate/onoe/onoe.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_rate/onoe/onoe.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -28,11 +29,12 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: stable/10/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"
 
@@ -130,19 +132,21 @@
  */
 void
 ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an,
-    uint8_t rix0, uint8_t *rix, uint8_t *try)
+    uint8_t rix0, struct ath_rc_series *rc)
 {
 	struct onoe_node *on = ATH_NODE_ONOE(an);
 
-/*	rix[0] = on->on_tx_rate0; */
-	rix[1] = on->on_tx_rate1;
-	rix[2] = on->on_tx_rate2;
-	rix[3] = on->on_tx_rate3;
+	rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0;
 
-	try[0] = on->on_tx_try0;
-	try[1] = 2;
-	try[2] = 2;
-	try[3] = 2;
+	rc[0].rix = on->on_tx_rate0;
+	rc[1].rix = on->on_tx_rate1;
+	rc[2].rix = on->on_tx_rate2;
+	rc[3].rix = on->on_tx_rate3;
+
+	rc[0].tries = on->on_tx_try0;
+	rc[1].tries = 2;
+	rc[2].tries = 2;
+	rc[3].tries = 2;
 }
 
 void
@@ -160,10 +164,10 @@
 
 void
 ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
-	const struct ath_buf *bf)
+	const struct ath_rc_series *rc, const struct ath_tx_status *ts,
+	int frame_size, int nframes, int nbad)
 {
 	struct onoe_node *on = ATH_NODE_ONOE(an);
-	const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
 
 	if (ts->ts_status == 0)
 		on->on_tx_ok++;
@@ -404,6 +408,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)
 {

Modified: trunk/sys/dev/ath/ath_rate/onoe/onoe.h
===================================================================
--- trunk/sys/dev/ath/ath_rate/onoe/onoe.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_rate/onoe/onoe.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_rate/onoe/onoe.h 178354 2008-04-20 20:35:46Z sam $
  */
 
 /*

Modified: trunk/sys/dev/ath/ath_rate/sample/sample.c
===================================================================
--- trunk/sys/dev/ath/ath_rate/sample/sample.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_rate/sample/sample.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2005 John Bicket
  * All rights reserved.
@@ -36,13 +37,15 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/ath_rate/sample/sample.c 247372 2013-02-27 04:33:06Z adrian $");
 
 /*
  * John Bicket's SampleRate control algorithm.
  */
+#include "opt_ath.h"
 #include "opt_inet.h"
 #include "opt_wlan.h"
+#include "opt_ah.h"
 
 #include <sys/param.h>
 #include <sys/systm.h> 
@@ -103,8 +106,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) 
 {
@@ -126,12 +127,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)
 {
@@ -146,6 +141,8 @@
 static int
 dot11rate(const HAL_RATE_TABLE *rt, int rix)
 {
+	if (rix < 0)
+		return -1;
 	return rt->info[rix].phy == IEEE80211_T_HT ?
 	    rt->info[rix].dot11Rate : (rt->info[rix].dot11Rate & IEEE80211_RATE_VAL) / 2;
 }
@@ -153,6 +150,8 @@
 static const char *
 dot11rate_label(const HAL_RATE_TABLE *rt, int rix)
 {
+	if (rix < 0)
+		return "";
 	return rt->info[rix].phy == IEEE80211_T_HT ? "MCS" : "Mb ";
 }
 
@@ -165,12 +164,13 @@
     int size_bin, int require_acked_before)
 {
 	struct sample_node *sn = ATH_NODE_SAMPLE(an);
-        int best_rate_rix, best_rate_tt;
-	uint32_t mask;
-	int rix, tt;
+        int best_rate_rix, best_rate_tt, best_rate_pct;
+	uint64_t mask;
+	int rix, tt, pct;
 
         best_rate_rix = 0;
         best_rate_tt = 0;
+	best_rate_pct = 0;
 	for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
 		if ((mask & 1) == 0)		/* not a supported rate */
 			continue;
@@ -187,14 +187,55 @@
 		     !sn->stats[size_bin][rix].packets_acked))
 			continue;
 
+		/* Calculate percentage if possible */
+		if (sn->stats[size_bin][rix].total_packets > 0) {
+			pct = sn->stats[size_bin][rix].ewma_pct;
+		} else {
+			/* XXX for now, assume 95% ok */
+			pct = 95;
+		}
+
 		/* don't use a bit-rate that has been failing */
 		if (sn->stats[size_bin][rix].successive_failures > 3)
 			continue;
 
-		if (best_rate_tt == 0 || tt < best_rate_tt) {
-			best_rate_tt = tt;
-			best_rate_rix = rix;
+		/*
+		 * For HT, Don't use a bit rate that is much more
+		 * lossy than the best.
+		 *
+		 * XXX this isn't optimal; it's just designed to
+		 * eliminate rates that are going to be obviously
+		 * worse.
+		 */
+		if (an->an_node.ni_flags & IEEE80211_NODE_HT) {
+			if (best_rate_pct > (pct + 50))
+				continue;
 		}
+
+		/*
+		 * For non-MCS rates, use the current average txtime for
+		 * comparison.
+		 */
+		if (! (an->an_node.ni_flags & IEEE80211_NODE_HT)) {
+			if (best_rate_tt == 0 || tt <= best_rate_tt) {
+				best_rate_tt = tt;
+				best_rate_rix = rix;
+				best_rate_pct = pct;
+			}
+		}
+
+		/*
+		 * Since 2 stream rates have slightly higher TX times,
+		 * allow a little bit of leeway. This should later
+		 * be abstracted out and properly handled.
+		 */
+		if (an->an_node.ni_flags & IEEE80211_NODE_HT) {
+			if (best_rate_tt == 0 || (tt * 8 <= best_rate_tt * 10)) {
+				best_rate_tt = tt;
+				best_rate_rix = rix;
+				best_rate_pct = pct;
+			}
+		}
         }
         return (best_rate_tt ? best_rate_rix : -1);
 }
@@ -211,7 +252,7 @@
 	struct sample_node *sn = ATH_NODE_SAMPLE(an);
 	int current_rix, rix;
 	unsigned current_tt;
-	uint32_t mask;
+	uint64_t mask;
 	
 	current_rix = sn->current_rix[size_bin];
 	if (current_rix < 0) {
@@ -223,9 +264,9 @@
 	current_tt = sn->stats[size_bin][current_rix].average_tx_time;
 
 	rix = sn->last_sample_rix[size_bin]+1;	/* next sample rate */
-	mask = sn->ratemask &~ (1<<current_rix);/* don't sample current rate */
+	mask = sn->ratemask &~ ((uint64_t) 1<<current_rix);/* don't sample current rate */
 	while (mask != 0) {
-		if ((mask & (1<<rix)) == 0) {	/* not a supported rate */
+		if ((mask & ((uint64_t) 1<<rix)) == 0) {	/* not a supported rate */
 	nextrate:
 			if (++rix >= rt->rateCount)
 				rix = 0;
@@ -232,16 +273,33 @@
 			continue;
 		}
 
+		/*
+		 * The following code stops trying to sample
+		 * non-MCS rates when speaking to an MCS node.
+		 * However, at least for CCK rates in 2.4GHz mode,
+		 * the non-MCS rates MAY actually provide better
+		 * PER at the very far edge of reception.
+		 *
+		 * However! Until ath_rate_form_aggr() grows
+		 * some logic to not form aggregates if the
+		 * selected rate is non-MCS, this won't work.
+		 *
+		 * So don't disable this code until you've taught
+		 * ath_rate_form_aggr() to drop out if any of
+		 * the selected rates are non-MCS.
+		 */
+#if 1
 		/* if the node is HT and the rate isn't HT, don't bother sample */
 		if ((an->an_node.ni_flags & IEEE80211_NODE_HT) &&
 		    (rt->info[rix].phy != IEEE80211_T_HT)) {
-			mask &= ~(1<<rix);
+			mask &= ~((uint64_t) 1<<rix);
 			goto nextrate;
 		}
+#endif
 
 		/* this bit-rate is always worse than the current one */
 		if (sn->stats[size_bin][rix].perfect_tx_time > current_tt) {
-			mask &= ~(1<<rix);
+			mask &= ~((uint64_t) 1<<rix);
 			goto nextrate;
 		}
 
@@ -248,14 +306,26 @@
 		/* rarely sample bit-rates that fail a lot */
 		if (sn->stats[size_bin][rix].successive_failures > ssc->max_successive_failures &&
 		    ticks - sn->stats[size_bin][rix].last_tx < ssc->stale_failure_timeout) {
-			mask &= ~(1<<rix);
+			mask &= ~((uint64_t) 1<<rix);
 			goto nextrate;
 		}
 
+		/*
+		 * For HT, only sample a few rates on either side of the
+		 * current rix; there's quite likely a lot of them.
+		 */
+		if (an->an_node.ni_flags & IEEE80211_NODE_HT) {
+			if (rix < (current_rix - 3) ||
+			    rix > (current_rix + 3)) {
+				mask &= ~((uint64_t) 1<<rix);
+				goto nextrate;
+			}
+		}
+
 		/* Don't sample more than 2 rates higher for rates > 11M for non-HT rates */
 		if (! (an->an_node.ni_flags & IEEE80211_NODE_HT)) {
 			if (DOT11RATE(rix) > 2*11 && rix > current_rix + 2) {
-				mask &= ~(1<<rix);
+				mask &= ~((uint64_t) 1<<rix);
 				goto nextrate;
 			}
 		}
@@ -315,8 +385,98 @@
 	}
 }
 
+/*
+ * Pick a non-HT rate to begin using.
+ */
+static int
+ath_rate_pick_seed_rate_legacy(struct ath_softc *sc, struct ath_node *an,
+    int frameLen)
+{
+#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
+#define	MCS(ix)		(rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
+#define	RATE(ix)	(DOT11RATE(ix) / 2)
+	int rix = -1;
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	struct sample_node *sn = ATH_NODE_SAMPLE(an);
+	const int size_bin = size_to_bin(frameLen);
 
+	/* no packet has been sent successfully yet */
+	for (rix = rt->rateCount-1; rix > 0; rix--) {
+		if ((sn->ratemask & ((uint64_t) 1<<rix)) == 0)
+			continue;
 
+		/* Skip HT rates */
+		if (rt->info[rix].phy == IEEE80211_T_HT)
+			continue;
+
+		/*
+		 * Pick the highest rate <= 36 Mbps
+		 * that hasn't failed.
+		 */
+		if (DOT11RATE(rix) <= 72 &&
+		    sn->stats[size_bin][rix].successive_failures == 0) {
+			break;
+		}
+	}
+	return rix;
+#undef	RATE
+#undef	MCS
+#undef	DOT11RATE
+}
+
+/*
+ * Pick a HT rate to begin using.
+ *
+ * Don't use any non-HT rates; only consider HT rates.
+ */
+static int
+ath_rate_pick_seed_rate_ht(struct ath_softc *sc, struct ath_node *an,
+    int frameLen)
+{
+#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
+#define	MCS(ix)		(rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
+#define	RATE(ix)	(DOT11RATE(ix) / 2)
+	int rix = -1, ht_rix = -1;
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	struct sample_node *sn = ATH_NODE_SAMPLE(an);
+	const int size_bin = size_to_bin(frameLen);
+
+	/* no packet has been sent successfully yet */
+	for (rix = rt->rateCount-1; rix > 0; rix--) {
+		/* Skip rates we can't use */
+		if ((sn->ratemask & ((uint64_t) 1<<rix)) == 0)
+			continue;
+
+		/* Keep a copy of the last seen HT rate index */
+		if (rt->info[rix].phy == IEEE80211_T_HT)
+			ht_rix = rix;
+
+		/* Skip non-HT rates */
+		if (rt->info[rix].phy != IEEE80211_T_HT)
+			continue;
+
+		/*
+		 * Pick a medium-speed rate regardless of stream count
+		 * which has not seen any failures. Higher rates may fail;
+		 * we'll try them later.
+		 */
+		if (((MCS(rix) & 0x7) <= 4) &&
+		    sn->stats[size_bin][rix].successive_failures == 0) {
+			break;
+		}
+	}
+
+	/*
+	 * If all the MCS rates have successive failures, rix should be
+	 * > 0; otherwise use the lowest MCS rix (hopefully MCS 0.)
+	 */
+	return MAX(rix, ht_rix);
+#undef	RATE
+#undef	MCS
+#undef	DOT11RATE
+}
+
+
 void
 ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
 		  int shortPreamble, size_t frameLen,
@@ -336,6 +496,14 @@
 
 	ath_rate_update_static_rix(sc, &an->an_node);
 
+	if (sn->currates != sc->sc_currates) {
+		device_printf(sc->sc_dev, "%s: currates != sc_currates!\n",
+		    __func__);
+		rix = 0;
+		*try0 = ATH_TXMAXTRY;
+		goto done;
+	}
+
 	if (sn->static_rix != -1) {
 		rix = sn->static_rix;
 		*try0 = ATH_TXMAXTRY;
@@ -342,8 +510,10 @@
 		goto done;
 	}
 
-	/* XXX TODO: this doesn't know about 11gn vs 11g protection; teach it */
-	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT);
+	mrr = sc->sc_mrretry;
+	/* XXX check HT protmode too */
+	if (mrr && (ic->ic_flags & IEEE80211_F_USEPROT && !sc->sc_mrrprot))
+		mrr = 0;
 
 	best_rix = pick_best_rate(an, rt, size_bin, !mrr);
 	if (best_rix >= 0) {
@@ -358,9 +528,14 @@
 	if (sn->sample_tt[size_bin] < average_tx_time * (sn->packets_since_sample[size_bin]*ssc->sample_rate/100)) {
 		rix = pick_sample_rate(ssc, an, rt, size_bin);
 		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
-		     &an->an_node, "size %u sample rate %d current rate %d",
-		     bin_to_size(size_bin), RATE(rix),
-		     RATE(sn->current_rix[size_bin]));
+		     &an->an_node, "att %d sample_tt %d size %u sample rate %d %s current rate %d %s",
+		     average_tx_time,
+		     sn->sample_tt[size_bin],
+		     bin_to_size(size_bin),
+		     dot11rate(rt, rix),
+		     dot11rate_label(rt, rix),
+		     dot11rate(rt, sn->current_rix[size_bin]),
+		     dot11rate_label(rt, sn->current_rix[size_bin]));
 		if (rix != sn->current_rix[size_bin]) {
 			sn->current_sample_rix[size_bin] = rix;
 		} else {
@@ -371,29 +546,58 @@
 		change_rates = 0;
 		if (!sn->packets_sent[size_bin] || best_rix == -1) {
 			/* no packet has been sent successfully yet */
-			for (rix = rt->rateCount-1; rix > 0; rix--) {
-				if ((sn->ratemask & (1<<rix)) == 0)
-					continue;
-				/* 
-				 * Pick the highest rate <= 36 Mbps
-				 * that hasn't failed.
-				 */
-				if (DOT11RATE(rix) <= 72 && 
-				    sn->stats[size_bin][rix].successive_failures == 0) {
-					break;
-				}
-			}
 			change_rates = 1;
-			best_rix = rix;
+			if (an->an_node.ni_flags & IEEE80211_NODE_HT)
+				best_rix =
+				    ath_rate_pick_seed_rate_ht(sc, an, frameLen);
+			else
+				best_rix =
+				    ath_rate_pick_seed_rate_legacy(sc, an, frameLen);
 		} else if (sn->packets_sent[size_bin] < 20) {
 			/* let the bit-rate switch quickly during the first few packets */
+			IEEE80211_NOTE(an->an_node.ni_vap,
+			    IEEE80211_MSG_RATECTL, &an->an_node,
+			    "%s: switching quickly..", __func__);
 			change_rates = 1;
 		} else if (ticks - ssc->min_switch > sn->ticks_since_switch[size_bin]) {
 			/* min_switch seconds have gone by */
+			IEEE80211_NOTE(an->an_node.ni_vap,
+			    IEEE80211_MSG_RATECTL, &an->an_node,
+			    "%s: min_switch %d > ticks_since_switch %d..",
+			    __func__, ticks - ssc->min_switch, sn->ticks_since_switch[size_bin]);
 			change_rates = 1;
-		} else if (2*average_tx_time < sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time) {
+		} else if ((! (an->an_node.ni_flags & IEEE80211_NODE_HT)) &&
+		    (2*average_tx_time < sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time)) {
 			/* the current bit-rate is twice as slow as the best one */
+			IEEE80211_NOTE(an->an_node.ni_vap,
+			    IEEE80211_MSG_RATECTL, &an->an_node,
+			    "%s: 2x att (= %d) < cur_rix att %d",
+			    __func__,
+			    2 * average_tx_time, sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time);
 			change_rates = 1;
+		} else if ((an->an_node.ni_flags & IEEE80211_NODE_HT)) {
+			int cur_rix = sn->current_rix[size_bin];
+			int cur_att = sn->stats[size_bin][cur_rix].average_tx_time;
+			/*
+			 * If the node is HT, upgrade it if the MCS rate is
+			 * higher and the average tx time is within 20% of
+			 * the current rate. It can fail a little.
+			 *
+			 * This is likely not optimal!
+			 */
+#if 0
+			printf("cur rix/att %x/%d, best rix/att %x/%d\n",
+			    MCS(cur_rix), cur_att, MCS(best_rix), average_tx_time);
+#endif
+			if ((MCS(best_rix) > MCS(cur_rix)) &&
+			    (average_tx_time * 8) <= (cur_att * 10)) {
+				IEEE80211_NOTE(an->an_node.ni_vap,
+				    IEEE80211_MSG_RATECTL, &an->an_node,
+				    "%s: HT: best_rix 0x%d > cur_rix 0x%x, average_tx_time %d, cur_att %d",
+				    __func__,
+				    MCS(best_rix), MCS(cur_rix), average_tx_time, cur_att);
+				change_rates = 1;
+			}
 		}
 
 		sn->packets_since_sample[size_bin]++;
@@ -428,6 +632,20 @@
 	}
 	*try0 = mrr ? sn->sched[rix].t0 : ATH_TXMAXTRY;
 done:
+
+	/*
+	 * This bug totally sucks and should be fixed.
+	 *
+	 * For now though, let's not panic, so we can start to figure
+	 * out how to better reproduce it.
+	 */
+	if (rix < 0 || rix >= rt->rateCount) {
+		printf("%s: ERROR: rix %d out of bounds (rateCount=%d)\n",
+		    __func__,
+		    rix,
+		    rt->rateCount);
+		    rix = 0;	/* XXX just default for now */
+	}
 	KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix));
 
 	*rix0 = rix;
@@ -445,22 +663,25 @@
  */
 void
 ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an,
-    uint8_t rix0, uint8_t *rix, uint8_t *try)
+    uint8_t rix0, struct ath_rc_series *rc)
 {
 	struct sample_node *sn = ATH_NODE_SAMPLE(an);
 	const struct txschedule *sched = &sn->sched[rix0];
 
-	KASSERT(rix0 == sched->r0, ("rix0 (%x) != sched->r0 (%x)!\n", rix0, sched->r0));
+	KASSERT(rix0 == sched->r0, ("rix0 (%x) != sched->r0 (%x)!\n",
+	    rix0, sched->r0));
 
-/*	rix[0] = sched->r0; */
-	rix[1] = sched->r1;
-	rix[2] = sched->r2;
-	rix[3] = sched->r3;
+	rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0;
 
-	try[0] = sched->t0;
-	try[1] = sched->t1;
-	try[2] = sched->t2;
-	try[3] = sched->t3;
+	rc[0].rix = sched->r0;
+	rc[1].rix = sched->r1;
+	rc[2].rix = sched->r2;
+	rc[3].rix = sched->r3;
+
+	rc[0].tries = sched->t0;
+	rc[1].tries = sched->t1;
+	rc[2].tries = sched->t2;
+	rc[3].tries = sched->t3;
 }
 
 void
@@ -495,14 +716,19 @@
 		  int rix1, int tries1,
 		  int rix2, int tries2,
 		  int rix3, int tries3,
-		  int short_tries, int tries, int status)
+		  int short_tries, int tries, int status,
+		  int nframes, int nbad)
 {
 	struct sample_node *sn = ATH_NODE_SAMPLE(an);
 	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
+#ifdef IEEE80211_DEBUG
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+#endif
 	const int size_bin = size_to_bin(frame_size);
 	const int size = bin_to_size(size_bin);
 	int tt, tries_so_far;
 	int is_ht40 = (an->an_node.ni_chw == 40);
+	int pct;
 
 	if (!IS_RATE_DEFINED(sn, rix0))
 		return;
@@ -537,7 +763,7 @@
 		/* just average the first few packets */
 		int avg_tx = sn->stats[size_bin][rix0].average_tx_time;
 		int packets = sn->stats[size_bin][rix0].total_packets;
-		sn->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+1);
+		sn->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+nframes);
 	} else {
 		/* use a ewma */
 		sn->stats[size_bin][rix0].average_tx_time = 
@@ -545,9 +771,17 @@
 			 (tt * (100 - ssc->smoothing_rate))) / 100;
 	}
 	
-	if (status != 0) {
+	/*
+	 * XXX Don't mark the higher bit rates as also having failed; as this
+	 * unfortunately stops those rates from being tasted when trying to
+	 * TX. This happens with 11n aggregation.
+	 */
+	if (nframes == nbad) {
+#if 0
 		int y;
-		sn->stats[size_bin][rix0].successive_failures++;
+#endif
+		sn->stats[size_bin][rix0].successive_failures += nbad;
+#if 0
 		for (y = size_bin+1; y < NUM_PACKET_SIZE_BINS; y++) {
 			/*
 			 * Also say larger packets failed since we
@@ -554,29 +788,54 @@
 			 * assume if a small packet fails at a
 			 * bit-rate then a larger one will also.
 			 */
-			sn->stats[y][rix0].successive_failures++;
+			sn->stats[y][rix0].successive_failures += nbad;
 			sn->stats[y][rix0].last_tx = ticks;
 			sn->stats[y][rix0].tries += tries;
-			sn->stats[y][rix0].total_packets++;
+			sn->stats[y][rix0].total_packets += nframes;
 		}
+#endif
 	} else {
-		sn->stats[size_bin][rix0].packets_acked++;
+		sn->stats[size_bin][rix0].packets_acked += (nframes - nbad);
 		sn->stats[size_bin][rix0].successive_failures = 0;
 	}
 	sn->stats[size_bin][rix0].tries += tries;
 	sn->stats[size_bin][rix0].last_tx = ticks;
-	sn->stats[size_bin][rix0].total_packets++;
+	sn->stats[size_bin][rix0].total_packets += nframes;
 
+	/* update EWMA for this rix */
+
+	/* Calculate percentage based on current rate */
+	if (nframes == 0)
+		nframes = nbad = 1;
+	pct = ((nframes - nbad) * 1000) / nframes;
+
+	if (sn->stats[size_bin][rix0].total_packets <
+	    ssc->smoothing_minpackets) {
+		/* just average the first few packets */
+		int a_pct = (sn->stats[size_bin][rix0].packets_acked * 1000) /
+		    (sn->stats[size_bin][rix0].total_packets);
+		sn->stats[size_bin][rix0].ewma_pct = a_pct;
+	} else {
+		/* use a ewma */
+		sn->stats[size_bin][rix0].ewma_pct =
+			((sn->stats[size_bin][rix0].ewma_pct * ssc->smoothing_rate) +
+			 (pct * (100 - ssc->smoothing_rate))) / 100;
+	}
+
+
 	if (rix0 == sn->current_sample_rix[size_bin]) {
 		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
 		   &an->an_node,
-"%s: size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)", 
+"%s: size %d %s sample rate %d %s tries (%d/%d) tt %d avg_tt (%d/%d) nfrm %d nbad %d", 
 		    __func__, 
 		    size,
 		    status ? "FAIL" : "OK",
-		    rix0, short_tries, tries, tt, 
+		    dot11rate(rt, rix0),
+		    dot11rate_label(rt, rix0),
+		    short_tries, tries, tt, 
 		    sn->stats[size_bin][rix0].average_tx_time,
-		    sn->stats[size_bin][rix0].perfect_tx_time);
+		    sn->stats[size_bin][rix0].perfect_tx_time,
+		    nframes, nbad);
 		sn->sample_tt[size_bin] = tt;
 		sn->current_sample_rix[size_bin] = -1;
 	}
@@ -591,21 +850,26 @@
 
 void
 ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
-	const struct ath_buf *bf)
+	const struct ath_rc_series *rc, const struct ath_tx_status *ts,
+	int frame_size, int nframes, int nbad)
 {
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ieee80211com *ic = ifp->if_l2com;
 	struct sample_node *sn = ATH_NODE_SAMPLE(an);
-	const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
-	const struct ath_desc *ds0 = &bf->bf_desc[0];
-	int final_rix, short_tries, long_tries, frame_size;
+	int final_rix, short_tries, long_tries;
 	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	int status = ts->ts_status;
 	int mrr;
 
 	final_rix = rt->rateCodeToIndex[ts->ts_rate];
 	short_tries = ts->ts_shortretry;
 	long_tries = ts->ts_longretry + 1;
-	frame_size = ds0->ds_ctl0 & 0x0fff; /* low-order 12 bits of ds_ctl0 */
+
+	if (nframes == 0) {
+		device_printf(sc->sc_dev, "%s: nframes=0?\n", __func__);
+		return;
+	}
+
 	if (frame_size == 0)		    /* NB: should not happen */
 		frame_size = 1500;
 
@@ -615,14 +879,20 @@
 		    "%s: size %d %s rate/try %d/%d no rates yet", 
 		    __func__,
 		    bin_to_size(size_to_bin(frame_size)),
-		    ts->ts_status ? "FAIL" : "OK",
+		    status ? "FAIL" : "OK",
 		    short_tries, long_tries);
 		return;
 	}
-	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT);
+	mrr = sc->sc_mrretry;
+	/* XXX check HT protmode too */
+	if (mrr && (ic->ic_flags & IEEE80211_F_USEPROT && !sc->sc_mrrprot))
+		mrr = 0;
+
 	if (!mrr || ts->ts_finaltsi == 0) {
 		if (!IS_RATE_DEFINED(sn, final_rix)) {
-			badrate(ifp, 0, ts->ts_rate, long_tries, ts->ts_status);
+			device_printf(sc->sc_dev, "%s: ts_rate=%d ts_finaltsi=%d\n",
+			    __func__, ts->ts_rate, ts->ts_finaltsi);
+			badrate(ifp, 0, ts->ts_rate, long_tries, status);
 			return;
 		}
 		/*
@@ -629,20 +899,22 @@
 		 * Only one rate was used; optimize work.
 		 */
 		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
-		     &an->an_node, "%s: size %d (%d bytes) %s rate/try %d %s/%d/%d",
+		     &an->an_node, "%s: size %d (%d bytes) %s rate/short/long %d %s/%d/%d nframes/nbad [%d/%d]",
 		     __func__,
 		     bin_to_size(size_to_bin(frame_size)),
 		     frame_size,
-		     ts->ts_status ? "FAIL" : "OK",
-		     dot11rate(rt, final_rix), dot11rate_label(rt, final_rix), short_tries, long_tries);
+		     status ? "FAIL" : "OK",
+		     dot11rate(rt, final_rix), dot11rate_label(rt, final_rix),
+		     short_tries, long_tries, nframes, nbad);
 		update_stats(sc, an, frame_size, 
 			     final_rix, long_tries,
 			     0, 0,
 			     0, 0,
 			     0, 0,
-			     short_tries, long_tries, ts->ts_status);
+			     short_tries, long_tries, status,
+			     nframes, nbad);
+
 	} else {
-		int hwrates[4], tries[4], rix[4];
 		int finalTSIdx = ts->ts_finaltsi;
 		int i;
 
@@ -649,29 +921,31 @@
 		/*
 		 * Process intermediate rates that failed.
 		 */
-		ath_hal_gettxcompletionrates(sc->sc_ah, ds0, hwrates, tries);
 
-		for (i = 0; i < 4; i++) {
-			rix[i] = rt->rateCodeToIndex[hwrates[i]];
-		}
-
 		IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL,
 		    &an->an_node,
-"%s: size %d (%d bytes) finaltsidx %d tries %d %s rate/try [%d %s/%d %d %s/%d %d %s/%d %d %s/%d]", 
+"%s: size %d (%d bytes) finaltsidx %d short %d long %d %s rate/try [%d %s/%d %d %s/%d %d %s/%d %d %s/%d] nframes/nbad [%d/%d]", 
 		     __func__,
 		     bin_to_size(size_to_bin(frame_size)),
 		     frame_size,
 		     finalTSIdx,
-		     long_tries, 
-		     ts->ts_status ? "FAIL" : "OK",
-		     dot11rate(rt, rix[0]), dot11rate_label(rt, rix[0]), tries[0],
-		     dot11rate(rt, rix[1]), dot11rate_label(rt, rix[1]), tries[1],
-		     dot11rate(rt, rix[2]), dot11rate_label(rt, rix[2]), tries[2],
-		     dot11rate(rt, rix[3]), dot11rate_label(rt, rix[3]), tries[3]);
+		     short_tries,
+		     long_tries,
+		     status ? "FAIL" : "OK",
+		     dot11rate(rt, rc[0].rix),
+		      dot11rate_label(rt, rc[0].rix), rc[0].tries,
+		     dot11rate(rt, rc[1].rix),
+		      dot11rate_label(rt, rc[1].rix), rc[1].tries,
+		     dot11rate(rt, rc[2].rix),
+		      dot11rate_label(rt, rc[2].rix), rc[2].tries,
+		     dot11rate(rt, rc[3].rix),
+		      dot11rate_label(rt, rc[3].rix), rc[3].tries,
+		     nframes, nbad);
 
 		for (i = 0; i < 4; i++) {
-			if (tries[i] && !IS_RATE_DEFINED(sn, rix[i]))
-				badrate(ifp, 0, hwrates[i], tries[i], ts->ts_status);
+			if (rc[i].tries && !IS_RATE_DEFINED(sn, rc[i].rix))
+				badrate(ifp, 0, rc[i].ratecode, rc[i].tries,
+				    status);
 		}
 
 		/*
@@ -681,47 +955,51 @@
 		 * sample higher rates 1 try at a time doing so
 		 * may unfairly penalize them.
 		 */
-		if (tries[0]) {
-			update_stats(sc, an, frame_size, 
-				     rix[0], tries[0], 
-				     rix[1], tries[1], 
-				     rix[2], tries[2], 
-				     rix[3], tries[3], 
-				     short_tries, long_tries, 
-				     long_tries > tries[0]);
-			long_tries -= tries[0];
+		if (rc[0].tries) {
+			update_stats(sc, an, frame_size,
+				     rc[0].rix, rc[0].tries,
+				     rc[1].rix, rc[1].tries,
+				     rc[2].rix, rc[2].tries,
+				     rc[3].rix, rc[3].tries,
+				     short_tries, long_tries,
+				     long_tries > rc[0].tries,
+				     nframes, nbad);
+			long_tries -= rc[0].tries;
 		}
 		
-		if (tries[1] && finalTSIdx > 0) {
-			update_stats(sc, an, frame_size, 
-				     rix[1], tries[1], 
-				     rix[2], tries[2], 
-				     rix[3], tries[3], 
-				     0, 0, 
-				     short_tries, long_tries, 
-				     ts->ts_status);
-			long_tries -= tries[1];
+		if (rc[1].tries && finalTSIdx > 0) {
+			update_stats(sc, an, frame_size,
+				     rc[1].rix, rc[1].tries,
+				     rc[2].rix, rc[2].tries,
+				     rc[3].rix, rc[3].tries,
+				     0, 0,
+				     short_tries, long_tries,
+				     status,
+				     nframes, nbad);
+			long_tries -= rc[1].tries;
 		}
 
-		if (tries[2] && finalTSIdx > 1) {
-			update_stats(sc, an, frame_size, 
-				     rix[2], tries[2], 
-				     rix[3], tries[3], 
+		if (rc[2].tries && finalTSIdx > 1) {
+			update_stats(sc, an, frame_size,
+				     rc[2].rix, rc[2].tries,
+				     rc[3].rix, rc[3].tries,
 				     0, 0,
 				     0, 0,
-				     short_tries, long_tries, 
-				     ts->ts_status);
-			long_tries -= tries[2];
+				     short_tries, long_tries,
+				     status,
+				     nframes, nbad);
+			long_tries -= rc[2].tries;
 		}
 
-		if (tries[3] && finalTSIdx > 2) {
-			update_stats(sc, an, frame_size, 
-				     rix[3], tries[3],
+		if (rc[3].tries && finalTSIdx > 2) {
+			update_stats(sc, an, frame_size,
+				     rc[3].rix, rc[3].tries,
 				     0, 0,
 				     0, 0,
 				     0, 0,
-				     short_tries, long_tries, 
-				     ts->ts_status);
+				     short_tries, long_tries,
+				     status,
+				     nframes, nbad);
 		}
 	}
 }
@@ -766,6 +1044,7 @@
 
 	KASSERT(sc->sc_curmode < IEEE80211_MODE_MAX+2,
 	    ("curmode %u", sc->sc_curmode));
+
 	sn->sched = mrr_schedules[sc->sc_curmode];
 	KASSERT(sn->sched != NULL,
 	    ("no mrr schedule for mode %u", sc->sc_curmode));
@@ -773,6 +1052,8 @@
         sn->static_rix = -1;
 	ath_rate_update_static_rix(sc, ni);
 
+	sn->currates = sc->sc_currates;
+
 	/*
 	 * Construct a bitmask of usable rates.  This has all
 	 * negotiated rates minus those marked by the hal as
@@ -790,7 +1071,7 @@
 				continue;
 			KASSERT(rix < SAMPLE_MAXRATES,
 			    ("mcs %u has rix %d", MCS(x), rix));
-			sn->ratemask |= 1<<rix;
+			sn->ratemask |= (uint64_t) 1<<rix;
 		}
 	}
 
@@ -804,11 +1085,11 @@
 			continue;
 		KASSERT(rix < SAMPLE_MAXRATES,
 		    ("rate %u has rix %d", RATE(x), rix));
-		sn->ratemask |= 1<<rix;
+		sn->ratemask |= (uint64_t) 1<<rix;
 	}
 #ifdef IEEE80211_DEBUG
 	if (ieee80211_msg(ni->ni_vap, IEEE80211_MSG_RATECTL)) {
-		uint32_t mask;
+		uint64_t mask;
 
 		ieee80211_note(ni->ni_vap, "[%6D] %s: size 1600 rate/tt",
 		    ni->ni_macaddr, ":", __func__);
@@ -824,7 +1105,7 @@
 #endif
 	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
 		int size = bin_to_size(y);
-		uint32_t mask;
+		uint64_t mask;
 
 		sn->packets_sent[y] = 0;
 		sn->current_sample_rix[y] = -1;
@@ -844,6 +1125,7 @@
 			sn->stats[y][rix].total_packets = 0;
 			sn->stats[y][rix].packets_acked = 0;
 			sn->stats[y][rix].last_tx = 0;
+			sn->stats[y][rix].ewma_pct = 0;
 			
 			sn->stats[y][rix].perfect_tx_time =
 			    calc_usecs_unicast_packet(sc, size, rix, 0, 0,
@@ -872,6 +1154,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)
 {
@@ -878,12 +1247,14 @@
 	struct ath_softc *sc = arg;
 	const HAL_RATE_TABLE *rt = sc->sc_currates;
 	struct sample_node *sn = ATH_NODE_SAMPLE(ATH_NODE(ni));
-	uint32_t mask;
+	uint64_t mask;
 	int rix, y;
 
-	printf("\n[%s] refcnt %d static_rix %d ratemask 0x%x\n",
+	printf("\n[%s] refcnt %d static_rix (%d %s) ratemask 0x%jx\n",
 	    ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni),
-	    sn->static_rix, sn->ratemask);
+	    dot11rate(rt, sn->static_rix),
+	    dot11rate_label(rt, sn->static_rix),
+	    (uintmax_t)sn->ratemask);
 	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
 		printf("[%4u] cur rix %d (%d %s) since switch: packets %d ticks %u\n",
 		    bin_to_size(y), sn->current_rix[y],
@@ -890,9 +1261,13 @@
 		    dot11rate(rt, sn->current_rix[y]),
 		    dot11rate_label(rt, sn->current_rix[y]),
 		    sn->packets_since_switch[y], sn->ticks_since_switch[y]);
-		printf("[%4u] last sample %d cur sample %d packets sent %d\n",
-		    bin_to_size(y), sn->last_sample_rix[y],
-		    sn->current_sample_rix[y], sn->packets_sent[y]);
+		printf("[%4u] last sample (%d %s) cur sample (%d %s) packets sent %d\n",
+		    bin_to_size(y),
+		    dot11rate(rt, sn->last_sample_rix[y]),
+		    dot11rate_label(rt, sn->last_sample_rix[y]),
+		    dot11rate(rt, sn->current_sample_rix[y]),
+		    dot11rate_label(rt, sn->current_sample_rix[y]),
+		    sn->packets_sent[y]);
 		printf("[%4u] packets since sample %d sample tt %u\n",
 		    bin_to_size(y), sn->packets_since_sample[y],
 		    sn->sample_tt[y]);
@@ -903,13 +1278,16 @@
 		for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
 			if (sn->stats[y][rix].total_packets == 0)
 				continue;
-			printf("[%2u %s:%4u] %8d:%-8d (%3d%%) T %8d F %4d avg %5u last %u\n",
+			printf("[%2u %s:%4u] %8ju:%-8ju (%3d%%) (EWMA %3d.%1d%%) T %8ju F %4d avg %5u last %u\n",
 			    dot11rate(rt, rix), dot11rate_label(rt, rix),
 			    bin_to_size(y),
-			    sn->stats[y][rix].total_packets,
-			    sn->stats[y][rix].packets_acked,
-			    (100*sn->stats[y][rix].packets_acked)/sn->stats[y][rix].total_packets,
-			    sn->stats[y][rix].tries,
+			    (uintmax_t) sn->stats[y][rix].total_packets,
+			    (uintmax_t) sn->stats[y][rix].packets_acked,
+			    (int) ((sn->stats[y][rix].packets_acked * 100ULL) /
+			     sn->stats[y][rix].total_packets),
+			    sn->stats[y][rix].ewma_pct / 10,
+			    sn->stats[y][rix].ewma_pct % 10,
+			    (uintmax_t) sn->stats[y][rix].tries,
 			    sn->stats[y][rix].successive_failures,
 			    sn->stats[y][rix].average_tx_time,
 			    ticks - sn->stats[y][rix].last_tx);
@@ -995,7 +1373,7 @@
 	if (ssc == NULL)
 		return NULL;
 	ssc->arc.arc_space = sizeof(struct sample_node);
-	ssc->smoothing_rate = 95;		/* ewma percentage ([0..99]) */
+	ssc->smoothing_rate = 75;		/* ewma percentage ([0..99]) */
 	ssc->smoothing_minpackets = 100 / (100 - ssc->smoothing_rate);
 	ssc->sample_rate = 10;			/* %time to try diff tx rates */
 	ssc->max_successive_failures = 3;	/* threshold for rate sampling*/

Modified: trunk/sys/dev/ath/ath_rate/sample/sample.h
===================================================================
--- trunk/sys/dev/ath/ath_rate/sample/sample.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_rate/sample/sample.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2005 John Bicket
  * All rights reserved.
@@ -33,7 +34,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/ath_rate/sample/sample.h 239284 2012-08-15 07:10:10Z adrian $
  */
 
 /*
@@ -51,6 +52,7 @@
 	int	max_successive_failures;
 	int	stale_failure_timeout;	/* how long to honor max_successive_failures */
 	int	min_switch;		/* min time between rate changes */
+	int	min_good_pct;		/* min good percentage for a rate to be considered */
 };
 #define	ATH_SOFTC_SAMPLE(sc)	((struct sample_softc *)sc->sc_rc)
 
@@ -57,9 +59,10 @@
 struct rate_stats {	
 	unsigned average_tx_time;
 	int successive_failures;
-	int tries;
-	int total_packets;
-	int packets_acked;
+	uint64_t tries;
+	uint64_t total_packets;	/* pkts total since assoc */
+	uint64_t packets_acked;	/* pkts acked since assoc */
+	int ewma_pct;	/* EWMA percentage */
 	unsigned perfect_tx_time; /* transmit time for 0 retries */
 	int last_tx;
 };
@@ -77,13 +80,23 @@
  */
 #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] */
-	uint32_t ratemask;		/* bit mask of valid rate indices */
+#define	SAMPLE_MAXRATES	64		/* NB: corresponds to hal info[32] */
+	uint64_t ratemask;		/* bit mask of valid rate indices */
 	const struct txschedule *sched;	/* tx schedule table */
 
+	const HAL_RATE_TABLE *currates;
+
 	struct rate_stats stats[NUM_PACKET_SIZE_BINS][SAMPLE_MAXRATES];
 	int last_sample_rix[NUM_PACKET_SIZE_BINS];
 
@@ -97,6 +110,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)
 
@@ -221,4 +237,7 @@
 	}
 	return tt;
 }
+
+#endif	/* _KERNEL */
+
 #endif /* _DEV_ATH_RATE_SAMPLE_H */

Modified: trunk/sys/dev/ath/ath_rate/sample/tx_schedules.h
===================================================================
--- trunk/sys/dev/ath/ath_rate/sample/tx_schedules.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/ath_rate/sample/tx_schedules.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2005 John Bicket
  * All rights reserved.
@@ -38,7 +39,7 @@
 #define	__ATH_RATE_SAMPLE_TXSCHEDULES_H__
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/ath_rate/sample/tx_schedules.h 239286 2012-08-15 07:50:42Z adrian $");
 
 #define A(_r) \
     (((_r) == 6)   ? 0 : (((_r) == 9)   ? 1 : (((_r) == 12)  ? 2 : \
@@ -63,6 +64,10 @@
 	(((_r) == 13) ? 16 : (((_r) == 26) ? 17 : (((_r) == 39) ? 18 : \
 	(((_r) == 52) ? 19 : (((_r) == 78) ? 20 : (((_r) == 104)? 21 : \
 	(((_r) == 117)? 22 : (((_r) == 130)? 23 : 0))))))))
+#define NA3(_r) \
+	(((_r) == 19.5)  ? 24 : (((_r) == 39) ? 25 : (((_r) == 58.5)  ? 26 : \
+	(((_r) == 78)  ? 27 : (((_r) == 117) ? 28 : (((_r) == 156) ? 29 : \
+	(((_r) == 175.5) ? 30 : (((_r) == 195)? 31 : 0))))))))
 static const struct txschedule series_11na[] = {
 	{ 3,A( 6), 3,A(  6), 0,A(  6), 0,A( 6) },       /*   6Mb/s */
 	{ 4,A( 9), 3,A(  6), 4,A(  6), 0,A( 6) },       /*   9Mb/s */
@@ -72,6 +77,9 @@
 	{ 4,A(36), 3,A( 24), 4,A( 18), 2,A( 6) },       /*  36Mb/s */
 	{ 4,A(48), 3,A( 36), 4,A( 24), 2,A(12) },       /*  48Mb/s */
 	{ 4,A(54), 3,A( 48), 4,A( 36), 2,A(24) },       /*  54Mb/s */
+
+	/* 1 stream rates */
+
 	{ 3,NA1( 6.5), 3,NA1( 6.5), 0,NA1( 6.5), 0,NA1(6.5) },  /* 6.5Mb/s */
 	{ 4,NA1(  13), 3,NA1( 6.5), 4,NA1( 6.5), 0,NA1(6.5) },  /*  13Mb/s */
 	{ 4,NA1(19.5), 3,NA1( 6.5), 4,NA1( 6.5), 0,NA1(6.5) },  /*19.5Mb/s */
@@ -80,6 +88,9 @@
 	{ 4,NA1(  52), 3,NA1(  39), 4,NA1(  26), 2,NA1(6.5) },  /*  52Mb/s */
 	{ 4,NA1(58.5), 3,NA1(  52), 4,NA1(  39), 2,NA1( 13) },  /*58.5Mb/s */
 	{ 4,NA1(  65), 3,NA1(58.5), 4,NA1(  52), 2,NA1( 13) },  /*  65Mb/s */
+
+	/* 2 stream rates */
+
 	{ 3,NA2(  13), 3,NA2(  13), 0,NA2(  13), 0,NA2( 13) },  /*  13Mb/s */
 	{ 4,NA2(  26), 3,NA2(  13), 4,NA2(  13), 0,NA2( 13) },  /*  26Mb/s */
 	{ 4,NA2(  39), 3,NA2(  26), 4,NA2(  13), 2,NA2( 13) },  /*  39Mb/s */
@@ -87,9 +98,21 @@
 	{ 4,NA2(  78), 3,NA2(  52), 4,NA2(  39), 2,NA2( 13) },  /*  78Mb/s */
 	{ 4,NA2( 104), 3,NA2(  78), 4,NA2(  52), 2,NA2( 13) },  /* 104Mb/s */
 	{ 4,NA2( 117), 3,NA2( 104), 4,NA2(  78), 2,NA2( 26) },  /* 117Mb/s */
-	{ 4,NA2( 130), 3,NA2( 117), 4,NA2( 104), 2,NA2( 26) }   /* 130Mb/s */
+	{ 4,NA2( 130), 3,NA2( 117), 4,NA2( 104), 2,NA2( 26) },   /* 130Mb/s */
+
+	/* 3 stream rates */
+
+	{ 3,NA3(19.5), 3,NA3(19.5), 0,NA3(19.5), 0,NA3(19.5) },  /*  19Mb/s */
+	{ 3,NA3(  39), 3,NA3(19.5), 0,NA3(19.5), 0,NA3(19.5) },  /*  39Mb/s */
+	{ 3,NA3(58.5), 3,NA3(  39), 0,NA3(19.5), 0,NA3(19.5) },  /*  58Mb/s */
+	{ 3,NA3(  78), 3,NA3(58.5), 0,NA3(  39), 0,NA3(19.5) },  /*  78Mb/s */
+	{ 3,NA3( 117), 3,NA3(  78), 0,NA3(58.5), 0,NA3(19.5) },  /* 117Mb/s */
+	{ 3,NA3( 156), 3,NA3( 117), 0,NA3(  78), 0,NA3(19.5) },  /*  156Mb/s */
+	{ 3,NA3(175.5), 3,NA3( 156), 0,NA3( 117), 0,NA3(  39) },  /*  175Mb/s */
+	{ 3,NA3( 195), 3,NA3( 195), 0,NA3( 156), 0,NA3(58.5) },  /* 195Mb/s */
 };
 #undef A
+#undef NA3
 #undef NA2
 #undef NA1
 
@@ -121,6 +144,11 @@
 	(((_r) == 13)  ? 20 : (((_r) == 26) ? 21 : (((_r) == 39)  ? 22 : \
 	(((_r) == 52)  ? 23 : (((_r) == 78) ? 24 : (((_r) == 104) ? 25 : \
 	(((_r) == 117) ? 26 : (((_r) == 130)? 27 : 0))))))))
+#define NG3(_r) \
+	(((_r) == 19.5)  ? 28 : (((_r) == 39) ? 29 : (((_r) == 58.5)  ? 30 : \
+	(((_r) == 78)  ? 31 : (((_r) == 117) ? 32 : (((_r) == 156) ? 33 : \
+	(((_r) == 175.5) ? 34 : (((_r) == 195)? 35 : 0))))))))
+
 static const struct txschedule series_11ng[] = {
 	{ 3,G( 1), 3,G(  1), 0,G(  1), 0,G( 1) },       /*   1Mb/s */
 	{ 4,G( 2), 3,G(  1), 4,G(  1), 0,G( 1) },       /*   2Mb/s */
@@ -134,6 +162,9 @@
 	{ 4,G(36), 3,G( 24), 4,G( 18), 2,G( 1) },       /*  36Mb/s */
 	{ 4,G(48), 3,G( 36), 4,G( 24), 2,G( 1) },       /*  48Mb/s */
 	{ 4,G(54), 3,G( 48), 4,G( 36), 2,G( 1) },       /*  54Mb/s */
+
+	/* 1 stream rates */
+
 	{ 3,NG1( 6.5), 3,NG1( 6.5), 0,NG1( 6.5), 0,NG1(6.5) },  /* 6.5Mb/s */
 	{ 4,NG1(  13), 3,NG1( 6.5), 4,NG1( 6.5), 0,NG1(6.5) },  /*  13Mb/s */
 	{ 4,NG1(19.5), 3,NG1( 6.5), 4,NG1( 6.5), 0,NG1(6.5) },  /*19.5Mb/s */
@@ -142,6 +173,9 @@
 	{ 4,NG1(  52), 3,NG1(  39), 4,NG1(  26), 2,NG1(6.5) },  /*  52Mb/s */
 	{ 4,NG1(58.5), 3,NG1(  52), 4,NG1(  39), 2,NG1( 13) },  /*58.5Mb/s */
 	{ 4,NG1(  65), 3,NG1(58.5), 4,NG1(  52), 2,NG1( 13) },  /*  65Mb/s */
+
+	/* 2 stream rates */
+
 	{ 3,NG2(  13), 3,NG2(  13), 0,NG2(  13), 0,NG2( 13) },  /*  13Mb/s */
 	{ 4,NG2(  26), 3,NG2(  13), 4,NG2(  13), 0,NG2( 13) },  /*  26Mb/s */
 	{ 4,NG2(  39), 3,NG2(  26), 4,NG2(  13), 2,NG2( 13) },  /*  39Mb/s */
@@ -149,9 +183,22 @@
 	{ 4,NG2(  78), 3,NG2(  52), 4,NG2(  39), 2,NG2( 13) },  /*  78Mb/s */
 	{ 4,NG2( 104), 3,NG2(  78), 4,NG2(  52), 2,NG2( 13) },  /* 104Mb/s */
 	{ 4,NG2( 117), 3,NG2( 104), 4,NG2(  78), 2,NG2( 26) },  /* 117Mb/s */
-	{ 4,NG2( 130), 3,NG2( 117), 4,NG2( 104), 2,NG2( 26) }   /* 130Mb/s */
+	{ 4,NG2( 130), 3,NG2( 117), 4,NG2( 104), 2,NG2( 26) },  /* 130Mb/s */
+
+	/* 3 stream rates */
+
+	{ 3,NG3(19.5), 3,NG3(19.5), 0,NG3(19.5), 0,NG3(19.5) },  /*  19Mb/s */
+	{ 3,NG3(  39), 3,NG3(19.5), 0,NG3(19.5), 0,NG3(19.5) },  /*  39Mb/s */
+	{ 3,NG3(58.5), 3,NG3(  39), 0,NG3(19.5), 0,NG3(19.5) },  /*  58Mb/s */
+	{ 3,NG3(  78), 3,NG3(58.5), 0,NG3(  39), 0,NG3(19.5) },  /*  78Mb/s */
+	{ 3,NG3( 117), 3,NG3(  78), 0,NG3(58.5), 0,NG3(19.5) },  /* 117Mb/s */
+	{ 3,NG3( 156), 3,NG3( 117), 0,NG3(  78), 0,NG3(19.5) },  /*  156Mb/s */
+	{ 3,NG3(175.5), 3,NG3( 156), 0,NG3( 117), 0,NG3(  39) },  /*  175Mb/s */
+	{ 3,NG3( 195), 3,NG3( 195), 0,NG3( 156), 0,NG3(58.5) },  /* 195Mb/s */
+
 };
 #undef G
+#undef NG3
 #undef NG2
 #undef NG1
 

Modified: trunk/sys/dev/ath/if_ath.c
===================================================================
--- trunk/sys/dev/ath/if_ath.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -28,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath.c 332321 2018-04-09 12:55:09Z emaste $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -39,6 +40,14 @@
 
 #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>
@@ -59,6 +68,8 @@
 #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>
 
@@ -92,9 +103,18 @@
 
 #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_ath_btcoex.h>
+#include <dev/ath/if_ath_spectral.h>
+#include <dev/ath/if_ath_lna_div.h>
 #include <dev/ath/if_athdfs.h>
 
 #ifdef ATH_TX99_DIAG
@@ -101,8 +121,16 @@
 #include <dev/ath/ath_tx99/ath_tx99.h>
 #endif
 
+#ifdef	ATH_DEBUG_ALQ
+#include <dev/ath/if_ath_alq.h>
+#endif
 
 /*
+ * Only enable this if you're working on PS-POLL support.
+ */
+#define	ATH_SW_PSQ
+
+/*
  * ATH_BCBUF determines the number of vap's that can transmit
  * beacons and also (currently) the number of vap's that can
  * have unique mac addresses/bssid.  When staggering beacons
@@ -116,9 +144,7 @@
  * the U/L bit and tracking addresses in a byte; it would be
  * worthwhile to allow more for applications like proxy sta.
  */
-#ifdef CTASSERT
 CTASSERT(ATH_BCBUF <= 8);
-#endif
 
 static struct ieee80211vap *ath_vap_create(struct ieee80211com *,
 		    const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
@@ -128,8 +154,9 @@
 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_transmit(struct ifnet *ifp, struct mbuf *m);
+static void	ath_qflush(struct ifnet *ifp);
 static int	ath_media_change(struct ifnet *);
 static void	ath_watchdog(void *);
 static int	ath_ioctl(struct ifnet *, u_long, caddr_t);
@@ -140,52 +167,36 @@
 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_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 void	ath_reset_proc(void *, int);
 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 *,
 			const uint8_t [IEEE80211_ADDR_LEN]);
+static void	ath_node_cleanup(struct ieee80211_node *);
 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(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 int	ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq,
+		    int dosched);
 static void	ath_tx_proc_q0(void *, int);
 static void	ath_tx_proc_q0123(void *, int);
 static void	ath_tx_proc(void *, int);
-static void	ath_tx_draintxq(struct ath_softc *, struct ath_txq *);
+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 *);
-static void	ath_stoprecv(struct ath_softc *);
-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 *);
 static void	ath_set_channel(struct ieee80211com *);
+#ifdef	ATH_ENABLE_11N
+static void	ath_update_chw(struct ieee80211com *);
+#endif	/* ATH_ENABLE_11N */
 static void	ath_calibrate(void *);
 static int	ath_newstate(struct ieee80211vap *, enum ieee80211_state, int);
 static void	ath_setup_stationkey(struct ieee80211_node *);
@@ -196,7 +207,6 @@
 static void	ath_getradiocaps(struct ieee80211com *, int, int *,
 		    struct ieee80211_channel []);
 static int	ath_getchannels(struct ath_softc *);
-static void	ath_led_event(struct ath_softc *, int);
 
 static int	ath_rate_setup(struct ath_softc *, u_int mode);
 static void	ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
@@ -204,33 +214,14 @@
 static void	ath_announce(struct ath_softc *);
 
 static void	ath_dfs_tasklet(void *, int);
+static void	ath_node_powersave(struct ieee80211_node *, int);
+static int	ath_node_set_tim(struct ieee80211_node *, int);
+static void	ath_node_recv_pspoll(struct ieee80211_node *, struct mbuf *);
 
 #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
 
-#define	TDMA_EP_MULTIPLIER	(1<<10) /* pow2 to optimize out * and / */
-#define	TDMA_LPF_LEN		6
-#define	TDMA_DUMMY_MARKER	0x127
-#define	TDMA_EP_MUL(x, mul)	((x) * (mul))
-#define	TDMA_IN(x)		(TDMA_EP_MUL((x), TDMA_EP_MULTIPLIER))
-#define	TDMA_LPF(x, y, len) \
-    ((x != TDMA_DUMMY_MARKER) ? (((x) * ((len)-1) + (y)) / (len)) : (y))
-#define	TDMA_SAMPLE(x, y) do {					\
-	x = TDMA_LPF((x), TDMA_IN(y), TDMA_LPF_LEN);		\
-} while (0)
-#define	TDMA_EP_RND(x,mul) \
-	((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
-#define	TDMA_AVG(x)		TDMA_EP_RND(x, TDMA_EP_MULTIPLIER)
-#endif /* IEEE80211_SUPPORT_TDMA */
-
 SYSCTL_DECL(_hw_ath);
 
 /* XXX validate sysctl values */
@@ -247,21 +238,47 @@
 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);
+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);
 
-static	int ath_bstuck_threshold = 4;		/* max missed beacons */
+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");
 
-static MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers");
+MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers");
 
+void
+ath_legacy_attach_comp_func(struct ath_softc *sc)
+{
+
+	/*
+	 * Special case certain configurations.  Note the
+	 * CAB queue is handled by these specially so don't
+	 * include them when checking the txq setup mask.
+	 */
+	switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) {
+	case 0x01:
+		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc);
+		break;
+	case 0x0f:
+		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc);
+		break;
+	default:
+		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc);
+		break;
+	}
+}
+
 #define	HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20)
 #define	HAL_MODE_HT40 \
 	(HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \
@@ -276,13 +293,16 @@
 	int error = 0, i;
 	u_int wmodes;
 	uint8_t macaddr[IEEE80211_ADDR_LEN];
+	int rx_chainmask, tx_chainmask;
 
 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
 
+	CURVNET_SET(vnet0);
 	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
 	if (ifp == NULL) {
 		device_printf(sc->sc_dev, "can not if_alloc()\n");
 		error = ENOSPC;
+		CURVNET_RESTORE();
 		goto bad;
 	}
 	ic = ifp->if_l2com;
@@ -290,8 +310,10 @@
 	/* set these up early for if_printf use */
 	if_initname(ifp, device_get_name(sc->sc_dev),
 		device_get_unit(sc->sc_dev));
+	CURVNET_RESTORE();
 
-	ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, sc->sc_eepromdata, &status);
+	ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh,
+	    sc->sc_eepromdata, &status);
 	if (ah == NULL) {
 		if_printf(ifp, "unable to attach hardware; HAL status %u\n",
 			status);
@@ -305,6 +327,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
@@ -362,13 +399,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);
 
@@ -379,9 +434,12 @@
 	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET,
 		"%s taskq", ifp->if_xname);
 
-	TASK_INIT(&sc->sc_rxtask, 0, ath_rx_proc, 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);
+	TASK_INIT(&sc->sc_txqtask, 0, ath_txq_sched_tasklet, sc);
+	TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc);
 
 	/*
 	 * Allocate hardware transmit queues: one queue for
@@ -391,7 +449,7 @@
 	 *
 	 * XXX PS-Poll
 	 */
-	sc->sc_bhalq = ath_beaconq_setup(ah);
+	sc->sc_bhalq = ath_beaconq_setup(sc);
 	if (sc->sc_bhalq == (u_int) -1) {
 		if_printf(ifp, "unable to setup a beacon xmit queue!\n");
 		error = EIO;
@@ -430,21 +488,12 @@
 	}
 
 	/*
-	 * Special case certain configurations.  Note the
-	 * CAB queue is handled by these specially so don't
-	 * include them when checking the txq setup mask.
+	 * Attach the TX completion function.
+	 *
+	 * The non-EDMA chips may have some special case optimisations;
+	 * this method gives everyone a chance to attach cleanly.
 	 */
-	switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) {
-	case 0x01:
-		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc);
-		break;
-	case 0x0f:
-		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc);
-		break;
-	default:
-		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc);
-		break;
-	}
+	sc->sc_tx.xmit_attach_comp_func(sc);
 
 	/*
 	 * Setup rate control.  Some rate control modules
@@ -461,34 +510,73 @@
 
 	/* Attach DFS module */
 	if (! ath_dfs_attach(sc)) {
-		device_printf(sc->sc_dev, "%s: unable to attach DFS\n", __func__);
+		device_printf(sc->sc_dev,
+		    "%s: unable to attach DFS\n", __func__);
 		error = EIO;
 		goto bad2;
 	}
 
+	/* Attach spectral module */
+	if (ath_spectral_attach(sc) < 0) {
+		device_printf(sc->sc_dev,
+		    "%s: unable to attach spectral\n", __func__);
+		error = EIO;
+		goto bad2;
+	}
+
+	/* Attach bluetooth coexistence module */
+	if (ath_btcoex_attach(sc) < 0) {
+		device_printf(sc->sc_dev,
+		    "%s: unable to attach bluetooth coexistence\n", __func__);
+		error = EIO;
+		goto bad2;
+	}
+
+	/* Attach LNA diversity module */
+	if (ath_lna_div_attach(sc) < 0) {
+		device_printf(sc->sc_dev,
+		    "%s: unable to attach LNA diversity\n", __func__);
+		error = EIO;
+		goto bad2;
+	}
+
 	/* Start DFS processing tasklet */
 	TASK_INIT(&sc->sc_dfstask, 0, ath_dfs_tasklet, sc);
 
+	/* Configure LED state */
 	sc->sc_blinking = 0;
 	sc->sc_ledstate = 1;
 	sc->sc_ledon = 0;			/* low true */
 	sc->sc_ledidle = (2700*hz)/1000;	/* 2.7sec */
-	callout_init(&sc->sc_ledtimer, CALLOUT_MPSAFE);
+	callout_init(&sc->sc_ledtimer, 1);
+
 	/*
+	 * Don't setup hardware-based blinking.
+	 *
+	 * Although some NICs may have this configured in the
+	 * default reset register values, the user may wish
+	 * to alter which pins have which function.
+	 *
+	 * The reference driver attaches the MAC network LED to GPIO1 and
+	 * the MAC power LED to GPIO2.  However, the DWA-552 cardbus
+	 * NIC has these reversed.
+	 */
+	sc->sc_hardled = (1 == 0);
+	sc->sc_led_net_pin = -1;
+	sc->sc_led_pwr_pin = -1;
+	/*
 	 * Auto-enable soft led processing for IBM cards and for
 	 * 5211 minipci cards.  Users can also manually enable/disable
 	 * support with a sysctl.
 	 */
 	sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID);
-	if (sc->sc_softled) {
-		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin,
-		    HAL_GPIO_MUX_MAC_NETWORK_LED);
-		ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
-	}
+	ath_led_config(sc);
+	ath_hal_setledstate(ah, HAL_LED_INIT);
 
 	ifp->if_softc = sc;
 	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
-	ifp->if_start = ath_start;
+	ifp->if_transmit = ath_transmit;
+	ifp->if_qflush = ath_qflush;
 	ifp->if_ioctl = ath_ioctl;
 	ifp->if_init = ath_init;
 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
@@ -510,10 +598,12 @@
 		| 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 DFS radar detection */
+		| IEEE80211_C_DFS		/* Enable radar detection */
 #endif
 		;
 	/*
@@ -598,6 +688,10 @@
 	sc->sc_hastsfadd = ath_hal_hastsfadjust(ah);
 	sc->sc_rxslink = ath_hal_self_linked_final_rxdesc(ah);
 	sc->sc_rxtsf32 = ath_hal_has_long_rxdesc_tsf(ah);
+	sc->sc_hasenforcetxop = ath_hal_hasenforcetxop(ah);
+	sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah);
+	sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah);
+
 	if (ath_hal_hasfastframes(ah))
 		ic->ic_caps |= IEEE80211_C_FF;
 	wmodes = ath_hal_getwirelessmodes(ah);
@@ -611,11 +705,77 @@
 #endif
 
 	/*
-	 * 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.
+	 * TODO: enforce that at least this many frames are available
+	 * in the txbuf list before allowing data frames (raw or
+	 * otherwise) to be transmitted.
 	 */
+	sc->sc_txq_data_minfree = 10;
+	/*
+	 * Leave this as default to maintain legacy behaviour.
+	 * Shortening the cabq/mcastq may end up causing some
+	 * undesirable behaviour.
+	 */
+	sc->sc_txq_mcastq_maxdepth = ath_txbuf;
 
+	/*
+	 * How deep can the node software TX queue get whilst it's asleep.
+	 */
+	sc->sc_txq_node_psq_maxdepth = 16;
+
+	/*
+	 * Default the maximum queue depth for a given node
+	 * to 1/4'th the TX buffers, or 64, whichever
+	 * is larger.
+	 */
+	sc->sc_txq_node_maxdepth = MAX(64, ath_txbuf / 4);
+
+	/* Enable CABQ by default */
+	sc->sc_cabq_enable = 1;
+
+	/*
+	 * Allow the TX and RX chainmasks to be overridden by
+	 * environment variables and/or device.hints.
+	 *
+	 * This must be done early - before the hardware is
+	 * calibrated or before the 802.11n stream calculation
+	 * is done.
+	 */
+	if (resource_int_value(device_get_name(sc->sc_dev),
+	    device_get_unit(sc->sc_dev), "rx_chainmask",
+	    &rx_chainmask) == 0) {
+		device_printf(sc->sc_dev, "Setting RX chainmask to 0x%x\n",
+		    rx_chainmask);
+		(void) ath_hal_setrxchainmask(sc->sc_ah, rx_chainmask);
+	}
+	if (resource_int_value(device_get_name(sc->sc_dev),
+	    device_get_unit(sc->sc_dev), "tx_chainmask",
+	    &tx_chainmask) == 0) {
+		device_printf(sc->sc_dev, "Setting TX chainmask to 0x%x\n",
+		    tx_chainmask);
+		(void) ath_hal_settxchainmask(sc->sc_ah, tx_chainmask);
+	}
+
+	/*
+	 * Query the TX/RX chainmask configuration.
+	 *
+	 * This is only relevant for 11n devices.
+	 */
+	ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask);
+	ath_hal_gettxchainmask(ah, &sc->sc_txchainmask);
+
+	/*
+	 * Disable MRR with protected frames by default.
+	 * Only 802.11n series NICs can handle this.
+	 */
+	sc->sc_mrrprot = 0;	/* XXX should be a capability */
+
+	/*
+	 * Query the enterprise mode information the HAL.
+	 */
+	if (ath_hal_getcapability(ah, HAL_CAP_ENTERPRISE_MODE, 0,
+	    &sc->sc_ent_cfg) == HAL_OK)
+		sc->sc_use_ent = 1;
+
 #ifdef	ATH_ENABLE_11N
 	/*
 	 * Query HT capabilities
@@ -622,14 +782,18 @@
 	 */
 	if (ath_hal_getcapability(ah, HAL_CAP_HT, 0, NULL) == HAL_OK &&
 	    (wmodes & (HAL_MODE_HT20 | HAL_MODE_HT40))) {
-		int rxs, txs;
+		uint32_t rxs, txs;
 
 		device_printf(sc->sc_dev, "[HT] enabling HT modes\n");
-		ic->ic_htcaps = IEEE80211_HTC_HT		/* HT operation */
-			    | IEEE80211_HTC_AMPDU		/* A-MPDU tx/rx */
-			    | IEEE80211_HTC_AMSDU		/* A-MSDU tx/rx */
-			    | IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
-			    | IEEE80211_HTCAP_SMPS_OFF;		/* SM power save off */
+
+		sc->sc_mrrprot = 1;	/* XXX should be a capability */
+
+		ic->ic_htcaps = IEEE80211_HTC_HT	/* HT operation */
+			    | IEEE80211_HTC_AMPDU	/* A-MPDU tx/rx */
+			    | IEEE80211_HTC_AMSDU	/* A-MSDU tx/rx */
+			    | IEEE80211_HTCAP_MAXAMSDU_3839
+			    				/* max A-MSDU length */
+			    | IEEE80211_HTCAP_SMPS_OFF;	/* SM power save off */
 			;
 
 		/*
@@ -650,24 +814,78 @@
 			    |  IEEE80211_HTCAP_SHORTGI40;
 
 		/*
-		 * rx/tx stream is not currently used anywhere; it needs to be taken
-		 * into account when negotiating which MCS rates it'll receive and
+		 * TX/RX streams need to be taken into account when
+		 * negotiating which MCS rates it'll receive and
 		 * what MCS rates are available for TX.
 		 */
-		(void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 0, &rxs);
-		(void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &txs);
-
-		ath_hal_getrxchainmask(ah, &sc->sc_rxchainmask);
-		ath_hal_gettxchainmask(ah, &sc->sc_txchainmask);
-
+		(void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 0, &txs);
+		(void) ath_hal_getcapability(ah, HAL_CAP_STREAMS, 1, &rxs);
 		ic->ic_txstream = txs;
 		ic->ic_rxstream = rxs;
 
-		device_printf(sc->sc_dev, "[HT] %d RX streams; %d TX streams\n", rxs, txs);
+		/*
+		 * Setup TX and RX STBC based on what the HAL allows and
+		 * the currently configured chainmask set.
+		 * Ie - don't enable STBC TX if only one chain is enabled.
+		 * STBC RX is fine on a single RX chain; it just won't
+		 * provide any real benefit.
+		 */
+		if (ath_hal_getcapability(ah, HAL_CAP_RX_STBC, 0,
+		    NULL) == HAL_OK) {
+			sc->sc_rx_stbc = 1;
+			device_printf(sc->sc_dev,
+			    "[HT] 1 stream STBC receive enabled\n");
+			ic->ic_htcaps |= IEEE80211_HTCAP_RXSTBC_1STREAM;
+		}
+		if (txs > 1 && ath_hal_getcapability(ah, HAL_CAP_TX_STBC, 0,
+		    NULL) == HAL_OK) {
+			sc->sc_tx_stbc = 1;
+			device_printf(sc->sc_dev,
+			    "[HT] 1 stream STBC transmit enabled\n");
+			ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC;
+		}
+
+		(void) ath_hal_getcapability(ah, HAL_CAP_RTS_AGGR_LIMIT, 1,
+		    &sc->sc_rts_aggr_limit);
+		if (sc->sc_rts_aggr_limit != (64 * 1024))
+			device_printf(sc->sc_dev,
+			    "[HT] RTS aggregates limited to %d KiB\n",
+			    sc->sc_rts_aggr_limit / 1024);
+
+		device_printf(sc->sc_dev,
+		    "[HT] %d RX streams; %d TX streams\n", rxs, txs);
 	}
 #endif
 
 	/*
+	 * Initial aggregation settings.
+	 */
+	sc->sc_hwq_limit_aggr = ATH_AGGR_MIN_QDEPTH;
+	sc->sc_hwq_limit_nonaggr = ATH_NONAGGR_MIN_QDEPTH;
+	sc->sc_tid_hwq_lo = ATH_AGGR_SCHED_LOW;
+	sc->sc_tid_hwq_hi = ATH_AGGR_SCHED_HIGH;
+	sc->sc_aggr_limit = ATH_AGGR_MAXSIZE;
+	sc->sc_delim_min_pad = 0;
+
+	/*
+	 * Check if the hardware requires PCI register serialisation.
+	 * Some of the Owl based MACs require this.
+	 */
+	if (mp_ncpus > 1 &&
+	    ath_hal_getcapability(ah, HAL_CAP_SERIALISE_WAR,
+	     0, NULL) == HAL_OK) {
+		sc->sc_ah->ah_config.ah_serialise_reg_war = 1;
+		device_printf(sc->sc_dev,
+		    "Enabling register serialisation\n");
+	}
+
+	/*
+	 * Initialise the deferred completed RX buffer list.
+	 */
+	TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP]);
+	TAILQ_INIT(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP]);
+
+	/*
 	 * Indicate we need the 802.11 header padded to a
 	 * 32-bit boundary for 4-address and QoS frames.
 	 */
@@ -709,18 +927,63 @@
 	ic->ic_node_alloc = ath_node_alloc;
 	sc->sc_node_free = ic->ic_node_free;
 	ic->ic_node_free = ath_node_free;
+	sc->sc_node_cleanup = ic->ic_node_cleanup;
+	ic->ic_node_cleanup = ath_node_cleanup;
 	ic->ic_node_getsignal = ath_node_getsignal;
 	ic->ic_scan_start = ath_scan_start;
 	ic->ic_scan_end = ath_scan_end;
 	ic->ic_set_channel = ath_set_channel;
+#ifdef	ATH_ENABLE_11N
+	/* 802.11n specific - but just override anyway */
+	sc->sc_addba_request = ic->ic_addba_request;
+	sc->sc_addba_response = ic->ic_addba_response;
+	sc->sc_addba_stop = ic->ic_addba_stop;
+	sc->sc_bar_response = ic->ic_bar_response;
+	sc->sc_addba_response_timeout = ic->ic_addba_response_timeout;
 
+	ic->ic_addba_request = ath_addba_request;
+	ic->ic_addba_response = ath_addba_response;
+	ic->ic_addba_response_timeout = ath_addba_response_timeout;
+	ic->ic_addba_stop = ath_addba_stop;
+	ic->ic_bar_response = ath_bar_response;
+
+	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 the ALQ logging if required
+	 */
+#ifdef	ATH_DEBUG_ALQ
+	if_ath_alq_init(&sc->sc_alq, device_get_nameunit(sc->sc_dev));
+	if_ath_alq_setcfg(&sc->sc_alq,
+	    sc->sc_ah->ah_macVersion,
+	    sc->sc_ah->ah_macRev,
+	    sc->sc_ah->ah_phyRev,
+	    sc->sc_ah->ah_magic);
+#endif
+
+	/*
 	 * Setup dynamic sysctl's now that country code and
 	 * regdomain are available from the hal.
 	 */
@@ -735,11 +998,21 @@
 bad2:
 	ath_tx_cleanup(sc);
 	ath_desc_free(sc);
+	ath_txdma_teardown(sc);
+	ath_rxdma_teardown(sc);
 bad:
 	if (ah)
 		ath_hal_detach(ah);
-	if (ifp != NULL)
+
+	/*
+	 * To work around scoping issues with CURVNET_SET/CURVNET_RESTORE..
+	 */
+	if (ifp != NULL && ifp->if_vnet) {
+		CURVNET_SET(ifp->if_vnet);
 		if_free(ifp);
+		CURVNET_RESTORE();
+	} else if (ifp != NULL)
+		if_free(ifp);
 	sc->sc_invalid = 1;
 	return error;
 }
@@ -774,12 +1047,22 @@
 		sc->sc_tx99->detach(sc->sc_tx99);
 #endif
 	ath_rate_detach(sc->sc_rc);
-
+#ifdef	ATH_DEBUG_ALQ
+	if_ath_alq_tidyup(&sc->sc_alq);
+#endif
+	ath_lna_div_detach(sc);
+	ath_btcoex_detach(sc);
+	ath_spectral_detach(sc);
 	ath_dfs_detach(sc);
 	ath_desc_free(sc);
+	ath_txdma_teardown(sc);
+	ath_rxdma_teardown(sc);
 	ath_tx_cleanup(sc);
 	ath_hal_detach(sc->sc_ah);	/* NB: sets chip in full sleep */
+
+	CURVNET_SET(ifp->if_vnet);
 	if_free(ifp);
+	CURVNET_RESTORE();
 
 	return 0;
 }
@@ -950,7 +1233,7 @@
 	/*
 	 * Check that a beacon buffer is available; the code below assumes it.
 	 */
-	if (needbeacon & STAILQ_EMPTY(&sc->sc_bbuf)) {
+	if (needbeacon & TAILQ_EMPTY(&sc->sc_bbuf)) {
 		device_printf(sc->sc_dev, "no beacon buffer available\n");
 		goto bad;
 	}
@@ -990,6 +1273,15 @@
 	avp->av_bmiss = vap->iv_bmiss;
 	vap->iv_bmiss = ath_bmiss_vap;
 
+	avp->av_node_ps = vap->iv_node_ps;
+	vap->iv_node_ps = ath_node_powersave;
+
+	avp->av_set_tim = vap->iv_set_tim;
+	vap->iv_set_tim = ath_node_set_tim;
+
+	avp->av_recv_pspoll = vap->iv_recv_pspoll;
+	vap->iv_recv_pspoll = ath_node_recv_pspoll;
+
 	/* Set default parameters */
 
 	/*
@@ -1012,8 +1304,8 @@
 		 * multicast frames.  We know a beacon buffer is
 		 * available because we checked above.
 		 */
-		avp->av_bcbuf = STAILQ_FIRST(&sc->sc_bbuf);
-		STAILQ_REMOVE_HEAD(&sc->sc_bbuf, bf_list);
+		avp->av_bcbuf = TAILQ_FIRST(&sc->sc_bbuf);
+		TAILQ_REMOVE(&sc->sc_bbuf, avp->av_bcbuf, bf_list);
 		if (opmode != IEEE80211_M_IBSS || !sc->sc_hasveol) {
 			/*
 			 * Assign the vap to a beacon xmit slot.  As above
@@ -1110,6 +1402,7 @@
 	struct ath_hal *ah = sc->sc_ah;
 	struct ath_vap *avp = ATH_VAP(vap);
 
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__);
 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
 		/*
 		 * Quiesce the hardware while we remove the vap.  In
@@ -1117,11 +1410,32 @@
 		 * the vap state by any frames pending on the tx queues.
 		 */
 		ath_hal_intrset(ah, 0);		/* disable interrupts */
-		ath_draintxq(sc);		/* stop xmit side */
-		ath_stoprecv(sc);		/* stop recv side */
+		ath_draintxq(sc, ATH_RESET_DEFAULT);		/* stop hw xmit side */
+		/* XXX Do all frames from all vaps/nodes need draining here? */
+		ath_stoprecv(sc, 1);		/* stop recv side */
 	}
 
 	ieee80211_vap_detach(vap);
+
+	/*
+	 * XXX Danger Will Robinson! Danger!
+	 *
+	 * Because ieee80211_vap_detach() can queue a frame (the station
+	 * diassociate message?) after we've drained the TXQ and
+	 * flushed the software TXQ, we will end up with a frame queued
+	 * to a node whose vap is about to be freed.
+	 *
+	 * To work around this, flush the hardware/software again.
+	 * This may be racy - the ath task may be running and the packet
+	 * may be being scheduled between sw->hw txq. Tsk.
+	 *
+	 * TODO: figure out why a new node gets allocated somewhere around
+	 * here (after the ath_tx_swq() call; and after an ath_stop_locked()
+	 * call!)
+	 */
+
+	ath_draintxq(sc, ATH_RESET_DEFAULT);
+
 	ATH_LOCK(sc);
 	/*
 	 * Reclaim beacon state.  Note this must be done before
@@ -1144,7 +1458,6 @@
 		 * Reclaim any pending mcast frames for the vap.
 		 */
 		ath_tx_draintxq(sc, &avp->av_mcastq);
-		ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq);
 	}
 	/*
 	 * Update bookkeeping.
@@ -1169,7 +1482,6 @@
 		sc->sc_swbmiss = 0;
 	}
 #endif
-	ATH_UNLOCK(sc);
 	free(avp, M_80211_VAP);
 
 	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
@@ -1190,6 +1502,7 @@
 		}
 		ath_hal_intrset(ah, sc->sc_imask);
 	}
+	ATH_UNLOCK(sc);
 }
 
 void
@@ -1202,15 +1515,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);
 }
 
 /*
@@ -1232,6 +1552,32 @@
 	ieee80211_crypto_reload_keys(ic);
 }
 
+/*
+ * Fetch the current chainmask configuration based on the current
+ * operating channel and options.
+ */
+static void
+ath_update_chainmasks(struct ath_softc *sc, struct ieee80211_channel *chan)
+{
+
+	/*
+	 * Set TX chainmask to the currently configured chainmask;
+	 * the TX chainmask depends upon the current operating mode.
+	 */
+	sc->sc_cur_rxchainmask = sc->sc_rxchainmask;
+	if (IEEE80211_IS_CHAN_HT(chan)) {
+		sc->sc_cur_txchainmask = sc->sc_txchainmask;
+	} else {
+		sc->sc_cur_txchainmask = 1;
+	}
+
+	DPRINTF(sc, ATH_DEBUG_RESET,
+	    "%s: TX chainmask is now 0x%x, RX is now 0x%x\n",
+	    __func__,
+	    sc->sc_cur_txchainmask,
+	    sc->sc_cur_rxchainmask);
+}
+
 void
 ath_resume(struct ath_softc *sc)
 {
@@ -1243,10 +1589,17 @@
 	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.
 	 */
+	ath_update_chainmasks(sc,
+	    sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan);
+	ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask,
+	    sc->sc_cur_rxchainmask);
 	ath_hal_reset(ah, sc->sc_opmode,
 	    sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan,
 	    AH_FALSE, &status);
@@ -1255,28 +1608,30 @@
 	/* Let DFS at it in case it's a DFS channel */
 	ath_dfs_radar_enable(sc, ic->ic_curchan);
 
-	if (sc->sc_resume_up) {
-		if (ic->ic_opmode == IEEE80211_M_STA) {
-			ath_init(sc);
-			/*
-			 * 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_softled) {
-		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin,
-		    HAL_GPIO_MUX_MAC_NETWORK_LED);
-		ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
-	}
+	/* Let spectral at in case spectral is enabled */
+	ath_spectral_enable(sc, ic->ic_curchan);
 
+	/*
+	 * Let bluetooth coexistence at in case it's needed for this channel
+	 */
+	ath_btcoex_enable(sc, ic->ic_curchan);
+
+	/*
+	 * If we're doing TDMA, enforce the TXOP limitation for chips that
+	 * support it.
+	 */
+	if (sc->sc_hasenforcetxop && sc->sc_tdma)
+		ath_hal_setenforcetxop(sc->sc_ah, 1);
+	else
+		ath_hal_setenforcetxop(sc->sc_ah, 0);
+
+	/* Restore the LED configuration */
+	ath_led_config(sc);
+	ath_hal_setledstate(ah, HAL_LED_INIT);
+
+	if (sc->sc_resume_up)
+		ieee80211_resume_all(ic);
+
 	/* XXX beacons ? */
 }
 
@@ -1302,7 +1657,24 @@
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ath_hal *ah = sc->sc_ah;
 	HAL_INT status = 0;
+	uint32_t txqs;
 
+	/*
+	 * If we're inside a reset path, just print a warning and
+	 * clear the ISR. The reset routine will finish it for us.
+	 */
+	ATH_PCU_LOCK(sc);
+	if (sc->sc_inreset_cnt) {
+		HAL_INT status;
+		ath_hal_getisr(ah, &status);	/* clear ISR */
+		ath_hal_intrset(ah, 0);		/* disable further intr's */
+		DPRINTF(sc, ATH_DEBUG_ANY,
+		    "%s: in reset, ignoring: status=0x%x\n",
+		    __func__, status);
+		ATH_PCU_UNLOCK(sc);
+		return;
+	}
+
 	if (sc->sc_invalid) {
 		/*
 		 * The hardware is not ready/present, don't touch anything.
@@ -1309,10 +1681,14 @@
 		 * Note this can happen early on if the IRQ is shared.
 		 */
 		DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__);
+		ATH_PCU_UNLOCK(sc);
 		return;
 	}
-	if (!ath_hal_intrpend(ah))		/* shared irq, not for us */
+	if (!ath_hal_intrpend(ah)) {		/* shared irq, not for us */
+		ATH_PCU_UNLOCK(sc);
 		return;
+	}
+
 	if ((ifp->if_flags & IFF_UP) == 0 ||
 	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
 		HAL_INT status;
@@ -1321,8 +1697,10 @@
 			__func__, ifp->if_flags);
 		ath_hal_getisr(ah, &status);	/* clear ISR */
 		ath_hal_intrset(ah, 0);		/* disable further intr's */
+		ATH_PCU_UNLOCK(sc);
 		return;
 	}
+
 	/*
 	 * Figure out the reason(s) for the interrupt.  Note
 	 * that the hal returns a pseudo-ISR that may include
@@ -1331,16 +1709,53 @@
 	 */
 	ath_hal_getisr(ah, &status);		/* NB: clears ISR too */
 	DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status);
+	ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1, "ath_intr: mask=0x%.8x", status);
+#ifdef	ATH_DEBUG_ALQ
+	if_ath_alq_post_intr(&sc->sc_alq, status, ah->ah_intrstate,
+	    ah->ah_syncstate);
+#endif	/* ATH_DEBUG_ALQ */
+#ifdef	ATH_KTR_INTR_DEBUG
+	ATH_KTR(sc, ATH_KTR_INTERRUPTS, 5,
+	    "ath_intr: ISR=0x%.8x, ISR_S0=0x%.8x, ISR_S1=0x%.8x, ISR_S2=0x%.8x, ISR_S5=0x%.8x",
+	    ah->ah_intrstate[0],
+	    ah->ah_intrstate[1],
+	    ah->ah_intrstate[2],
+	    ah->ah_intrstate[3],
+	    ah->ah_intrstate[6]);
+#endif
+
+	/* Squirrel away SYNC interrupt debugging */
+	if (ah->ah_syncstate != 0) {
+		int i;
+		for (i = 0; i < 32; i++)
+			if (ah->ah_syncstate & (i << i))
+				sc->sc_intr_stats.sync_intr[i]++;
+	}
+
 	status &= sc->sc_imask;			/* discard unasked for bits */
 
 	/* Short-circuit un-handled interrupts */
-	if (status == 0x0)
+	if (status == 0x0) {
+		ATH_PCU_UNLOCK(sc);
 		return;
+	}
 
+	/*
+	 * Take a note that we're inside the interrupt handler, so
+	 * the reset routines know to wait.
+	 */
+	sc->sc_intr_cnt++;
+	ATH_PCU_UNLOCK(sc);
+
+	/*
+	 * Handle the interrupt. We won't run concurrent with the reset
+	 * or channel change routines as they'll wait for sc_intr_cnt
+	 * to be 0 before continuing.
+	 */
 	if (status & HAL_INT_FATAL) {
 		sc->sc_stats.ast_hardware++;
 		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
-		ath_fatal_proc(sc, 0);
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask);
 	} else {
 		if (status & HAL_INT_SWBA) {
 			/*
@@ -1370,12 +1785,14 @@
 				 * traffic so any frames held on the staging
 				 * queue are aged and potentially flushed.
 				 */
-				taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
+				sc->sc_rx.recv_sched(sc, 1);
 #endif
 			}
 		}
 		if (status & HAL_INT_RXEOL) {
-			int imask = sc->sc_imask;
+			int imask;
+			ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXEOL");
+			ATH_PCU_LOCK(sc);
 			/*
 			 * NB: the hardware should re-read the link when
 			 *     RXE bit is written, but it doesn't work at
@@ -1391,16 +1808,30 @@
 			 * by a call to ath_reset() somehow, the
 			 * interrupt mask will be correctly reprogrammed.
 			 */
+			imask = sc->sc_imask;
 			imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN);
 			ath_hal_intrset(ah, imask);
 			/*
+			 * Only blank sc_rxlink if we've not yet kicked
+			 * the PCU.
+			 *
+			 * This isn't entirely correct - the correct solution
+			 * would be to have a PCU lock and engage that for
+			 * the duration of the PCU fiddling; which would include
+			 * running the RX process. Otherwise we could end up
+			 * messing up the RX descriptor chain and making the
+			 * RX desc list much shorter.
+			 */
+			if (! sc->sc_kickpcu)
+				sc->sc_rxlink = NULL;
+			sc->sc_kickpcu = 1;
+			ATH_PCU_UNLOCK(sc);
+			/*
 			 * Enqueue an RX proc, to handled whatever
 			 * is in the RX queue.
 			 * This will then kick the PCU.
 			 */
-			taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
-			sc->sc_rxlink = NULL;
-			sc->sc_kickpcu = 1;
+			sc->sc_rx.recv_sched(sc, 1);
 		}
 		if (status & HAL_INT_TXURN) {
 			sc->sc_stats.ast_txurn++;
@@ -1407,10 +1838,35 @@
 			/* bump tx trigger level */
 			ath_hal_updatetxtriglevel(ah, AH_TRUE);
 		}
-		if (status & HAL_INT_RX)
-			taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
-		if (status & HAL_INT_TX)
+		/*
+		 * 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++;
+			sc->sc_rx.recv_sched(sc, 1);
+		}
+		if (status & HAL_INT_TX) {
+			sc->sc_stats.ast_tx_intr++;
+			/*
+			 * Grab all the currently set bits in the HAL txq bitmap
+			 * and blank them. This is the only place we should be
+			 * doing this.
+			 */
+			if (! sc->sc_isedma) {
+				ATH_PCU_LOCK(sc);
+				txqs = 0xffffffff;
+				ath_hal_gettxintrtxqs(sc->sc_ah, &txqs);
+				ATH_KTR(sc, ATH_KTR_INTERRUPTS, 3,
+				    "ath_intr: TX; txqs=0x%08x, txq_active was 0x%08x, now 0x%08x",
+				    txqs,
+				    sc->sc_txq_active,
+				    sc->sc_txq_active | txqs);
+				sc->sc_txq_active |= txqs;
+				ATH_PCU_UNLOCK(sc);
+			}
 			taskqueue_enqueue(sc->sc_tq, &sc->sc_txtask);
+		}
 		if (status & HAL_INT_BMISS) {
 			sc->sc_stats.ast_bmiss++;
 			taskqueue_enqueue(sc->sc_tq, &sc->sc_bmisstask);
@@ -1421,6 +1877,7 @@
 			sc->sc_stats.ast_tx_cst++;
 		if (status & HAL_INT_MIB) {
 			sc->sc_stats.ast_mib++;
+			ATH_PCU_LOCK(sc);
 			/*
 			 * Disable interrupts until we service the MIB
 			 * interrupt; otherwise it will continue to fire.
@@ -1431,13 +1888,25 @@
 			 * clear whatever condition caused the interrupt.
 			 */
 			ath_hal_mibevent(ah, &sc->sc_halstats);
-			ath_hal_intrset(ah, sc->sc_imask);
+			/*
+			 * Don't reset the interrupt if we've just
+			 * kicked the PCU, or we may get a nested
+			 * RXEOL before the rxproc has had a chance
+			 * to run.
+			 */
+			if (sc->sc_kickpcu == 0)
+				ath_hal_intrset(ah, sc->sc_imask);
+			ATH_PCU_UNLOCK(sc);
 		}
 		if (status & HAL_INT_RXORN) {
 			/* NB: hal marks HAL_INT_FATAL when RXORN is fatal */
+			ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXORN");
 			sc->sc_stats.ast_rxorn++;
 		}
 	}
+	ATH_PCU_LOCK(sc);
+	sc->sc_intr_cnt--;
+	ATH_PCU_UNLOCK(sc);
 }
 
 static void
@@ -1462,7 +1931,7 @@
 		    state[0], state[1] , state[2], state[3],
 		    state[4], state[5]);
 	}
-	ath_reset(ifp);
+	ath_reset(ifp, ATH_RESET_NOLOSS);
 }
 
 static void
@@ -1481,6 +1950,7 @@
 		struct ath_softc *sc = ifp->if_softc;
 		u_int64_t lastrx = sc->sc_lastrx;
 		u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah);
+		/* XXX should take a locked ref to iv_bss */
 		u_int bmisstimeout =
 			vap->iv_bmissthreshold * vap->iv_bss->ni_intval * 1024;
 
@@ -1498,7 +1968,7 @@
 	ATH_VAP(vap)->av_bmiss(vap);
 }
 
-static int
+int
 ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs)
 {
 	uint32_t rsize;
@@ -1520,11 +1990,19 @@
 
 	DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending);
 
+	/*
+	 * Do a reset upon any becaon miss event.
+	 *
+	 * It may be a non-recognised RX clear hang which needs a reset
+	 * to clear.
+	 */
 	if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) {
+		ath_reset(ifp, ATH_RESET_NOLOSS);
 		if_printf(ifp, "bb hang detected (0x%x), resetting\n", hangs);
-		ath_reset(ifp);
-	} else
+	} else {
+		ath_reset(ifp, ATH_RESET_NOLOSS);
 		ieee80211_beacon_miss(ifp->if_l2com);
+	}
 }
 
 /*
@@ -1577,6 +2055,9 @@
 	 * and then setup of the interrupt mask.
 	 */
 	ath_settkipmic(sc);
+	ath_update_chainmasks(sc, ic->ic_curchan);
+	ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask,
+	    sc->sc_cur_rxchainmask);
 	if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, &status)) {
 		if_printf(ifp, "unable to reset hardware; hal status %u\n",
 			status);
@@ -1588,7 +2069,24 @@
 	/* Let DFS at it in case it's a DFS channel */
 	ath_dfs_radar_enable(sc, ic->ic_curchan);
 
+	/* Let spectral at in case spectral is enabled */
+	ath_spectral_enable(sc, ic->ic_curchan);
+
 	/*
+	 * Let bluetooth coexistence at in case it's needed for this channel
+	 */
+	ath_btcoex_enable(sc, ic->ic_curchan);
+
+	/*
+	 * If we're doing TDMA, enforce the TXOP limitation for chips that
+	 * support it.
+	 */
+	if (sc->sc_hasenforcetxop && sc->sc_tdma)
+		ath_hal_setenforcetxop(sc->sc_ah, 1);
+	else
+		ath_hal_setenforcetxop(sc->sc_ah, 0);
+
+	/*
 	 * Likewise this is set during reset so update
 	 * state cached in the driver.
 	 */
@@ -1624,8 +2122,17 @@
 	 */
 	sc->sc_imask = HAL_INT_RX | HAL_INT_TX
 		  | HAL_INT_RXEOL | HAL_INT_RXORN
+		  | HAL_INT_TXURN
 		  | 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.
 	 */
@@ -1695,9 +2202,9 @@
 			}
 			ath_hal_intrset(ah, 0);
 		}
-		ath_draintxq(sc);
+		ath_draintxq(sc, ATH_RESET_DEFAULT);
 		if (!sc->sc_invalid) {
-			ath_stoprecv(sc);
+			ath_stoprecv(sc, 1);
 			ath_hal_phydisable(ah);
 		} else
 			sc->sc_rxlink = NULL;
@@ -1705,7 +2212,121 @@
 	}
 }
 
+#define	MAX_TXRX_ITERATIONS	1000
 static void
+ath_txrx_stop_locked(struct ath_softc *sc)
+{
+	int i = MAX_TXRX_ITERATIONS;
+
+	ATH_UNLOCK_ASSERT(sc);
+	ATH_PCU_LOCK_ASSERT(sc);
+
+	/*
+	 * Sleep until all the pending operations have completed.
+	 *
+	 * The caller must ensure that reset has been incremented
+	 * or the pending operations may continue being queued.
+	 */
+	while (sc->sc_rxproc_cnt || sc->sc_txproc_cnt ||
+	    sc->sc_txstart_cnt || sc->sc_intr_cnt) {
+		if (i <= 0)
+			break;
+		msleep(sc, &sc->sc_pcu_mtx, 0, "ath_txrx_stop", 1);
+		i--;
+	}
+
+	if (i <= 0)
+		device_printf(sc->sc_dev,
+		    "%s: didn't finish after %d iterations\n",
+		    __func__, MAX_TXRX_ITERATIONS);
+}
+#undef	MAX_TXRX_ITERATIONS
+
+#if 0
+static void
+ath_txrx_stop(struct ath_softc *sc)
+{
+	ATH_UNLOCK_ASSERT(sc);
+	ATH_PCU_UNLOCK_ASSERT(sc);
+
+	ATH_PCU_LOCK(sc);
+	ath_txrx_stop_locked(sc);
+	ATH_PCU_UNLOCK(sc);
+}
+#endif
+
+static void
+ath_txrx_start(struct ath_softc *sc)
+{
+
+	taskqueue_unblock(sc->sc_tq);
+}
+
+/*
+ * Grab the reset lock, and wait around until noone else
+ * is trying to do anything with it.
+ *
+ * This is totally horrible but we can't hold this lock for
+ * long enough to do TX/RX or we end up with net80211/ip stack
+ * LORs and eventual deadlock.
+ *
+ * "dowait" signals whether to spin, waiting for the reset
+ * lock count to reach 0. This should (for now) only be used
+ * during the reset path, as the rest of the code may not
+ * be locking-reentrant enough to behave correctly.
+ *
+ * Another, cleaner way should be found to serialise all of
+ * these operations.
+ */
+#define	MAX_RESET_ITERATIONS	10
+static int
+ath_reset_grablock(struct ath_softc *sc, int dowait)
+{
+	int w = 0;
+	int i = MAX_RESET_ITERATIONS;
+
+	ATH_PCU_LOCK_ASSERT(sc);
+	do {
+		if (sc->sc_inreset_cnt == 0) {
+			w = 1;
+			break;
+		}
+		if (dowait == 0) {
+			w = 0;
+			break;
+		}
+		ATH_PCU_UNLOCK(sc);
+		pause("ath_reset_grablock", 1);
+		i--;
+		ATH_PCU_LOCK(sc);
+	} while (i > 0);
+
+	/*
+	 * We always increment the refcounter, regardless
+	 * of whether we succeeded to get it in an exclusive
+	 * way.
+	 */
+	sc->sc_inreset_cnt++;
+
+	if (i <= 0)
+		device_printf(sc->sc_dev,
+		    "%s: didn't finish after %d iterations\n",
+		    __func__, MAX_RESET_ITERATIONS);
+
+	if (w == 0)
+		device_printf(sc->sc_dev,
+		    "%s: warning, recursive reset path!\n",
+		    __func__);
+
+	return w;
+}
+#undef MAX_RESET_ITERATIONS
+
+/*
+ * XXX TODO: write ath_reset_releaselock
+ */
+
+static void
 ath_stop(struct ifnet *ifp)
 {
 	struct ath_softc *sc = ifp->if_softc;
@@ -1723,18 +2344,67 @@
  * to reset or reload hardware state.
  */
 int
-ath_reset(struct ifnet *ifp)
+ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type)
 {
 	struct ath_softc *sc = ifp->if_softc;
 	struct ieee80211com *ic = ifp->if_l2com;
 	struct ath_hal *ah = sc->sc_ah;
 	HAL_STATUS status;
+	int i;
 
-	ath_hal_intrset(ah, 0);		/* disable interrupts */
-	ath_draintxq(sc);		/* stop xmit side */
-	ath_stoprecv(sc);		/* stop recv side */
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__);
+
+	/* Ensure ATH_LOCK isn't held; ath_rx_proc can't be locked */
+	ATH_PCU_UNLOCK_ASSERT(sc);
+	ATH_UNLOCK_ASSERT(sc);
+
+	/* Try to (stop any further TX/RX from occuring */
+	taskqueue_block(sc->sc_tq);
+
+	ATH_PCU_LOCK(sc);
+
+	/*
+	 * Grab the reset lock before TX/RX is stopped.
+	 *
+	 * This is needed to ensure that when the TX/RX actually does finish,
+	 * no further TX/RX/reset runs in parallel with this.
+	 */
+	if (ath_reset_grablock(sc, 1) == 0) {
+		device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n",
+		    __func__);
+	}
+
+	/* disable interrupts */
+	ath_hal_intrset(ah, 0);
+
+	/*
+	 * Now, ensure that any in progress TX/RX completes before we
+	 * continue.
+	 */
+	ath_txrx_stop_locked(sc);
+
+	ATH_PCU_UNLOCK(sc);
+
+	/*
+	 * Should now wait for pending TX/RX to complete
+	 * and block future ones from occuring. This needs to be
+	 * done before the TX queue is drained.
+	 */
+	ath_draintxq(sc, reset_type);	/* stop xmit side */
+
+	/*
+	 * Regardless of whether we're doing a no-loss flush or
+	 * not, stop the PCU and handle what's in the RX queue.
+	 * That way frames aren't dropped which shouldn't be.
+	 */
+	ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS));
+	ath_rx_flush(sc);
+
 	ath_settkipmic(sc);		/* configure TKIP MIC handling */
 	/* NB: indicate channel change so we do a full reset */
+	ath_update_chainmasks(sc, ic->ic_curchan);
+	ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask,
+	    sc->sc_cur_rxchainmask);
 	if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_TRUE, &status))
 		if_printf(ifp, "%s: unable to reset hardware; hal status %u\n",
 			__func__, status);
@@ -1743,6 +2413,23 @@
 	/* Let DFS at it in case it's a DFS channel */
 	ath_dfs_radar_enable(sc, ic->ic_curchan);
 
+	/* Let spectral at in case spectral is enabled */
+	ath_spectral_enable(sc, ic->ic_curchan);
+
+	/*
+	 * Let bluetooth coexistence at in case it's needed for this channel
+	 */
+	ath_btcoex_enable(sc, ic->ic_curchan);
+
+	/*
+	 * If we're doing TDMA, enforce the TXOP limitation for chips that
+	 * support it.
+	 */
+	if (sc->sc_hasenforcetxop && sc->sc_tdma)
+		ath_hal_setenforcetxop(sc->sc_ah, 1);
+	else
+		ath_hal_setenforcetxop(sc->sc_ah, 0);
+
 	if (ath_startrecv(sc) != 0)	/* restart recv */
 		if_printf(ifp, "%s: unable to start recv logic\n", __func__);
 	/*
@@ -1759,9 +2446,64 @@
 #endif
 			ath_beacon_config(sc, NULL);
 	}
+
+	/*
+	 * Release the reset lock and re-enable interrupts here.
+	 * If an interrupt was being processed in ath_intr(),
+	 * it would disable interrupts at this point. So we have
+	 * to atomically enable interrupts and decrement the
+	 * reset counter - this way ath_intr() doesn't end up
+	 * disabling interrupts without a corresponding enable
+	 * in the rest or channel change path.
+	 */
+	ATH_PCU_LOCK(sc);
+	sc->sc_inreset_cnt--;
+	/* XXX only do this if sc_inreset_cnt == 0? */
 	ath_hal_intrset(ah, sc->sc_imask);
+	ATH_PCU_UNLOCK(sc);
 
-	ath_start(ifp);			/* restart xmit */
+	/*
+	 * TX and RX can be started here. If it were started with
+	 * sc_inreset_cnt > 0, the TX and RX path would abort.
+	 * Thus if this is a nested call through the reset or
+	 * channel change code, TX completion will occur but
+	 * RX completion and ath_start / ath_tx_start will not
+	 * run.
+	 */
+
+	/* Restart TX/RX as needed */
+	ath_txrx_start(sc);
+
+	/* Restart TX completion and pending TX */
+	if (reset_type == ATH_RESET_NOLOSS) {
+		for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+			if (ATH_TXQ_SETUP(sc, i)) {
+				ATH_TXQ_LOCK(&sc->sc_txq[i]);
+				ath_txq_restart_dma(sc, &sc->sc_txq[i]);
+				ATH_TXQ_UNLOCK(&sc->sc_txq[i]);
+
+				ATH_TX_LOCK(sc);
+				ath_txq_sched(sc, &sc->sc_txq[i]);
+				ATH_TX_UNLOCK(sc);
+			}
+		}
+	}
+
+	/*
+	 * This may have been set during an ath_start() call which
+	 * set this once it detected a concurrent TX was going on.
+	 * So, clear it.
+	 */
+	IF_LOCK(&ifp->if_snd);
+	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	IF_UNLOCK(&ifp->if_snd);
+
+	/* Handle any frames in the TX queue */
+	/*
+	 * XXX should this be done by the caller, rather than
+	 * ath_reset() ?
+	 */
+	ath_tx_kick(sc);		/* restart xmit */
 	return 0;
 }
 
@@ -1784,141 +2526,455 @@
 			ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
 		return 0;
 	}
-	return ath_reset(ifp);
+	/* XXX? Full or NOLOSS? */
+	return ath_reset(ifp, ATH_RESET_FULL);
 }
 
 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 = STAILQ_FIRST(&sc->sc_txbuf);
-	if (bf != NULL && (bf->bf_flags & ATH_BUF_BUSY) == 0)
-		STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+	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 {
+		if (bf->bf_flags & ATH_BUF_BUSY) {
+			sc->sc_stats.ast_tx_getbusybuf++;
+			bf = NULL;
+		}
+	}
+
+	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__,
-		    STAILQ_FIRST(&sc->sc_txbuf) == NULL ?
+		    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) */
+	bf->bf_flags = 0;
+	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 */
+	bf->bf_comp = NULL;	/* XXX again, just to be sure */
+	bzero(&bf->bf_state, sizeof(bf->bf_state));
+
+	/*
+	 * Track the descriptor ID only if doing EDMA
+	 */
+	if (sc->sc_isedma) {
+		bf->bf_descid = sc->sc_txbuf_descid;
+		sc->sc_txbuf_descid++;
+	}
+
 	return bf;
 }
 
+/*
+ * When retrying a software frame, buffers marked ATH_BUF_BUSY
+ * can't be thrown back on the queue as they could still be
+ * in use by the hardware.
+ *
+ * This duplicates the buffer, or returns NULL.
+ *
+ * The descriptor is also copied but the link pointers and
+ * the DMA segments aren't copied; this frame should thus
+ * be again passed through the descriptor setup/chain routines
+ * so the link is correct.
+ *
+ * The caller must free the buffer using ath_freebuf().
+ */
 struct ath_buf *
-ath_getbuf(struct ath_softc *sc)
+ath_buf_clone(struct ath_softc *sc, struct ath_buf *bf)
 {
+	struct ath_buf *tbf;
+
+	tbf = ath_getbuf(sc,
+	    (bf->bf_flags & ATH_BUF_MGMT) ?
+	     ATH_BUFTYPE_MGMT : ATH_BUFTYPE_NORMAL);
+	if (tbf == NULL)
+		return NULL;	/* XXX failure? Why? */
+
+	/* Copy basics */
+	tbf->bf_next = NULL;
+	tbf->bf_nseg = bf->bf_nseg;
+	tbf->bf_flags = bf->bf_flags & ATH_BUF_FLAGS_CLONE;
+	tbf->bf_status = bf->bf_status;
+	tbf->bf_m = bf->bf_m;
+	tbf->bf_node = bf->bf_node;
+	/* will be setup by the chain/setup function */
+	tbf->bf_lastds = NULL;
+	/* for now, last == self */
+	tbf->bf_last = tbf;
+	tbf->bf_comp = bf->bf_comp;
+
+	/* NOTE: DMA segments will be setup by the setup/chain functions */
+
+	/* The caller has to re-init the descriptor + links */
+
+	/*
+	 * Free the DMA mapping here, before we NULL the mbuf.
+	 * We must only call bus_dmamap_unload() once per mbuf chain
+	 * or behaviour is undefined.
+	 */
+	if (bf->bf_m != NULL) {
+		/*
+		 * XXX is this POSTWRITE call required?
+		 */
+		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+		    BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+	}
+
+	bf->bf_m = NULL;
+	bf->bf_node = NULL;
+
+	/* Copy state */
+	memcpy(&tbf->bf_state, &bf->bf_state, sizeof(bf->bf_state));
+
+	return tbf;
+}
+
+struct ath_buf *
+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;
 
 		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__);
 		sc->sc_stats.ast_tx_qstop++;
+		IF_LOCK(&ifp->if_snd);
 		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+		IF_UNLOCK(&ifp->if_snd);
 	}
-	ATH_TXBUF_UNLOCK(sc);
 	return bf;
 }
 
 static void
-ath_start(struct ifnet *ifp)
+ath_qflush(struct ifnet *ifp)
 {
-	struct ath_softc *sc = ifp->if_softc;
+
+	/* XXX TODO */
+}
+
+/*
+ * Transmit a single frame.
+ *
+ * net80211 will free the node reference if the transmit
+ * fails, so don't free the node reference here.
+ */
+static int
+ath_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+	struct ieee80211com *ic = ifp->if_l2com;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
 	struct ieee80211_node *ni;
+	struct mbuf *next;
 	struct ath_buf *bf;
-	struct mbuf *m, *next;
 	ath_bufhead frags;
+	int retval = 0;
 
-	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid)
-		return;
-	for (;;) {
+	/*
+	 * Tell the reset path that we're currently transmitting.
+	 */
+	ATH_PCU_LOCK(sc);
+	if (sc->sc_inreset_cnt > 0) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%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);
+		ATH_KTR(sc, ATH_KTR_TX, 0, "ath_start_task: OACTIVE, finish");
+		return (ENOBUFS);	/* XXX should be EINVAL or? */
+	}
+	sc->sc_txstart_cnt++;
+	ATH_PCU_UNLOCK(sc);
+
+	ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: start");
+	/*
+	 * Grab the TX lock - it's ok to do this here; we haven't
+	 * yet started transmitting.
+	 */
+	ATH_TX_LOCK(sc);
+
+	/*
+	 * Node reference, if there's one.
+	 */
+	ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+
+	/*
+	 * Enforce how deep a node queue can get.
+	 *
+	 * XXX it would be nicer if we kept an mbuf queue per
+	 * node and only whacked them into ath_bufs when we
+	 * are ready to schedule some traffic from them.
+	 * .. that may come later.
+	 *
+	 * XXX we should also track the per-node hardware queue
+	 * depth so it is easy to limit the _SUM_ of the swq and
+	 * hwq frames.  Since we only schedule two HWQ frames
+	 * at a time, this should be OK for now.
+	 */
+	if ((!(m->m_flags & M_EAPOL)) &&
+	    (ATH_NODE(ni)->an_swq_depth > sc->sc_txq_node_maxdepth)) {
+		sc->sc_stats.ast_tx_nodeq_overflow++;
+		m_freem(m);
+		m = NULL;
+		retval = ENOBUFS;
+		goto finish;
+	}
+
+	/*
+	 * Check how many TX buffers are available.
+	 *
+	 * If this is for non-EAPOL traffic, just leave some
+	 * space free in order for buffer cloning and raw
+	 * frame transmission to occur.
+	 *
+	 * If it's for EAPOL traffic, ignore this for now.
+	 * Management traffic will be sent via the raw transmit
+	 * method which bypasses this check.
+	 *
+	 * This is needed to ensure that EAPOL frames during
+	 * (re) keying have a chance to go out.
+	 *
+	 * See kern/138379 for more information.
+	 */
+	if ((!(m->m_flags & M_EAPOL)) &&
+	    (sc->sc_txbuf_cnt <= sc->sc_txq_data_minfree)) {
+		sc->sc_stats.ast_tx_nobuf++;
+		m_freem(m);
+		m = NULL;
+		retval = ENOBUFS;
+		goto finish;
+	}
+
+	/*
+	 * Grab a TX buffer and associated resources.
+	 *
+	 * If it's an EAPOL frame, allocate a MGMT ath_buf.
+	 * That way even with temporary buffer exhaustion due to
+	 * the data path doesn't leave us without the ability
+	 * to transmit management frames.
+	 *
+	 * Otherwise allocate a normal buffer.
+	 */
+	if (m->m_flags & M_EAPOL)
+		bf = ath_getbuf(sc, ATH_BUFTYPE_MGMT);
+	else
+		bf = ath_getbuf(sc, ATH_BUFTYPE_NORMAL);
+
+	if (bf == NULL) {
 		/*
-		 * Grab a TX buffer and associated resources.
+		 * If we failed to allocate a buffer, fail.
+		 *
+		 * We shouldn't fail normally, due to the check
+		 * above.
 		 */
-		bf = ath_getbuf(sc);
-		if (bf == NULL)
-			break;
+		sc->sc_stats.ast_tx_nobuf++;
+		IF_LOCK(&ifp->if_snd);
+		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+		IF_UNLOCK(&ifp->if_snd);
+		m_freem(m);
+		m = NULL;
+		retval = ENOBUFS;
+		goto finish;
+	}
 
-		IFQ_DEQUEUE(&ifp->if_snd, m);
-		if (m == NULL) {
-			ATH_TXBUF_LOCK(sc);
-			STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
-			ATH_TXBUF_UNLOCK(sc);
-			break;
-		}
-		ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+	/*
+	 * At this point we have a buffer; so we need to free it
+	 * if we hit any error conditions.
+	 */
+
+	/*
+	 * Check for fragmentation.  If this frame
+	 * has been broken up verify we have enough
+	 * buffers to send all the fragments so all
+	 * go out or none...
+	 */
+	TAILQ_INIT(&frags);
+	if ((m->m_flags & M_FRAG) &&
+	    !ath_txfrag_setup(sc, &frags, m, ni)) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: out of txfrag buffers\n", __func__);
+		sc->sc_stats.ast_tx_nofrag++;
+		ifp->if_oerrors++;
+		ath_freetx(m);
+		goto bad;
+	}
+
+	/*
+	 * At this point if we have any TX fragments, then we will
+	 * have bumped the node reference once for each of those.
+	 */
+
+	/*
+	 * XXX Is there anything actually _enforcing_ that the
+	 * fragments are being transmitted in one hit, rather than
+	 * being interleaved with other transmissions on that
+	 * hardware queue?
+	 *
+	 * The ATH TX output lock is the only thing serialising this
+	 * right now.
+	 */
+
+	/*
+	 * Calculate the "next fragment" length field in ath_buf
+	 * in order to let the transmit path know enough about
+	 * what to next write to the hardware.
+	 */
+	if (m->m_flags & M_FRAG) {
+		struct ath_buf *fbf = bf;
+		struct ath_buf *n_fbf = NULL;
+		struct mbuf *fm = m->m_nextpkt;
+
 		/*
-		 * Check for fragmentation.  If this frame
-		 * has been broken up verify we have enough
-		 * buffers to send all the fragments so all
-		 * go out or none...
+		 * We need to walk the list of fragments and set
+		 * the next size to the following buffer.
+		 * However, the first buffer isn't in the frag
+		 * list, so we have to do some gymnastics here.
 		 */
-		STAILQ_INIT(&frags);
-		if ((m->m_flags & M_FRAG) &&
-		    !ath_txfrag_setup(sc, &frags, m, ni)) {
-			DPRINTF(sc, ATH_DEBUG_XMIT,
-			    "%s: out of txfrag buffers\n", __func__);
-			sc->sc_stats.ast_tx_nofrag++;
-			ifp->if_oerrors++;
-			ath_freetx(m);
-			goto bad;
+		TAILQ_FOREACH(n_fbf, &frags, bf_list) {
+			fbf->bf_nextfraglen = fm->m_pkthdr.len;
+			fbf = n_fbf;
+			fm = fm->m_nextpkt;
 		}
-		ifp->if_opackets++;
-	nextfrag:
+	}
+
+	/*
+	 * Bump the ifp output counter.
+	 *
+	 * XXX should use atomics?
+	 */
+	ifp->if_opackets++;
+nextfrag:
+	/*
+	 * Pass the frame to the h/w for transmission.
+	 * Fragmented frames have each frag chained together
+	 * with m_nextpkt.  We know there are sufficient ath_buf's
+	 * to send all the frags because of work done by
+	 * ath_txfrag_setup.  We leave m_nextpkt set while
+	 * calling ath_tx_start so it can use it to extend the
+	 * the tx duration to cover the subsequent frag and
+	 * so it can reclaim all the mbufs in case of an error;
+	 * ath_tx_start clears m_nextpkt once it commits to
+	 * handing the frame to the hardware.
+	 *
+	 * Note: if this fails, then the mbufs are freed but
+	 * not the node reference.
+	 */
+	next = m->m_nextpkt;
+	if (ath_tx_start(sc, ni, bf, m)) {
+bad:
+		ifp->if_oerrors++;
+reclaim:
+		bf->bf_m = NULL;
+		bf->bf_node = NULL;
+		ATH_TXBUF_LOCK(sc);
+		ath_returnbuf_head(sc, bf);
 		/*
-		 * Pass the frame to the h/w for transmission.
-		 * Fragmented frames have each frag chained together
-		 * with m_nextpkt.  We know there are sufficient ath_buf's
-		 * to send all the frags because of work done by
-		 * ath_txfrag_setup.  We leave m_nextpkt set while
-		 * calling ath_tx_start so it can use it to extend the
-		 * the tx duration to cover the subsequent frag and
-		 * so it can reclaim all the mbufs in case of an error;
-		 * ath_tx_start clears m_nextpkt once it commits to
-		 * handing the frame to the hardware.
+		 * Free the rest of the node references and
+		 * buffers for the fragment list.
 		 */
-		next = m->m_nextpkt;
-		if (ath_tx_start(sc, ni, bf, m)) {
-	bad:
-			ifp->if_oerrors++;
-	reclaim:
-			bf->bf_m = NULL;
-			bf->bf_node = NULL;
-			ATH_TXBUF_LOCK(sc);
-			STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
-			ath_txfrag_cleanup(sc, &frags, ni);
-			ATH_TXBUF_UNLOCK(sc);
-			if (ni != NULL)
-				ieee80211_free_node(ni);
-			continue;
+		ath_txfrag_cleanup(sc, &frags, ni);
+		ATH_TXBUF_UNLOCK(sc);
+		retval = ENOBUFS;
+		goto finish;
+	}
+
+	/*
+	 * Check here if the node is in power save state.
+	 */
+	ath_tx_update_tim(sc, ni, 1);
+
+	if (next != NULL) {
+		/*
+		 * Beware of state changing between frags.
+		 * XXX check sta power-save state?
+		 */
+		if (ni->ni_vap->iv_state != IEEE80211_S_RUN) {
+			DPRINTF(sc, ATH_DEBUG_XMIT,
+			    "%s: flush fragmented packet, state %s\n",
+			    __func__,
+			    ieee80211_state_name[ni->ni_vap->iv_state]);
+			/* XXX dmamap */
+			ath_freetx(next);
+			goto reclaim;
 		}
-		if (next != NULL) {
-			/*
-			 * Beware of state changing between frags.
-			 * XXX check sta power-save state?
-			 */
-			if (ni->ni_vap->iv_state != IEEE80211_S_RUN) {
-				DPRINTF(sc, ATH_DEBUG_XMIT,
-				    "%s: flush fragmented packet, state %s\n",
-				    __func__,
-				    ieee80211_state_name[ni->ni_vap->iv_state]);
-				ath_freetx(next);
-				goto reclaim;
-			}
-			m = next;
-			bf = STAILQ_FIRST(&frags);
-			KASSERT(bf != NULL, ("no buf for txfrag"));
-			STAILQ_REMOVE_HEAD(&frags, bf_list);
-			goto nextfrag;
-		}
+		m = next;
+		bf = TAILQ_FIRST(&frags);
+		KASSERT(bf != NULL, ("no buf for txfrag"));
+		TAILQ_REMOVE(&frags, bf, bf_list);
+		goto nextfrag;
+	}
 
-		sc->sc_wd_timer = 5;
-	}
+	/*
+	 * Bump watchdog timer.
+	 */
+	sc->sc_wd_timer = 5;
+
+finish:
+	ATH_TX_UNLOCK(sc);
+
+	/*
+	 * Finished transmitting!
+	 */
+	ATH_PCU_LOCK(sc);
+	sc->sc_txstart_cnt--;
+	ATH_PCU_UNLOCK(sc);
+
+	ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: finished");
+	
+	return (retval);
 }
 
 static int
@@ -1957,99 +3013,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;
-
-	if (sc->sc_dodfs) {
-		rfilt |= HAL_RX_FILTER_PHYRADAR;
-	}
-
-	/*
-	 * 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;
-
-	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)
 {
@@ -2099,7 +3062,7 @@
 		__func__, mfilt[0], mfilt[1]);
 }
 
-static void
+void
 ath_mode_init(struct ath_softc *sc)
 {
 	struct ifnet *ifp = sc->sc_ifp;
@@ -2113,6 +3076,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));
 
@@ -2123,7 +3093,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;
@@ -2176,499 +3146,42 @@
 }
 
 /*
- * 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;
-	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;
-
-	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)
 {
-	STAILQ_CONCAT(&dst->axq_q, &src->axq_q);
+
+	ATH_TXQ_LOCK_ASSERT(src);
+	ATH_TXQ_LOCK_ASSERT(dst);
+
+	TAILQ_CONCAT(&dst->axq_q, &src->axq_q, bf_list);
 	dst->axq_link = src->axq_link;
 	src->axq_link = NULL;
 	dst->axq_depth += src->axq_depth;
+	dst->axq_aggr_depth += src->axq_aggr_depth;
 	src->axq_depth = 0;
+	src->axq_aggr_depth = 0;
 }
 
 /*
- * 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.
+ * Reset the hardware, with no loss.
+ *
+ * This can't be used for a general case reset.
  */
 static void
-ath_beacon_proc(void *arg, int pending)
+ath_reset_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;
+	struct ifnet *ifp = sc->sc_ifp;
 
-	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++;
-	}
+#if 0
+	if_printf(ifp, "%s: resetting\n", __func__);
+#endif
+	ath_reset(ifp, ATH_RESET_NOLOSS);
 }
 
-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;
-	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 = STAILQ_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 */
-		ath_hal_txstart(ah, cabq->axq_qnum);
-		ATH_TXQ_UNLOCK(cabq);
-		ATH_TXQ_UNLOCK(&avp->av_mcastq);
-	}
-	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 after detecting beacons have stopped.
  */
@@ -2677,257 +3190,27 @@
 {
 	struct ath_softc *sc = arg;
 	struct ifnet *ifp = sc->sc_ifp;
+	uint32_t hangs = 0;
 
+	if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0)
+		if_printf(ifp, "bb hang detected (0x%x)\n", hangs);
+
+#ifdef	ATH_DEBUG_ALQ
+	if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_STUCK_BEACON))
+		if_ath_alq_post(&sc->sc_alq, ATH_ALQ_STUCK_BEACON, 0, NULL);
+#endif
+
 	if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n",
 		sc->sc_bmisscount);
 	sc->sc_stats.ast_bstuck++;
-	ath_reset(ifp);
+	/*
+	 * This assumes that there's no simultaneous channel mode change
+	 * occuring.
+	 */
+	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)
-{
-
-	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;
-	}
-	STAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list);
-}
-
-/*
- * Reclaim beacon resources.
- */
-static void
-ath_beacon_free(struct ath_softc *sc)
-{
-	struct ath_buf *bf;
-
-	STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) {
-		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 = 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;
-#undef FUDGE
-#undef TSF_TO_TU
-}
-
-static void
 ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
 {
 	bus_addr_t *paddr = (bus_addr_t*) arg;
@@ -2935,10 +3218,16 @@
 	*paddr = segs->ds_addr;
 }
 
-static int
-ath_descdma_setup(struct ath_softc *sc,
+/*
+ * Allocate the descriptors and appropriate DMA tag/setup.
+ *
+ * For some situations (eg EDMA TX completion), there isn't a requirement
+ * for the ath_buf entries to be allocated.
+ */
+int
+ath_descdma_alloc_desc(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 ndesc)
 {
 #define	DS2PHYS(_dd, _ds) \
 	((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
@@ -2945,18 +3234,16 @@
 #define	ATH_DESC_4KB_BOUND_CHECK(_daddr, _len) \
 	((((u_int32_t)(_daddr) & 0xFFF) > (0x1000 - (_len))) ? 1 : 0)
 	struct ifnet *ifp = sc->sc_ifp;
-	uint8_t *ds;
-	struct ath_buf *bf;
-	int i, bsize, error;
-	int desc_len;
+	int error;
 
-	desc_len = sizeof(struct ath_desc);
+	dd->dd_descsize = ds_size;
 
-	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n",
-	    __func__, name, nbuf, ndesc);
+	DPRINTF(sc, ATH_DEBUG_RESET,
+	    "%s: %s DMA: %u desc, %d bytes per descriptor\n",
+	    __func__, name, ndesc, dd->dd_descsize);
 
 	dd->dd_name = name;
-	dd->dd_desc_len = desc_len * nbuf * ndesc;
+	dd->dd_desc_len = dd->dd_descsize * ndesc;
 
 	/*
 	 * Merlin work-around:
@@ -2964,12 +3251,15 @@
 	 * Assume one skipped descriptor per 4KB page.
 	 */
 	if (! ath_hal_split4ktrans(sc->sc_ah)) {
-		int numdescpage = 4096 / (desc_len * ndesc);
-		dd->dd_desc_len = (nbuf / numdescpage + 1) * 4096;
+		int numpages = dd->dd_desc_len / 4096;
+		dd->dd_desc_len += ds_size * numpages;
 	}
 
 	/*
 	 * Setup DMA descriptor area.
+	 *
+	 * BUS_DMA_ALLOCNOW is not used; we never use bounce
+	 * buffers for the descriptors themselves.
 	 */
 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev),	/* parent */
 		       PAGE_SIZE, 0,		/* alignment, bounds */
@@ -2979,7 +3269,7 @@
 		       dd->dd_desc_len,		/* maxsize */
 		       1,			/* nsegments */
 		       dd->dd_desc_len,		/* maxsegsize */
-		       BUS_DMA_ALLOCNOW,	/* flags */
+		       0,			/* flags */
 		       NULL,			/* lockfunc */
 		       NULL,			/* lockarg */
 		       &dd->dd_dmat);
@@ -2989,19 +3279,12 @@
 	}
 
 	/* allocate descriptors */
-	error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_NOWAIT, &dd->dd_dmamap);
-	if (error != 0) {
-		if_printf(ifp, "unable to create dmamap for %s descriptors, "
-			"error %u\n", dd->dd_name, error);
-		goto fail0;
-	}
-
 	error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc,
 				 BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
 				 &dd->dd_dmamap);
 	if (error != 0) {
 		if_printf(ifp, "unable to alloc memory for %u %s descriptors, "
-			"error %u\n", nbuf * ndesc, dd->dd_name, error);
+			"error %u\n", ndesc, dd->dd_name, error);
 		goto fail1;
 	}
 
@@ -3015,11 +3298,48 @@
 		goto fail2;
 	}
 
-	ds = (uint8_t *) dd->dd_desc;
 	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n",
-	    __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len,
-	    (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len);
+	    __func__, dd->dd_name, (uint8_t *) dd->dd_desc,
+	    (u_long) dd->dd_desc_len, (caddr_t) dd->dd_desc_paddr,
+	    /*XXX*/ (u_long) dd->dd_desc_len);
 
+	return (0);
+
+fail2:
+	bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+fail1:
+	bus_dma_tag_destroy(dd->dd_dmat);
+	memset(dd, 0, sizeof(*dd));
+	return error;
+#undef DS2PHYS
+#undef ATH_DESC_4KB_BOUND_CHECK
+}
+
+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)
+{
+#define	DS2PHYS(_dd, _ds) \
+	((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
+#define	ATH_DESC_4KB_BOUND_CHECK(_daddr, _len) \
+	((((u_int32_t)(_daddr) & 0xFFF) > (0x1000 - (_len))) ? 1 : 0)
+	struct ifnet *ifp = sc->sc_ifp;
+	uint8_t *ds;
+	struct ath_buf *bf;
+	int i, bsize, error;
+
+	/* Allocate descriptors */
+	error = ath_descdma_alloc_desc(sc, dd, head, name, ds_size,
+	    nbuf * ndesc);
+
+	/* Assume any errors during allocation were dealt with */
+	if (error != 0) {
+		return (error);
+	}
+
+	ds = (uint8_t *) dd->dd_desc;
+
 	/* allocate rx buffers */
 	bsize = sizeof(struct ath_buf) * nbuf;
 	bf = malloc(bsize, M_ATHDEV, M_NOWAIT | M_ZERO);
@@ -3030,8 +3350,8 @@
 	}
 	dd->dd_bufptr = bf;
 
-	STAILQ_INIT(head);
-	for (i = 0; i < nbuf; i++, bf++, ds += (ndesc * desc_len)) {
+	TAILQ_INIT(head);
+	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)) {
@@ -3041,7 +3361,7 @@
 			 * in the descriptor.
 			 */
 			 if (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr,
-			     desc_len * ndesc)) {
+			     dd->dd_descsize)) {
 				/* Start at the next page */
 				ds += 0x1000 - (bf->bf_daddr & 0xFFF);
 				bf->bf_desc = (struct ath_desc *) ds;
@@ -3056,16 +3376,20 @@
 			ath_descdma_cleanup(sc, dd, head);
 			return error;
 		}
-		STAILQ_INSERT_TAIL(head, bf, bf_list);
+		bf->bf_lastds = bf->bf_desc;	/* Just an initial value */
+		TAILQ_INSERT_TAIL(head, bf, bf_list);
 	}
+
+	/*
+	 * XXX TODO: ensure that ds doesn't overflow the descriptor
+	 * allocation otherwise weird stuff will occur and crash your
+	 * machine.
+	 */
 	return 0;
+	/* XXX this should likely just call ath_descdma_cleanup() */
 fail3:
 	bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
-fail2:
 	bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
-fail1:
-	bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
-fail0:
 	bus_dma_tag_destroy(dd->dd_dmat);
 	memset(dd, 0, sizeof(*dd));
 	return error;
@@ -3073,39 +3397,126 @@
 #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;
+	int do_warning = 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);
+	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_dma_tag_destroy(dd->dd_dmat);
+	}
 
-	STAILQ_FOREACH(bf, head, bf_list) {
-		if (bf->bf_m) {
-			m_freem(bf->bf_m);
-			bf->bf_m = NULL;
+	if (head != NULL) {
+		TAILQ_FOREACH(bf, head, bf_list) {
+			if (bf->bf_m) {
+				/*
+				 * XXX warn if there's buffers here.
+				 * XXX it should have been freed by the
+				 * owner!
+				 */
+				
+				if (do_warning == 0) {
+					do_warning = 1;
+					device_printf(sc->sc_dev,
+					    "%s: %s: mbuf should've been"
+					    " unmapped/freed!\n",
+					    __func__,
+					    dd->dd_name);
+				}
+				bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+				    BUS_DMASYNC_POSTREAD);
+				bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+				m_freem(bf->bf_m);
+				bf->bf_m = NULL;
+			}
+			if (bf->bf_dmamap != NULL) {
+				bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap);
+				bf->bf_dmamap = NULL;
+			}
+			ni = bf->bf_node;
+			bf->bf_node = NULL;
+			if (ni != NULL) {
+				/*
+				 * Reclaim node reference.
+				 */
+				ieee80211_free_node(ni);
+			}
 		}
-		if (bf->bf_dmamap != NULL) {
-			bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap);
-			bf->bf_dmamap = NULL;
-		}
-		ni = bf->bf_node;
-		bf->bf_node = NULL;
-		if (ni != NULL) {
-			/*
-			 * Reclaim node reference.
-			 */
-			ieee80211_free_node(ni);
-		}
 	}
 
-	STAILQ_INIT(head);
-	free(dd->dd_bufptr, M_ATHDEV);
+	if (head != NULL)
+		TAILQ_INIT(head);
+
+	if (dd->dd_bufptr != NULL)
+		free(dd->dd_bufptr, M_ATHDEV);
 	memset(dd, 0, sizeof(*dd));
 }
 
@@ -3114,23 +3525,32 @@
 {
 	int error;
 
-	error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
-			"rx", ath_rxbuf, 1);
-	if (error != 0)
+	error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
+		    "tx", sc->sc_tx_desclen, ath_txbuf, ATH_MAX_SCATTER);
+	if (error != 0) {
 		return error;
+	}
+	sc->sc_txbuf_cnt = ath_txbuf;
 
-	error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
-			"tx", ath_txbuf, ATH_TXDESC);
+	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_rxdma, &sc->sc_rxbuf);
+		ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
 		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", ATH_BCBUF, 1);
+			"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_rxdma, &sc->sc_rxbuf);
+		ath_descdma_cleanup(sc, &sc->sc_txdma_mgmt,
+		    &sc->sc_txbuf_mgmt);
 		return error;
 	}
 	return 0;
@@ -3144,8 +3564,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 *
@@ -3163,19 +3584,42 @@
 	}
 	ath_rate_node_init(sc, an);
 
-	DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an);
+	/* Setup the mutex - there's no associd yet so set the name to NULL */
+	snprintf(an->an_name, sizeof(an->an_name), "%s: node %p",
+	    device_get_nameunit(sc->sc_dev), an);
+	mtx_init(&an->an_mtx, an->an_name, NULL, MTX_DEF);
+
+	/* XXX setup ath_tid */
+	ath_tx_tid_init(sc, an);
+
+	DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, mac, ":", an);
 	return &an->an_node;
 }
 
 static void
-ath_node_free(struct ieee80211_node *ni)
+ath_node_cleanup(struct ieee80211_node *ni)
 {
 	struct ieee80211com *ic = ni->ni_ic;
-        struct ath_softc *sc = ic->ic_ifp->if_softc;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
 
-	DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni);
+	DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__,
+	    ni->ni_macaddr, ":", ATH_NODE(ni));
 
+	/* Cleanup ath_tid, free unused bufs, unlink bufs in TXQ */
+	ath_tx_node_flush(sc, ATH_NODE(ni));
 	ath_rate_node_cleanup(sc, ATH_NODE(ni));
+	sc->sc_node_cleanup(ni);
+}
+
+static void
+ath_node_free(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+	DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__,
+	    ni->ni_macaddr, ":", ATH_NODE(ni));
+	mtx_destroy(&ATH_NODE(ni)->an_mtx);
 	sc->sc_node_free(ni);
 }
 
@@ -3193,196 +3637,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 */
-		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;
@@ -3396,458 +3654,18 @@
 }
 
 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);
-	}
-}
-
-static void
-ath_rx_proc(void *arg, int npending)
-{
-#define	PA2DESC(_sc, _pa) \
-	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
-		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
-	struct ath_softc *sc = arg;
-	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;
-
-	DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
-	ngood = 0;
-	nf = ath_hal_getchannoise(ah, sc->sc_curchan);
-	sc->sc_stats.ast_rx_noise = nf;
-	tsf = ath_hal_gettsf64(ah);
-	do {
-		bf = STAILQ_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__);
-			STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, 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;
-		STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
-
-		/* These aren't specifically errors */
-		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++;
-
-		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 *), tsf, 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;
-				ath_rx_tap(ifp, m, rs, tsf, nf);
-				ieee80211_radiotap_rx_all(ic, 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;
-		}
-
-		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, tsf, 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;
-
-		if (rs->rs_isaggr)
-			sc->sc_stats.ast_rx_agg++;
-
-		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:
-		STAILQ_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;
-
-	/* Queue DFS tasklet if needed */
-	if (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.
-	 */
-	if (sc->sc_kickpcu) {
-		sc->sc_kickpcu = 0;
-		ath_stoprecv(sc);
-		sc->sc_imask |= (HAL_INT_RXEOL | HAL_INT_RXORN);
-		if (ath_startrecv(sc) != 0) {
-			if_printf(ifp,
-			    "%s: couldn't restart RX after RXEOL; resetting\n",
-			    __func__);
-			ath_reset(ifp);
-			return;
-		}
-		ath_hal_intrset(ah, sc->sc_imask);
-	}
-
-	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_start(ifp);
-	}
-#undef PA2DESC
-}
-
-static void
 ath_txq_init(struct ath_softc *sc, struct ath_txq *txq, int qnum)
 {
 	txq->axq_qnum = qnum;
 	txq->axq_ac = 0;
 	txq->axq_depth = 0;
+	txq->axq_aggr_depth = 0;
 	txq->axq_intrcnt = 0;
 	txq->axq_link = NULL;
-	STAILQ_INIT(&txq->axq_q);
+	txq->axq_softc = sc;
+	TAILQ_INIT(&txq->axq_q);
+	TAILQ_INIT(&txq->axq_tidq);
+	TAILQ_INIT(&txq->fifo.axq_q);
 	ATH_TXQ_LOCK_INIT(sc, txq);
 }
 
@@ -3879,7 +3697,13 @@
 	 * up in which case the top half of the kernel may backup
 	 * due to a lack of tx descriptors.
 	 */
-	qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE;
+	if (sc->sc_isedma)
+		qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE |
+		    HAL_TXQ_TXOKINT_ENABLE;
+	else
+		qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE |
+		    HAL_TXQ_TXDESCINT_ENABLE;
+
 	qnum = ath_hal_setuptxqueue(ah, qtype, &qi);
 	if (qnum == -1) {
 		/*
@@ -3973,10 +3797,15 @@
 		qi.tqi_burstTime = qi.tqi_readyTime;
 	} else {
 #endif
+		/*
+		 * XXX shouldn't this just use the default flags
+		 * used in the previous queue setup?
+		 */
 		qi.tqi_qflags = HAL_TXQ_TXOKINT_ENABLE
 			      | HAL_TXQ_TXERRINT_ENABLE
 			      | HAL_TXQ_TXDESCINT_ENABLE
 			      | HAL_TXQ_TXURNINT_ENABLE
+			      | HAL_TXQ_TXEOLINT_ENABLE
 			      ;
 		qi.tqi_aifs = wmep->wmep_aifsn;
 		qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
@@ -4008,7 +3837,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;
@@ -4027,8 +3856,8 @@
 {
 
 	ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum);
+	sc->sc_txqsetup &= ~(1<<txq->axq_qnum);
 	ATH_TXQ_LOCK_DESTROY(txq);
-	sc->sc_txqsetup &= ~(1<<txq->axq_qnum);
 }
 
 /*
@@ -4057,21 +3886,218 @@
 	return (rix == 0xff ? 0 : rix);
 }
 
+static void
+ath_tx_update_stats(struct ath_softc *sc, struct ath_tx_status *ts,
+    struct ath_buf *bf)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ieee80211com *ic = ifp->if_l2com;
+	int sr, lr, pri;
+
+	if (ts->ts_status == 0) {
+		u_int8_t txant = ts->ts_antenna;
+		sc->sc_stats.ast_ant_tx[txant]++;
+		sc->sc_ant_tx[txant]++;
+		if (ts->ts_finaltsi != 0)
+			sc->sc_stats.ast_tx_altrate++;
+		pri = M_WME_GETAC(bf->bf_m);
+		if (pri >= WME_AC_VO)
+			ic->ic_wme.wme_hipri_traffic++;
+		if ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)
+			ni->ni_inact = ni->ni_inact_reload;
+	} else {
+		if (ts->ts_status & HAL_TXERR_XRETRY)
+			sc->sc_stats.ast_tx_xretries++;
+		if (ts->ts_status & HAL_TXERR_FIFO)
+			sc->sc_stats.ast_tx_fifoerr++;
+		if (ts->ts_status & HAL_TXERR_FILT)
+			sc->sc_stats.ast_tx_filtered++;
+		if (ts->ts_status & HAL_TXERR_XTXOP)
+			sc->sc_stats.ast_tx_xtxop++;
+		if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED)
+			sc->sc_stats.ast_tx_timerexpired++;
+
+		if (bf->bf_m->m_flags & M_FF)
+			sc->sc_stats.ast_ff_txerr++;
+	}
+	/* XXX when is this valid? */
+	if (ts->ts_flags & HAL_TX_DESC_CFG_ERR)
+		sc->sc_stats.ast_tx_desccfgerr++;
+	/*
+	 * This can be valid for successful frame transmission!
+	 * If there's a TX FIFO underrun during aggregate transmission,
+	 * the MAC will pad the rest of the aggregate with delimiters.
+	 * If a BA is returned, the frame is marked as "OK" and it's up
+	 * to the TX completion code to notice which frames weren't
+	 * successfully transmitted.
+	 */
+	if (ts->ts_flags & HAL_TX_DATA_UNDERRUN)
+		sc->sc_stats.ast_tx_data_underrun++;
+	if (ts->ts_flags & HAL_TX_DELIM_UNDERRUN)
+		sc->sc_stats.ast_tx_delim_underrun++;
+
+	sr = ts->ts_shortretry;
+	lr = ts->ts_longretry;
+	sc->sc_stats.ast_tx_shortretry += sr;
+	sc->sc_stats.ast_tx_longretry += lr;
+
+}
+
 /*
+ * The default completion. If fail is 1, this means
+ * "please don't retry the frame, and just return -1 status
+ * to the net80211 stack.
+ */
+void
+ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, int fail)
+{
+	struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
+	int st;
+
+	if (fail == 1)
+		st = -1;
+	else
+		st = ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) ?
+		    ts->ts_status : HAL_TXERR_XRETRY;
+
+#if 0
+	if (bf->bf_state.bfs_dobaw)
+		device_printf(sc->sc_dev,
+		    "%s: bf %p: seqno %d: dobaw should've been cleared!\n",
+		    __func__,
+		    bf,
+		    SEQNO(bf->bf_state.bfs_seqno));
+#endif
+	if (bf->bf_next != NULL)
+		device_printf(sc->sc_dev,
+		    "%s: bf %p: seqno %d: bf_next not NULL!\n",
+		    __func__,
+		    bf,
+		    SEQNO(bf->bf_state.bfs_seqno));
+
+	/*
+	 * Check if the node software queue is empty; if so
+	 * then clear the TIM.
+	 *
+	 * This needs to be done before the buffer is freed as
+	 * otherwise the node reference will have been released
+	 * and the node may not actually exist any longer.
+	 *
+	 * XXX I don't like this belonging here, but it's cleaner
+	 * to do it here right now then all the other places
+	 * where ath_tx_default_comp() is called.
+	 *
+	 * XXX TODO: during drain, ensure that the callback is
+	 * being called so we get a chance to update the TIM.
+	 */
+	if (bf->bf_node) {
+		ATH_TX_LOCK(sc);
+		ath_tx_update_tim(sc, bf->bf_node, 0);
+		ATH_TX_UNLOCK(sc);
+	}
+
+	/*
+	 * Do any tx complete callback.  Note this must
+	 * be done before releasing the node reference.
+	 * This will free the mbuf, release the net80211
+	 * node and recycle the ath_buf.
+	 */
+	ath_tx_freebuf(sc, bf, st);
+}
+
+/*
+ * Update rate control with the given completion status.
+ */
+void
+ath_tx_update_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni,
+    struct ath_rc_series *rc, struct ath_tx_status *ts, int frmlen,
+    int nframes, int nbad)
+{
+	struct ath_node *an;
+
+	/* Only for unicast frames */
+	if (ni == NULL)
+		return;
+
+	an = ATH_NODE(ni);
+	ATH_NODE_UNLOCK_ASSERT(an);
+
+	if ((ts->ts_status & HAL_TXERR_FILT) == 0) {
+		ATH_NODE_LOCK(an);
+		ath_rate_tx_complete(sc, an, rc, ts, frmlen, nframes, nbad);
+		ATH_NODE_UNLOCK(an);
+	}
+}
+
+/*
+ * Process the completion of the given buffer.
+ *
+ * This calls the rate control update and then the buffer completion.
+ * This will either free the buffer or requeue it.  In any case, the
+ * bf pointer should be treated as invalid after this function is called.
+ */
+void
+ath_tx_process_buf_completion(struct ath_softc *sc, struct ath_txq *txq,
+    struct ath_tx_status *ts, struct ath_buf *bf)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_node *an = NULL;
+
+	ATH_TX_UNLOCK_ASSERT(sc);
+	ATH_TXQ_UNLOCK_ASSERT(txq);
+
+	/* If unicast frame, update general statistics */
+	if (ni != NULL) {
+		an = ATH_NODE(ni);
+		/* update statistics */
+		ath_tx_update_stats(sc, ts, bf);
+	}
+
+	/*
+	 * Call the completion handler.
+	 * The completion handler is responsible for
+	 * calling the rate control code.
+	 *
+	 * Frames with no completion handler get the
+	 * rate control code called here.
+	 */
+	if (bf->bf_comp == NULL) {
+		if ((ts->ts_status & HAL_TXERR_FILT) == 0 &&
+		    (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0) {
+			/*
+			 * XXX assume this isn't an aggregate
+			 * frame.
+			 */
+			ath_tx_update_ratectrl(sc, ni,
+			     bf->bf_state.bfs_rc, ts,
+			    bf->bf_state.bfs_pktlen, 1,
+			    (ts->ts_status == 0 ? 0 : 1));
+		}
+		ath_tx_default_comp(sc, bf, 0);
+	} else
+		bf->bf_comp(sc, bf, 0);
+}
+
+
+
+/*
  * Process completed xmit descriptors from the specified queue.
+ * Kick the packet scheduler if needed. This can occur from this
+ * particular task.
  */
 static int
-ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
+ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched)
 {
 	struct ath_hal *ah = sc->sc_ah;
-	struct ifnet *ifp = sc->sc_ifp;
-	struct ieee80211com *ic = ifp->if_l2com;
-	struct ath_buf *bf, *last;
-	struct ath_desc *ds, *ds0;
+	struct ath_buf *bf;
+	struct ath_desc *ds;
 	struct ath_tx_status *ts;
 	struct ieee80211_node *ni;
-	struct ath_node *an;
-	int sr, lr, pri, nacked;
+#ifdef	IEEE80211_SUPPORT_SUPERG
+	struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+#endif	/* IEEE80211_SUPPORT_SUPERG */
+	int nacked;
 	HAL_STATUS status;
 
 	DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n",
@@ -4078,126 +4104,118 @@
 		__func__, txq->axq_qnum,
 		(caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum),
 		txq->axq_link);
+
+	ATH_KTR(sc, ATH_KTR_TXCOMP, 4,
+	    "ath_tx_processq: txq=%u head %p link %p depth %p",
+	    txq->axq_qnum,
+	    (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum),
+	    txq->axq_link,
+	    txq->axq_depth);
+
 	nacked = 0;
 	for (;;) {
 		ATH_TXQ_LOCK(txq);
 		txq->axq_intrcnt = 0;	/* reset periodic desc intr count */
-		bf = STAILQ_FIRST(&txq->axq_q);
+		bf = TAILQ_FIRST(&txq->axq_q);
 		if (bf == NULL) {
 			ATH_TXQ_UNLOCK(txq);
 			break;
 		}
-		ds0 = &bf->bf_desc[0];
-		ds = &bf->bf_desc[bf->bf_nseg - 1];
+		ds = bf->bf_lastds;	/* XXX must be setup correctly! */
 		ts = &bf->bf_status.ds_txstat;
+
 		status = ath_hal_txprocdesc(ah, ds, ts);
 #ifdef ATH_DEBUG
 		if (sc->sc_debug & ATH_DEBUG_XMIT_DESC)
 			ath_printtxbuf(sc, bf, txq->axq_qnum, 0,
 			    status == HAL_OK);
+		else if ((sc->sc_debug & ATH_DEBUG_RESET) && (dosched == 0))
+			ath_printtxbuf(sc, bf, txq->axq_qnum, 0,
+			    status == HAL_OK);
 #endif
+#ifdef	ATH_DEBUG_ALQ
+		if (if_ath_alq_checkdebug(&sc->sc_alq,
+		    ATH_ALQ_EDMA_TXSTATUS)) {
+			if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS,
+			sc->sc_tx_statuslen,
+			(char *) ds);
+		}
+#endif
+
 		if (status == HAL_EINPROGRESS) {
+			ATH_KTR(sc, ATH_KTR_TXCOMP, 3,
+			    "ath_tx_processq: txq=%u, bf=%p ds=%p, HAL_EINPROGRESS",
+			    txq->axq_qnum, bf, ds);
 			ATH_TXQ_UNLOCK(txq);
 			break;
 		}
-		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
-#ifdef IEEE80211_SUPPORT_TDMA
+		ATH_TXQ_REMOVE(txq, bf, bf_list);
+
+		/*
+		 * Sanity check.
+		 */
+		if (txq->axq_qnum != bf->bf_state.bfs_tx_queue) {
+			device_printf(sc->sc_dev,
+			    "%s: TXQ=%d: bf=%p, bfs_tx_queue=%d\n",
+			    __func__,
+			    txq->axq_qnum,
+			    bf,
+			    bf->bf_state.bfs_tx_queue);
+		}
+		if (txq->axq_qnum != bf->bf_last->bf_state.bfs_tx_queue) {
+			device_printf(sc->sc_dev,
+			    "%s: TXQ=%d: bf_last=%p, bfs_tx_queue=%d\n",
+			    __func__,
+			    txq->axq_qnum,
+			    bf->bf_last,
+			    bf->bf_last->bf_state.bfs_tx_queue);
+		}
+
+#if 0
 		if (txq->axq_depth > 0) {
 			/*
 			 * More frames follow.  Mark the buffer busy
 			 * so it's not re-used while the hardware may
 			 * still re-read the link field in the descriptor.
+			 *
+			 * Use the last buffer in an aggregate as that
+			 * is where the hardware may be - intermediate
+			 * descriptors won't be "busy".
 			 */
-			bf->bf_flags |= ATH_BUF_BUSY;
+			bf->bf_last->bf_flags |= ATH_BUF_BUSY;
 		} else
+			txq->axq_link = NULL;
 #else
-		if (txq->axq_depth == 0)
+		bf->bf_last->bf_flags |= ATH_BUF_BUSY;
 #endif
-			txq->axq_link = NULL;
-		ATH_TXQ_UNLOCK(txq);
+		if (bf->bf_state.bfs_aggr)
+			txq->axq_aggr_depth--;
 
 		ni = bf->bf_node;
-		if (ni != NULL) {
-			an = ATH_NODE(ni);
-			if (ts->ts_status == 0) {
-				u_int8_t txant = ts->ts_antenna;
-				sc->sc_stats.ast_ant_tx[txant]++;
-				sc->sc_ant_tx[txant]++;
-				if (ts->ts_finaltsi != 0)
-					sc->sc_stats.ast_tx_altrate++;
-				pri = M_WME_GETAC(bf->bf_m);
-				if (pri >= WME_AC_VO)
-					ic->ic_wme.wme_hipri_traffic++;
-				if ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0)
-					ni->ni_inact = ni->ni_inact_reload;
-			} else {
-				if (ts->ts_status & HAL_TXERR_XRETRY)
-					sc->sc_stats.ast_tx_xretries++;
-				if (ts->ts_status & HAL_TXERR_FIFO)
-					sc->sc_stats.ast_tx_fifoerr++;
-				if (ts->ts_status & HAL_TXERR_FILT)
-					sc->sc_stats.ast_tx_filtered++;
-				if (ts->ts_status & HAL_TXERR_XTXOP)
-					sc->sc_stats.ast_tx_xtxop++;
-				if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED)
-					sc->sc_stats.ast_tx_timerexpired++;
 
-				/* XXX HAL_TX_DATA_UNDERRUN */
-				/* XXX HAL_TX_DELIM_UNDERRUN */
-
-				if (bf->bf_m->m_flags & M_FF)
-					sc->sc_stats.ast_ff_txerr++;
-			}
-			/* XXX when is this valid? */
-			if (ts->ts_status & HAL_TX_DESC_CFG_ERR)
-				sc->sc_stats.ast_tx_desccfgerr++;
-
-			sr = ts->ts_shortretry;
-			lr = ts->ts_longretry;
-			sc->sc_stats.ast_tx_shortretry += sr;
-			sc->sc_stats.ast_tx_longretry += lr;
-			/*
-			 * Hand the descriptor to the rate control algorithm.
-			 */
-			if ((ts->ts_status & HAL_TXERR_FILT) == 0 &&
-			    (bf->bf_txflags & HAL_TXDESC_NOACK) == 0) {
-				/*
-				 * If frame was ack'd update statistics,
-				 * including the last rx time used to
-				 * workaround phantom bmiss interrupts.
-				 */
-				if (ts->ts_status == 0) {
-					nacked++;
-					sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
-					ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi,
-						ts->ts_rssi);
-				}
-				ath_rate_tx_complete(sc, an, bf);
-			}
-			/*
-			 * Do any tx complete callback.  Note this must
-			 * be done before releasing the node reference.
-			 */
-			if (bf->bf_m->m_flags & M_TXCB)
-				ieee80211_process_callback(ni, bf->bf_m,
-				    (bf->bf_txflags & HAL_TXDESC_NOACK) == 0 ?
-				        ts->ts_status : HAL_TXERR_XRETRY);
-			ieee80211_free_node(ni);
+		ATH_KTR(sc, ATH_KTR_TXCOMP, 5,
+		    "ath_tx_processq: txq=%u, bf=%p, ds=%p, ni=%p, ts_status=0x%08x",
+		    txq->axq_qnum, bf, ds, ni, ts->ts_status);
+		/*
+		 * If unicast frame was ack'd update RSSI,
+		 * including the last rx time used to
+		 * workaround phantom bmiss interrupts.
+		 */
+		if (ni != NULL && ts->ts_status == 0 &&
+		    ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) {
+			nacked++;
+			sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
+			ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi,
+				ts->ts_rssi);
 		}
-		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
-		    BUS_DMASYNC_POSTWRITE);
-		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		ATH_TXQ_UNLOCK(txq);
 
-		m_freem(bf->bf_m);
-		bf->bf_m = NULL;
-		bf->bf_node = NULL;
+		/*
+		 * Update statistics and call completion
+		 */
+		ath_tx_process_buf_completion(sc, txq, ts, bf);
 
-		ATH_TXBUF_LOCK(sc);
-		last = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list);
-		if (last != NULL)
-			last->bf_flags &= ~ATH_BUF_BUSY;
-		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
-		ATH_TXBUF_UNLOCK(sc);
+		/* XXX at this point, bf and ni may be totally invalid */
 	}
 #ifdef IEEE80211_SUPPORT_SUPERG
 	/*
@@ -4206,16 +4224,22 @@
 	if (txq->axq_depth <= 1)
 		ieee80211_ff_flush(ic, txq->axq_ac);
 #endif
+
+	/* Kick the software TXQ scheduler */
+	if (dosched) {
+		ATH_TX_LOCK(sc);
+		ath_txq_sched(sc, txq);
+		ATH_TX_UNLOCK(sc);
+	}
+
+	ATH_KTR(sc, ATH_KTR_TXCOMP, 1,
+	    "ath_tx_processq: txq=%u: done",
+	    txq->axq_qnum);
+
 	return nacked;
 }
 
-static __inline int
-txqactive(struct ath_hal *ah, int qnum)
-{
-	u_int32_t txqs = 1<<qnum;
-	ath_hal_gettxintrtxqs(ah, &txqs);
-	return (txqs & (1<<qnum));
-}
+#define	TXQACTIVE(t, q)		( (t) & (1 << (q)))
 
 /*
  * Deferred processing of transmit interrupt; special-cased
@@ -4226,18 +4250,35 @@
 {
 	struct ath_softc *sc = arg;
 	struct ifnet *ifp = sc->sc_ifp;
+	uint32_t txqs;
 
-	if (txqactive(sc->sc_ah, 0) && ath_tx_processq(sc, &sc->sc_txq[0]))
+	ATH_PCU_LOCK(sc);
+	sc->sc_txproc_cnt++;
+	txqs = sc->sc_txq_active;
+	sc->sc_txq_active &= ~txqs;
+	ATH_PCU_UNLOCK(sc);
+
+	ATH_KTR(sc, ATH_KTR_TXCOMP, 1,
+	    "ath_tx_proc_q0: txqs=0x%08x", txqs);
+
+	if (TXQACTIVE(txqs, 0) && ath_tx_processq(sc, &sc->sc_txq[0], 1))
+		/* XXX why is lastrx updated in tx code? */
 		sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
-	if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum))
-		ath_tx_processq(sc, sc->sc_cabq);
+	if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum))
+		ath_tx_processq(sc, sc->sc_cabq, 1);
+	IF_LOCK(&ifp->if_snd);
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	IF_UNLOCK(&ifp->if_snd);
 	sc->sc_wd_timer = 0;
 
 	if (sc->sc_softled)
 		ath_led_event(sc, sc->sc_txrix);
 
-	ath_start(ifp);
+	ATH_PCU_LOCK(sc);
+	sc->sc_txproc_cnt--;
+	ATH_PCU_UNLOCK(sc);
+
+	ath_tx_kick(sc);
 }
 
 /*
@@ -4250,31 +4291,47 @@
 	struct ath_softc *sc = arg;
 	struct ifnet *ifp = sc->sc_ifp;
 	int nacked;
+	uint32_t txqs;
 
+	ATH_PCU_LOCK(sc);
+	sc->sc_txproc_cnt++;
+	txqs = sc->sc_txq_active;
+	sc->sc_txq_active &= ~txqs;
+	ATH_PCU_UNLOCK(sc);
+
+	ATH_KTR(sc, ATH_KTR_TXCOMP, 1,
+	    "ath_tx_proc_q0123: txqs=0x%08x", txqs);
+
 	/*
 	 * Process each active queue.
 	 */
 	nacked = 0;
-	if (txqactive(sc->sc_ah, 0))
-		nacked += ath_tx_processq(sc, &sc->sc_txq[0]);
-	if (txqactive(sc->sc_ah, 1))
-		nacked += ath_tx_processq(sc, &sc->sc_txq[1]);
-	if (txqactive(sc->sc_ah, 2))
-		nacked += ath_tx_processq(sc, &sc->sc_txq[2]);
-	if (txqactive(sc->sc_ah, 3))
-		nacked += ath_tx_processq(sc, &sc->sc_txq[3]);
-	if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum))
-		ath_tx_processq(sc, sc->sc_cabq);
+	if (TXQACTIVE(txqs, 0))
+		nacked += ath_tx_processq(sc, &sc->sc_txq[0], 1);
+	if (TXQACTIVE(txqs, 1))
+		nacked += ath_tx_processq(sc, &sc->sc_txq[1], 1);
+	if (TXQACTIVE(txqs, 2))
+		nacked += ath_tx_processq(sc, &sc->sc_txq[2], 1);
+	if (TXQACTIVE(txqs, 3))
+		nacked += ath_tx_processq(sc, &sc->sc_txq[3], 1);
+	if (TXQACTIVE(txqs, sc->sc_cabq->axq_qnum))
+		ath_tx_processq(sc, sc->sc_cabq, 1);
 	if (nacked)
 		sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
 
+	IF_LOCK(&ifp->if_snd);
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	IF_UNLOCK(&ifp->if_snd);
 	sc->sc_wd_timer = 0;
 
 	if (sc->sc_softled)
 		ath_led_event(sc, sc->sc_txrix);
 
-	ath_start(ifp);
+	ATH_PCU_LOCK(sc);
+	sc->sc_txproc_cnt--;
+	ATH_PCU_UNLOCK(sc);
+
+	ath_tx_kick(sc);
 }
 
 /*
@@ -4286,33 +4343,310 @@
 	struct ath_softc *sc = arg;
 	struct ifnet *ifp = sc->sc_ifp;
 	int i, nacked;
+	uint32_t txqs;
 
+	ATH_PCU_LOCK(sc);
+	sc->sc_txproc_cnt++;
+	txqs = sc->sc_txq_active;
+	sc->sc_txq_active &= ~txqs;
+	ATH_PCU_UNLOCK(sc);
+
+	ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc: txqs=0x%08x", txqs);
+
 	/*
 	 * Process each active queue.
 	 */
 	nacked = 0;
 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
-		if (ATH_TXQ_SETUP(sc, i) && txqactive(sc->sc_ah, i))
-			nacked += ath_tx_processq(sc, &sc->sc_txq[i]);
+		if (ATH_TXQ_SETUP(sc, i) && TXQACTIVE(txqs, i))
+			nacked += ath_tx_processq(sc, &sc->sc_txq[i], 1);
 	if (nacked)
 		sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
 
+	/* XXX check this inside of IF_LOCK? */
+	IF_LOCK(&ifp->if_snd);
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	IF_UNLOCK(&ifp->if_snd);
 	sc->sc_wd_timer = 0;
 
 	if (sc->sc_softled)
 		ath_led_event(sc, sc->sc_txrix);
 
-	ath_start(ifp);
+	ATH_PCU_LOCK(sc);
+	sc->sc_txproc_cnt--;
+	ATH_PCU_UNLOCK(sc);
+
+	ath_tx_kick(sc);
 }
+#undef	TXQACTIVE
 
+/*
+ * Deferred processing of TXQ rescheduling.
+ */
 static void
+ath_txq_sched_tasklet(void *arg, int npending)
+{
+	struct ath_softc *sc = arg;
+	int i;
+
+	/* XXX is skipping ok? */
+	ATH_PCU_LOCK(sc);
+#if 0
+	if (sc->sc_inreset_cnt > 0) {
+		device_printf(sc->sc_dev,
+		    "%s: sc_inreset_cnt > 0; skipping\n", __func__);
+		ATH_PCU_UNLOCK(sc);
+		return;
+	}
+#endif
+	sc->sc_txproc_cnt++;
+	ATH_PCU_UNLOCK(sc);
+
+	ATH_TX_LOCK(sc);
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+		if (ATH_TXQ_SETUP(sc, i)) {
+			ath_txq_sched(sc, &sc->sc_txq[i]);
+		}
+	}
+	ATH_TX_UNLOCK(sc);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_txproc_cnt--;
+	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;
+		}
+	}
+}
+
+/*
+ * Free the holding buffer if it exists
+ */
+void
+ath_txq_freeholdingbuf(struct ath_softc *sc, struct ath_txq *txq)
+{
+	ATH_TXBUF_UNLOCK_ASSERT(sc);
+	ATH_TXQ_LOCK_ASSERT(txq);
+
+	if (txq->axq_holdingbf == NULL)
+		return;
+
+	txq->axq_holdingbf->bf_flags &= ~ATH_BUF_BUSY;
+
+	ATH_TXBUF_LOCK(sc);
+	ath_returnbuf_tail(sc, txq->axq_holdingbf);
+	ATH_TXBUF_UNLOCK(sc);
+
+	txq->axq_holdingbf = NULL;
+}
+
+/*
+ * Add this buffer to the holding queue, freeing the previous
+ * one if it exists.
+ */
+static void
+ath_txq_addholdingbuf(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ath_txq *txq;
+
+	txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue];
+
+	ATH_TXBUF_UNLOCK_ASSERT(sc);
+	ATH_TXQ_LOCK_ASSERT(txq);
+
+	/* XXX assert ATH_BUF_BUSY is set */
+
+	/* XXX assert the tx queue is under the max number */
+	if (bf->bf_state.bfs_tx_queue > HAL_NUM_TX_QUEUES) {
+		device_printf(sc->sc_dev, "%s: bf=%p: invalid tx queue (%d)\n",
+		    __func__,
+		    bf,
+		    bf->bf_state.bfs_tx_queue);
+		bf->bf_flags &= ~ATH_BUF_BUSY;
+		ath_returnbuf_tail(sc, bf);
+		return;
+	}
+	ath_txq_freeholdingbuf(sc, txq);
+	txq->axq_holdingbf = bf;
+}
+
+/*
+ * Return a buffer to the pool and update the 'busy' flag on the
+ * previous 'tail' entry.
+ *
+ * This _must_ only be called when the buffer is involved in a completed
+ * TX. The logic is that if it was part of an active TX, the previous
+ * buffer on the list is now not involved in a halted TX DMA queue, waiting
+ * for restart (eg for TDMA.)
+ *
+ * The caller must free the mbuf and recycle the node reference.
+ *
+ * XXX This method of handling busy / holding buffers is insanely stupid.
+ * It requires bf_state.bfs_tx_queue to be correctly assigned.  It would
+ * be much nicer if buffers in the processq() methods would instead be
+ * always completed there (pushed onto a txq or ath_bufhead) so we knew
+ * exactly what hardware queue they came from in the first place.
+ */
+void
+ath_freebuf(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ath_txq *txq;
+
+	txq = &sc->sc_txq[bf->bf_state.bfs_tx_queue];
+
+	KASSERT((bf->bf_node == NULL), ("%s: bf->bf_node != NULL\n", __func__));
+	KASSERT((bf->bf_m == NULL), ("%s: bf->bf_m != NULL\n", __func__));
+
+	/*
+	 * If this buffer is busy, push it onto the holding queue.
+	 */
+	if (bf->bf_flags & ATH_BUF_BUSY) {
+		ATH_TXQ_LOCK(txq);
+		ath_txq_addholdingbuf(sc, bf);
+		ATH_TXQ_UNLOCK(txq);
+		return;
+	}
+
+	/*
+	 * Not a busy buffer, so free normally
+	 */
+	ATH_TXBUF_LOCK(sc);
+	ath_returnbuf_tail(sc, bf);
+	ATH_TXBUF_UNLOCK(sc);
+}
+
+/*
+ * This is currently used by ath_tx_draintxq() and
+ * ath_tx_tid_free_pkts().
+ *
+ * It recycles a single ath_buf.
+ */
+void
+ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct mbuf *m0 = bf->bf_m;
+
+	/*
+	 * Make sure that we only sync/unload if there's an mbuf.
+	 * If not (eg we cloned a buffer), the unload will have already
+	 * occured.
+	 */
+	if (bf->bf_m != NULL) {
+		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+		    BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+	}
+
+	bf->bf_node = NULL;
+	bf->bf_m = NULL;
+
+	/* Free the buffer, it's not needed any longer */
+	ath_freebuf(sc, bf);
+
+	/* Pass the buffer back to net80211 - completing it */
+	ieee80211_tx_complete(ni, m0, status);
+}
+
+static struct ath_buf *
+ath_tx_draintxq_get_one(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_buf *bf;
+
+	ATH_TXQ_LOCK_ASSERT(txq);
+
+	/*
+	 * Drain the FIFO queue first, then if it's
+	 * empty, move to the normal frame queue.
+	 */
+	bf = TAILQ_FIRST(&txq->fifo.axq_q);
+	if (bf != NULL) {
+		/*
+		 * Is it the last buffer in this set?
+		 * Decrement the FIFO counter.
+		 */
+		if (bf->bf_flags & ATH_BUF_FIFOEND) {
+			if (txq->axq_fifo_depth == 0) {
+				device_printf(sc->sc_dev,
+				    "%s: Q%d: fifo_depth=0, fifo.axq_depth=%d?\n",
+				    __func__,
+				    txq->axq_qnum,
+				    txq->fifo.axq_depth);
+			} else
+				txq->axq_fifo_depth--;
+		}
+		ATH_TXQ_REMOVE(&txq->fifo, bf, bf_list);
+		return (bf);
+	}
+
+	/*
+	 * Debugging!
+	 */
+	if (txq->axq_fifo_depth != 0 || txq->fifo.axq_depth != 0) {
+		device_printf(sc->sc_dev,
+		    "%s: Q%d: fifo_depth=%d, fifo.axq_depth=%d\n",
+		    __func__,
+		    txq->axq_qnum,
+		    txq->axq_fifo_depth,
+		    txq->fifo.axq_depth);
+	}
+
+	/*
+	 * Now drain the pending queue.
+	 */
+	bf = TAILQ_FIRST(&txq->axq_q);
+	if (bf == NULL) {
+		txq->axq_link = NULL;
+		return (NULL);
+	}
+	ATH_TXQ_REMOVE(txq, bf, bf_list);
+	return (bf);
+}
+
+void
 ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
 {
 #ifdef ATH_DEBUG
 	struct ath_hal *ah = sc->sc_ah;
 #endif
-	struct ieee80211_node *ni;
 	struct ath_buf *bf;
 	u_int ix;
 
@@ -4320,51 +4654,65 @@
 	 * NB: this assumes output has been stopped and
 	 *     we do not need to block ath_tx_proc
 	 */
-	ATH_TXBUF_LOCK(sc);
-	bf = STAILQ_LAST(&sc->sc_txbuf, ath_buf, bf_list);
-	if (bf != NULL)
-		bf->bf_flags &= ~ATH_BUF_BUSY;
-	ATH_TXBUF_UNLOCK(sc);
 	for (ix = 0;; ix++) {
 		ATH_TXQ_LOCK(txq);
-		bf = STAILQ_FIRST(&txq->axq_q);
+		bf = ath_tx_draintxq_get_one(sc, txq);
 		if (bf == NULL) {
-			txq->axq_link = NULL;
 			ATH_TXQ_UNLOCK(txq);
 			break;
 		}
-		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
-		ATH_TXQ_UNLOCK(txq);
+		if (bf->bf_state.bfs_aggr)
+			txq->axq_aggr_depth--;
 #ifdef ATH_DEBUG
 		if (sc->sc_debug & ATH_DEBUG_RESET) {
 			struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+			int status = 0;
 
-			ath_printtxbuf(sc, bf, txq->axq_qnum, ix,
-				ath_hal_txprocdesc(ah, bf->bf_desc,
+			/*
+			 * EDMA operation has a TX completion FIFO
+			 * separate from the TX descriptor, so this
+			 * method of checking the "completion" status
+			 * is wrong.
+			 */
+			if (! sc->sc_isedma) {
+				status = (ath_hal_txprocdesc(ah,
+				    bf->bf_lastds,
 				    &bf->bf_status.ds_txstat) == HAL_OK);
+			}
+			ath_printtxbuf(sc, bf, txq->axq_qnum, ix, status);
 			ieee80211_dump_pkt(ic, mtod(bf->bf_m, const uint8_t *),
 			    bf->bf_m->m_len, 0, -1);
 		}
 #endif /* ATH_DEBUG */
-		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-		ni = bf->bf_node;
-		bf->bf_node = NULL;
-		if (ni != NULL) {
-			/*
-			 * Do any callback and reclaim the node reference.
-			 */
-			if (bf->bf_m->m_flags & M_TXCB)
-				ieee80211_process_callback(ni, bf->bf_m, -1);
-			ieee80211_free_node(ni);
-		}
-		m_freem(bf->bf_m);
-		bf->bf_m = NULL;
+		/*
+		 * Since we're now doing magic in the completion
+		 * functions, we -must- call it for aggregation
+		 * destinations or BAW tracking will get upset.
+		 */
+		/*
+		 * Clear ATH_BUF_BUSY; the completion handler
+		 * will free the buffer.
+		 */
+		ATH_TXQ_UNLOCK(txq);
 		bf->bf_flags &= ~ATH_BUF_BUSY;
+		if (bf->bf_comp)
+			bf->bf_comp(sc, bf, 1);
+		else
+			ath_tx_default_comp(sc, bf, 1);
+	}
 
-		ATH_TXBUF_LOCK(sc);
-		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
-		ATH_TXBUF_UNLOCK(sc);
-	}
+	/*
+	 * Free the holding buffer if it exists
+	 */
+	ATH_TXQ_LOCK(txq);
+	ath_txq_freeholdingbuf(sc, txq);
+	ATH_TXQ_UNLOCK(txq);
+
+	/*
+	 * Drain software queued frames which are on
+	 * active TIDs.
+	 */
+	ath_tx_txq_drain(sc, txq);
 }
 
 static void
@@ -4372,24 +4720,42 @@
 {
 	struct ath_hal *ah = sc->sc_ah;
 
-	DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n",
-	    __func__, txq->axq_qnum,
+	ATH_TXQ_LOCK_ASSERT(txq);
+
+	DPRINTF(sc, ATH_DEBUG_RESET,
+	    "%s: tx queue [%u] %p, active=%d, hwpending=%d, flags 0x%08x, "
+	    "link %p, holdingbf=%p\n",
+	    __func__,
+	    txq->axq_qnum,
 	    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum),
-	    txq->axq_link);
+	    (int) (!! ath_hal_txqenabled(ah, txq->axq_qnum)),
+	    (int) ath_hal_numtxpending(ah, txq->axq_qnum),
+	    txq->axq_flags,
+	    txq->axq_link,
+	    txq->axq_holdingbf);
+
 	(void) ath_hal_stoptxdma(ah, txq->axq_qnum);
+	/* We've stopped TX DMA, so mark this as stopped. */
+	txq->axq_flags &= ~ATH_TXQ_PUTRUNNING;
+
+#ifdef	ATH_DEBUG
+	if ((sc->sc_debug & ATH_DEBUG_RESET)
+	    && (txq->axq_holdingbf != NULL)) {
+		ath_printtxbuf(sc, txq->axq_holdingbf, txq->axq_qnum, 0, 0);
+	}
+#endif
 }
 
-/*
- * Drain the transmit queues and reclaim resources.
- */
-static void
-ath_draintxq(struct ath_softc *sc)
+int
+ath_stoptxdma(struct ath_softc *sc)
 {
 	struct ath_hal *ah = sc->sc_ah;
-	struct ifnet *ifp = sc->sc_ifp;
 	int i;
 
 	/* XXX return value */
+	if (sc->sc_invalid)
+		return 0;
+
 	if (!sc->sc_invalid) {
 		/* don't touch the hardware if marked invalid */
 		DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n",
@@ -4396,100 +4762,118 @@
 		    __func__, sc->sc_bhalq,
 		    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq),
 		    NULL);
+
+		/* stop the beacon queue */
 		(void) ath_hal_stoptxdma(ah, sc->sc_bhalq);
-		for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
-			if (ATH_TXQ_SETUP(sc, i))
+
+		/* Stop the data queues */
+		for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+			if (ATH_TXQ_SETUP(sc, i)) {
+				ATH_TXQ_LOCK(&sc->sc_txq[i]);
 				ath_tx_stopdma(sc, &sc->sc_txq[i]);
-	}
-	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
-		if (ATH_TXQ_SETUP(sc, i))
-			ath_tx_draintxq(sc, &sc->sc_txq[i]);
-#ifdef ATH_DEBUG
-	if (sc->sc_debug & ATH_DEBUG_RESET) {
-		struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf);
-		if (bf != NULL && bf->bf_m != NULL) {
-			ath_printtxbuf(sc, bf, sc->sc_bhalq, 0,
-				ath_hal_txprocdesc(ah, bf->bf_desc,
-				    &bf->bf_status.ds_txstat) == HAL_OK);
-			ieee80211_dump_pkt(ifp->if_l2com,
-			    mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len,
-			    0, -1);
+				ATH_TXQ_UNLOCK(&sc->sc_txq[i]);
+			}
 		}
 	}
-#endif /* ATH_DEBUG */
-	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-	sc->sc_wd_timer = 0;
+
+	return 1;
 }
 
-/*
- * Disable the receive h/w in preparation for a reset.
- */
-static void
-ath_stoprecv(struct ath_softc *sc)
+#ifdef	ATH_DEBUG
+void
+ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq)
 {
-#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;
+	struct ath_buf *bf;
+	int i = 0;
 
-	ath_hal_stoppcurecv(ah);	/* disable PCU */
-	ath_hal_setrxfilter(ah, 0);	/* clear recv filter */
-	ath_hal_stopdmarecv(ah);	/* disable DMA engine */
-	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;
+	if (! (sc->sc_debug & ATH_DEBUG_RESET))
+		return;
 
-		printf("%s: rx queue %p, link %p\n", __func__,
-			(caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink);
-		ix = 0;
-		STAILQ_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++;
-		}
+	device_printf(sc->sc_dev, "%s: Q%d: begin\n",
+	    __func__, txq->axq_qnum);
+	TAILQ_FOREACH(bf, &txq->axq_q, bf_list) {
+		ath_printtxbuf(sc, bf, txq->axq_qnum, i,
+			ath_hal_txprocdesc(ah, bf->bf_lastds,
+			    &bf->bf_status.ds_txstat) == HAL_OK);
+		i++;
 	}
-#endif
-	if (sc->sc_rxpending != NULL) {
-		m_freem(sc->sc_rxpending);
-		sc->sc_rxpending = NULL;
-	}
-	sc->sc_rxlink = NULL;		/* just in case */
-#undef PA2DESC
+	device_printf(sc->sc_dev, "%s: Q%d: end\n",
+	    __func__, txq->axq_qnum);
 }
+#endif /* ATH_DEBUG */
 
 /*
- * Enable the receive h/w following a reset.
+ * Drain the transmit queues and reclaim resources.
  */
-static int
-ath_startrecv(struct ath_softc *sc)
+void
+ath_legacy_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type)
 {
 	struct ath_hal *ah = sc->sc_ah;
-	struct ath_buf *bf;
+	struct ifnet *ifp = sc->sc_ifp;
+	int i;
+	struct ath_buf *bf_last;
 
-	sc->sc_rxlink = NULL;
-	sc->sc_rxpending = NULL;
-	STAILQ_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;
+	(void) ath_stoptxdma(sc);
+
+	/*
+	 * Dump the queue contents
+	 */
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+		/*
+		 * XXX TODO: should we just handle the completed TX frames
+		 * here, whether or not the reset is a full one or not?
+		 */
+		if (ATH_TXQ_SETUP(sc, i)) {
+#ifdef	ATH_DEBUG
+			if (sc->sc_debug & ATH_DEBUG_RESET)
+				ath_tx_dump(sc, &sc->sc_txq[i]);
+#endif	/* ATH_DEBUG */
+			if (reset_type == ATH_RESET_NOLOSS) {
+				ath_tx_processq(sc, &sc->sc_txq[i], 0);
+				ATH_TXQ_LOCK(&sc->sc_txq[i]);
+				/*
+				 * Free the holding buffer; DMA is now
+				 * stopped.
+				 */
+				ath_txq_freeholdingbuf(sc, &sc->sc_txq[i]);
+				/*
+				 * Setup the link pointer to be the
+				 * _last_ buffer/descriptor in the list.
+				 * If there's nothing in the list, set it
+				 * to NULL.
+				 */
+				bf_last = ATH_TXQ_LAST(&sc->sc_txq[i],
+				    axq_q_s);
+				if (bf_last != NULL) {
+					ath_hal_gettxdesclinkptr(ah,
+					    bf_last->bf_lastds,
+					    &sc->sc_txq[i].axq_link);
+				} else {
+					sc->sc_txq[i].axq_link = NULL;
+				}
+				ATH_TXQ_UNLOCK(&sc->sc_txq[i]);
+			} else
+				ath_tx_draintxq(sc, &sc->sc_txq[i]);
 		}
 	}
-
-	bf = STAILQ_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;
+#ifdef ATH_DEBUG
+	if (sc->sc_debug & ATH_DEBUG_RESET) {
+		struct ath_buf *bf = TAILQ_FIRST(&sc->sc_bbuf);
+		if (bf != NULL && bf->bf_m != NULL) {
+			ath_printtxbuf(sc, bf, sc->sc_bhalq, 0,
+				ath_hal_txprocdesc(ah, bf->bf_lastds,
+				    &bf->bf_status.ds_txstat) == HAL_OK);
+			ieee80211_dump_pkt(ifp->if_l2com,
+			    mtod(bf->bf_m, const uint8_t *), bf->bf_m->m_len,
+			    0, -1);
+		}
+	}
+#endif /* ATH_DEBUG */
+	IF_LOCK(&ifp->if_snd);
+	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	IF_UNLOCK(&ifp->if_snd);
+	sc->sc_wd_timer = 0;
 }
 
 /*
@@ -4522,7 +4906,30 @@
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ieee80211com *ic = ifp->if_l2com;
 	struct ath_hal *ah = sc->sc_ah;
+	int ret = 0;
 
+	/* Treat this as an interface reset */
+	ATH_PCU_UNLOCK_ASSERT(sc);
+	ATH_UNLOCK_ASSERT(sc);
+
+	/* (Try to) stop TX/RX from occuring */
+	taskqueue_block(sc->sc_tq);
+
+	ATH_PCU_LOCK(sc);
+
+	/* Stop new RX/TX/interrupt completion */
+	if (ath_reset_grablock(sc, 1) == 0) {
+		device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n",
+		    __func__);
+	}
+
+	ath_hal_intrset(ah, 0);
+
+	/* Stop pending RX/TX completion */
+	ath_txrx_stop_locked(sc);
+
+	ATH_PCU_UNLOCK(sc);
+
 	DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, flags 0x%x)\n",
 	    __func__, ieee80211_chan2ieee(ic, chan),
 	    chan->ic_freq, chan->ic_flags);
@@ -4534,28 +4941,62 @@
 		 * hardware at the new frequency, and then re-enable
 		 * the relevant bits of the h/w.
 		 */
+#if 0
 		ath_hal_intrset(ah, 0);		/* disable interrupts */
-		ath_draintxq(sc);		/* clear pending tx frames */
-		ath_stoprecv(sc);		/* turn off frame recv */
+#endif
+		ath_stoprecv(sc, 1);		/* turn off frame recv */
+		/*
+		 * First, handle completed TX/RX frames.
+		 */
+		ath_rx_flush(sc);
+		ath_draintxq(sc, ATH_RESET_NOLOSS);
+		/*
+		 * Next, flush the non-scheduled frames.
+		 */
+		ath_draintxq(sc, ATH_RESET_FULL);	/* clear pending tx frames */
+
+		ath_update_chainmasks(sc, chan);
+		ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask,
+		    sc->sc_cur_rxchainmask);
 		if (!ath_hal_reset(ah, sc->sc_opmode, chan, AH_TRUE, &status)) {
 			if_printf(ifp, "%s: unable to reset "
 			    "channel %u (%u MHz, flags 0x%x), hal status %u\n",
 			    __func__, ieee80211_chan2ieee(ic, chan),
 			    chan->ic_freq, chan->ic_flags, status);
-			return EIO;
+			ret = EIO;
+			goto finish;
 		}
 		sc->sc_diversity = ath_hal_getdiversity(ah);
 
 		/* Let DFS at it in case it's a DFS channel */
-		ath_dfs_radar_enable(sc, ic->ic_curchan);
+		ath_dfs_radar_enable(sc, chan);
 
+		/* Let spectral at in case spectral is enabled */
+		ath_spectral_enable(sc, chan);
+
 		/*
+		 * Let bluetooth coexistence at in case it's needed for this
+		 * channel
+		 */
+		ath_btcoex_enable(sc, ic->ic_curchan);
+
+		/*
+		 * If we're doing TDMA, enforce the TXOP limitation for chips
+		 * that support it.
+		 */
+		if (sc->sc_hasenforcetxop && sc->sc_tdma)
+			ath_hal_setenforcetxop(sc->sc_ah, 1);
+		else
+			ath_hal_setenforcetxop(sc->sc_ah, 0);
+
+		/*
 		 * Re-enable rx framework.
 		 */
 		if (ath_startrecv(sc) != 0) {
 			if_printf(ifp, "%s: unable to restart recv logic\n",
 			    __func__);
-			return EIO;
+			ret = EIO;
+			goto finish;
 		}
 
 		/*
@@ -4580,9 +5021,25 @@
 		/*
 		 * Re-enable interrupts.
 		 */
+#if 0
 		ath_hal_intrset(ah, sc->sc_imask);
+#endif
 	}
-	return 0;
+
+finish:
+	ATH_PCU_LOCK(sc);
+	sc->sc_inreset_cnt--;
+	/* XXX only do this if sc_inreset_cnt == 0? */
+	ath_hal_intrset(ah, sc->sc_imask);
+	ATH_PCU_UNLOCK(sc);
+
+	IF_LOCK(&ifp->if_snd);
+	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	IF_UNLOCK(&ifp->if_snd);
+	ath_txrx_start(sc);
+	/* XXX ath_start? */
+
+	return ret;
 }
 
 /*
@@ -4596,7 +5053,7 @@
 	struct ath_hal *ah = sc->sc_ah;
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ieee80211com *ic = ifp->if_l2com;
-	HAL_BOOL longCal, isCalDone = AH_FALSE;
+	HAL_BOOL longCal, isCalDone = AH_TRUE;
 	HAL_BOOL aniCal, shortCal = AH_FALSE;
 	int nextcal;
 
@@ -4625,7 +5082,11 @@
 			DPRINTF(sc, ATH_DEBUG_CALIBRATE,
 				"%s: rfgain change\n", __func__);
 			sc->sc_stats.ast_per_rfgain++;
-			ath_reset(ifp);
+			sc->sc_resetcal = 0;
+			sc->sc_doresetcal = AH_TRUE;
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask);
+			callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc);
+			return;
 		}
 		/*
 		 * If this long cal is after an idle period, then
@@ -4642,6 +5103,7 @@
 
 	/* Only call if we're doing a short/long cal, not for ANI calibration */
 	if (shortCal || longCal) {
+		isCalDone = AH_FALSE;
 		if (ath_hal_calibrateN(ah, sc->sc_curchan, longCal, &isCalDone)) {
 			if (longCal) {
 				/*
@@ -4707,11 +5169,16 @@
 
 	/* XXX calibration timer? */
 
+	ATH_LOCK(sc);
 	sc->sc_scanning = 1;
 	sc->sc_syncbeacon = 0;
 	rfilt = ath_calcrxfilter(sc);
+	ATH_UNLOCK(sc);
+
+	ATH_PCU_LOCK(sc);
 	ath_hal_setrxfilter(ah, rfilt);
 	ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0);
+	ATH_PCU_UNLOCK(sc);
 
 	DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n",
 		 __func__, rfilt, ether_sprintf(ifp->if_broadcastaddr));
@@ -4725,12 +5192,17 @@
 	struct ath_hal *ah = sc->sc_ah;
 	u_int32_t rfilt;
 
+	ATH_LOCK(sc);
 	sc->sc_scanning = 0;
 	rfilt = ath_calcrxfilter(sc);
+	ATH_UNLOCK(sc);
+
+	ATH_PCU_LOCK(sc);
 	ath_hal_setrxfilter(ah, rfilt);
 	ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid);
 
 	ath_hal_process_noisefloor(ah);
+	ATH_PCU_UNLOCK(sc);
 
 	DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n",
 		 __func__, rfilt, ether_sprintf(sc->sc_curbssid),
@@ -4737,7 +5209,34 @@
 		 sc->sc_curaid);
 }
 
+#ifdef	ATH_ENABLE_11N
+/*
+ * For now, just do a channel change.
+ *
+ * Later, we'll go through the hard slog of suspending tx/rx, changing rate
+ * control state and resetting the hardware without dropping frames out
+ * of the queue.
+ *
+ * The unfortunate trouble here is making absolutely sure that the
+ * channel width change has propagated enough so the hardware
+ * absolutely isn't handed bogus frames for it's current operating
+ * mode. (Eg, 40MHz frames in 20MHz mode.) Since TX and RX can and
+ * does occur in parallel, we need to make certain we've blocked
+ * any further ongoing TX (and RX, that can cause raw TX)
+ * before we do this.
+ */
 static void
+ath_update_chw(struct ieee80211com *ic)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ath_softc *sc = ifp->if_softc;
+
+	DPRINTF(sc, ATH_DEBUG_STATE, "%s: called\n", __func__);
+	ath_set_channel(ic);
+}
+#endif	/* ATH_ENABLE_11N */
+
+static void
 ath_set_channel(struct ieee80211com *ic)
 {
 	struct ifnet *ifp = ic->ic_ifp;
@@ -4750,8 +5249,10 @@
 	 * beacon timers.  Note that since we only hear beacons in
 	 * sta/ibss mode this has no effect in other operating modes.
 	 */
+	ATH_LOCK(sc);
 	if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan)
 		sc->sc_syncbeacon = 1;
+	ATH_UNLOCK(sc);
 }
 
 /*
@@ -4783,6 +5284,7 @@
 	int i, error, stamode;
 	u_int32_t rfilt;
 	int csa_run_transition = 0;
+
 	static const HAL_LED_STATE leds[] = {
 	    HAL_LED_INIT,	/* IEEE80211_S_INIT */
 	    HAL_LED_SCAN,	/* IEEE80211_S_SCAN */
@@ -4798,6 +5300,15 @@
 		ieee80211_state_name[vap->iv_state],
 		ieee80211_state_name[nstate]);
 
+	/*
+	 * net80211 _should_ have the comlock asserted at this point.
+	 * There are some comments around the calls to vap->iv_newstate
+	 * which indicate that it (newstate) may end up dropping the
+	 * lock.  This and the subsequent lock assert check after newstate
+	 * are an attempt to catch these and figure out how/why.
+	 */
+	IEEE80211_LOCK_ASSERT(ic);
+
 	if (vap->iv_state == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN)
 		csa_run_transition = 1;
 
@@ -4818,7 +5329,7 @@
 		taskqueue_unblock(sc->sc_tq);
 	}
 
-	ni = vap->iv_bss;
+	ni = ieee80211_ref_node(vap->iv_bss);
 	rfilt = ath_calcrxfilter(sc);
 	stamode = (vap->iv_opmode == IEEE80211_M_STA ||
 		   vap->iv_opmode == IEEE80211_M_AHDEMO ||
@@ -4847,9 +5358,16 @@
 	if (error != 0)
 		goto bad;
 
+	/*
+	 * See above: ensure av_newstate() doesn't drop the lock
+	 * on us.
+	 */
+	IEEE80211_LOCK_ASSERT(ic);
+
 	if (nstate == IEEE80211_S_RUN) {
 		/* NB: collect bss node again, it may have changed */
-		ni = vap->iv_bss;
+		ieee80211_free_node(ni);
+		ni = ieee80211_ref_node(vap->iv_bss);
 
 		DPRINTF(sc, ATH_DEBUG_STATE,
 		    "%s(RUN): iv_flags 0x%08x bintvl %d bssid %s "
@@ -4911,10 +5429,32 @@
 			 * force a beacon update so we pick up a lack of
 			 * beacons from an AP in CAC and thus force a
 			 * scan.
+			 *
+			 * And, there's also corner cases here where
+			 * after a scan, the AP may have disappeared.
+			 * In that case, we may not receive an actual
+			 * beacon to update the beacon timer and thus we
+			 * won't get notified of the missing beacons.
 			 */
 			sc->sc_syncbeacon = 1;
+#if 0
 			if (csa_run_transition)
+#endif
 				ath_beacon_config(sc, vap);
+
+			/*
+			 * PR: kern/175227
+			 *
+			 * Reconfigure beacons during reset; as otherwise
+			 * we won't get the beacon timers reprogrammed
+			 * after a reset and thus we won't pick up a
+			 * beacon miss interrupt.
+			 *
+			 * Hopefully we'll see a beacon before the BMISS
+			 * timer fires (too often), leading to a STA
+			 * disassociation.
+			 */
+			sc->sc_beacons = 1;
 			break;
 		case IEEE80211_M_MONITOR:
 			/*
@@ -4972,6 +5512,7 @@
 #endif
 	}
 bad:
+	ieee80211_free_node(ni);
 	return error;
 }
 
@@ -4990,6 +5531,7 @@
 	struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
 	ieee80211_keyix keyix, rxkeyix;
 
+	/* XXX should take a locked ref to vap->iv_bss */
 	if (!ath_key_alloc(vap, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
 		/*
 		 * Key cache is full; we'll fall back to doing
@@ -5005,7 +5547,7 @@
 		ni->ni_ucastkey.wk_flags |= IEEE80211_KEY_DEVKEY;
 		IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr);
 		/* NB: this will create a pass-thru key entry */
-		ath_keyset(sc, &ni->ni_ucastkey, vap->iv_bss);
+		ath_keyset(sc, vap, &ni->ni_ucastkey, vap->iv_bss);
 	}
 }
 
@@ -5026,10 +5568,36 @@
 	an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate);
 
 	ath_rate_newassoc(sc, an, isnew);
+
 	if (isnew &&
 	    (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey &&
 	    ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
 		ath_setup_stationkey(ni);
+
+	/*
+	 * If we're reassociating, make sure that any paused queues
+	 * get unpaused.
+	 *
+	 * Now, we may hvae frames in the hardware queue for this node.
+	 * So if we are reassociating and there are frames in the queue,
+	 * we need to go through the cleanup path to ensure that they're
+	 * marked as non-aggregate.
+	 */
+	if (! isnew) {
+		DPRINTF(sc, ATH_DEBUG_NODE,
+		    "%s: %6D: reassoc; is_powersave=%d\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":",
+		    an->an_is_powersave);
+
+		/* XXX for now, we can't hold the lock across assoc */
+		ath_tx_node_reassoc(sc, an);
+
+		/* XXX for now, we can't hold the lock across wakeup */
+		if (an->an_is_powersave)
+			ath_tx_node_wakeup(sc, an);
+	}
 }
 
 static int
@@ -5110,49 +5678,6 @@
 	return 0;
 }
 
-static void
-ath_led_done(void *arg)
-{
-	struct ath_softc *sc = arg;
-
-	sc->sc_blinking = 0;
-}
-
-/*
- * Turn the LED off: flip the pin and then set a timer so no
- * update will happen for the specified duration.
- */
-static void
-ath_led_off(void *arg)
-{
-	struct ath_softc *sc = arg;
-
-	ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
-	callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc);
-}
-
-/*
- * Blink the LED according to the specified on/off times.
- */
-static void
-ath_led_blink(struct ath_softc *sc, int on, int off)
-{
-	DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off);
-	ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon);
-	sc->sc_blinking = 1;
-	sc->sc_ledoff = off;
-	callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc);
-}
-
-static void
-ath_led_event(struct ath_softc *sc, int rix)
-{
-	sc->sc_ledevent = ticks;	/* time of last event */
-	if (sc->sc_blinking)		/* don't interrupt active blink */
-		return;
-	ath_led_blink(sc, sc->sc_hwmap[rix].ledon, sc->sc_hwmap[rix].ledoff);
-}
-
 static int
 ath_rate_setup(struct ath_softc *sc, u_int mode)
 {
@@ -5280,6 +5805,7 @@
 ath_watchdog(void *arg)
 {
 	struct ath_softc *sc = arg;
+	int do_reset = 0;
 
 	if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) {
 		struct ifnet *ifp = sc->sc_ifp;
@@ -5291,13 +5817,61 @@
 			    hangs & 0xff ? "bb" : "mac", hangs);
 		} else
 			if_printf(ifp, "device timeout\n");
-		ath_reset(ifp);
+		do_reset = 1;
 		ifp->if_oerrors++;
 		sc->sc_stats.ast_watchdog++;
 	}
+
+	/*
+	 * We can't hold the lock across the ath_reset() call.
+	 *
+	 * And since this routine can't hold a lock and sleep,
+	 * do the reset deferred.
+	 */
+	if (do_reset) {
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask);
+	}
+
 	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
@@ -5337,7 +5911,7 @@
 		 * pointer for us to use below in reclaiming the buffer;
 		 * may want to be more defensive.
 		 */
-		outdata = malloc(outsize, M_TEMP, M_NOWAIT);
+		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
 		if (outdata == NULL) {
 			error = ENOMEM;
 			goto bad;
@@ -5425,10 +5999,18 @@
 			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)
+		if (error == 0) {
 			memset(&sc->sc_stats, 0, sizeof(sc->sc_stats));
+			memset(&sc->sc_aggr_stats, 0,
+			    sizeof(sc->sc_aggr_stats));
+			memset(&sc->sc_intr_stats, 0,
+			    sizeof(sc->sc_intr_stats));
+		}
 		break;
 #ifdef ATH_DIAGAPI
 	case SIOCGATHDIAG:
@@ -5438,6 +6020,12 @@
 		error = ath_ioctl_phyerr(sc,(struct ath_diag*) ifr);
 		break;
 #endif
+	case SIOCGATHSPECTRAL:
+		error = ath_ioctl_spectral(sc,(struct ath_diag*) ifr);
+		break;
+	case SIOCGATHNODERATESTATS:
+		error = ath_ioctl_ratestats(sc, (struct ath_rateioctl *) ifr);
+		break;
 	case SIOCGIFADDR:
 		error = ether_ioctl(ifp, cmd, data);
 		break;
@@ -5461,6 +6049,8 @@
 	if_printf(ifp, "AR%s mac %d.%d RF%s phy %d.%d\n",
 		ath_hal_mac_name(ah), ah->ah_macVersion, ah->ah_macRev,
 		ath_hal_rf_name(ah), ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf);
+	if_printf(ifp, "2GHz radio: 0x%.4x; 5GHz radio: 0x%.4x\n",
+		ah->ah_analog2GhzRev, ah->ah_analog5GhzRev);
 	if (bootverbose) {
 		int i;
 		for (i = 0; i <= WME_AC_VO; i++) {
@@ -5480,372 +6070,448 @@
 		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)
+ath_dfs_tasklet(void *p, int npending)
 {
-	struct ath_hal *ah = sc->sc_ah;
-	HAL_BEACON_TIMERS bt;
+	struct ath_softc *sc = (struct ath_softc *) p;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ieee80211com *ic = ifp->if_l2com;
 
-	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);
+	/*
+	 * If previous processing has found a radar event,
+	 * signal this to the net80211 layer to begin DFS
+	 * processing.
+	 */
+	if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) {
+		/* DFS event found, initiate channel change */
+		/*
+		 * XXX doesn't currently tell us whether the event
+		 * XXX was found in the primary or extension
+		 * XXX channel!
+		 */
+		IEEE80211_LOCK(ic);
+		ieee80211_dfs_notify_radar(ic, sc->sc_curchan);
+		IEEE80211_UNLOCK(ic);
+	}
 }
 
 /*
- * 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.
+ * Enable/disable power save.  This must be called with
+ * no TX driver locks currently held, so it should only
+ * be called from the RX path (which doesn't hold any
+ * TX driver locks.)
  */
 static void
-ath_tdma_bintvalsetup(struct ath_softc *sc,
-	const struct ieee80211_tdma_state *tdma)
+ath_node_powersave(struct ieee80211_node *ni, int enable)
 {
-	/* copy from vap state (XXX check all vaps have same value?) */
-	sc->sc_tdmaslotlen = tdma->tdma_slotlen;
+#ifdef	ATH_SW_PSQ
+	struct ath_node *an = ATH_NODE(ni);
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+	struct ath_vap *avp = ATH_VAP(ni->ni_vap);
 
-	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++;
+	/* XXX and no TXQ locks should be held here */
 
-	if (tdma->tdma_slot == 0) {
+	DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d\n",
+	    __func__,
+	    ni->ni_macaddr,
+	    ":",
+	    !! enable);
+
+	/* Suspend or resume software queue handling */
+	if (enable)
+		ath_tx_node_sleep(sc, an);
+	else
+		ath_tx_node_wakeup(sc, an);
+
+	/* Update net80211 state */
+	avp->av_node_ps(ni, enable);
+#else
+	struct ath_vap *avp = ATH_VAP(ni->ni_vap);
+
+	/* Update net80211 state */
+	avp->av_node_ps(ni, enable);
+#endif/* ATH_SW_PSQ */
+}
+
+/*
+ * Notification from net80211 that the powersave queue state has
+ * changed.
+ *
+ * Since the software queue also may have some frames:
+ *
+ * + if the node software queue has frames and the TID state
+ *   is 0, we set the TIM;
+ * + if the node and the stack are both empty, we clear the TIM bit.
+ * + If the stack tries to set the bit, always set it.
+ * + If the stack tries to clear the bit, only clear it if the
+ *   software queue in question is also cleared.
+ *
+ * TODO: this is called during node teardown; so let's ensure this
+ * is all correctly handled and that the TIM bit is cleared.
+ * It may be that the node flush is called _AFTER_ the net80211
+ * stack clears the TIM.
+ *
+ * Here is the racy part.  Since it's possible >1 concurrent,
+ * overlapping TXes will appear complete with a TX completion in
+ * another thread, it's possible that the concurrent TIM calls will
+ * clash.  We can't hold the node lock here because setting the
+ * TIM grabs the net80211 comlock and this may cause a LOR.
+ * The solution is either to totally serialise _everything_ at
+ * this point (ie, all TX, completion and any reset/flush go into
+ * one taskqueue) or a new "ath TIM lock" needs to be created that
+ * just wraps the driver state change and this call to avp->av_set_tim().
+ *
+ * The same race exists in the net80211 power save queue handling
+ * as well.  Since multiple transmitting threads may queue frames
+ * into the driver, as well as ps-poll and the driver transmitting
+ * frames (and thus clearing the psq), it's quite possible that
+ * a packet entering the PSQ and a ps-poll being handled will
+ * race, causing the TIM to be cleared and not re-set.
+ */
+static int
+ath_node_set_tim(struct ieee80211_node *ni, int enable)
+{
+#ifdef	ATH_SW_PSQ
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_vap *avp = ATH_VAP(ni->ni_vap);
+	int changed = 0;
+
+	ATH_TX_LOCK(sc);
+	an->an_stack_psq = enable;
+
+	/*
+	 * This will get called for all operating modes,
+	 * even if avp->av_set_tim is unset.
+	 * It's currently set for hostap/ibss modes; but
+	 * the same infrastructure is used for both STA
+	 * and AP/IBSS node power save.
+	 */
+	if (avp->av_set_tim == NULL) {
+		ATH_TX_UNLOCK(sc);
+		return (0);
+	}
+
+	/*
+	 * If setting the bit, always set it here.
+	 * If clearing the bit, only clear it if the
+	 * software queue is also empty.
+	 *
+	 * If the node has left power save, just clear the TIM
+	 * bit regardless of the state of the power save queue.
+	 *
+	 * XXX TODO: although atomics are used, it's quite possible
+	 * that a race will occur between this and setting/clearing
+	 * in another thread.  TX completion will occur always in
+	 * one thread, however setting/clearing the TIM bit can come
+	 * from a variety of different process contexts!
+	 */
+	if (enable && an->an_tim_set == 1) {
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: enable=%d, tim_set=1, ignoring\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":",
+		    enable);
+		ATH_TX_UNLOCK(sc);
+	} else if (enable) {
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: enable=%d, enabling TIM\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":",
+		    enable);
+		an->an_tim_set = 1;
+		ATH_TX_UNLOCK(sc);
+		changed = avp->av_set_tim(ni, enable);
+	} else if (an->an_swq_depth == 0) {
+		/* disable */
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: enable=%d, an_swq_depth == 0, disabling\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":",
+		    enable);
+		an->an_tim_set = 0;
+		ATH_TX_UNLOCK(sc);
+		changed = avp->av_set_tim(ni, enable);
+	} else if (! an->an_is_powersave) {
 		/*
-		 * Only slot 0 beacons; other slots respond.
+		 * disable regardless; the node isn't in powersave now
 		 */
-		sc->sc_imask |= HAL_INT_SWBA;
-		sc->sc_tdmaswba = 0;		/* beacon immediately */
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: enable=%d, an_pwrsave=0, disabling\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":",
+		    enable);
+		an->an_tim_set = 0;
+		ATH_TX_UNLOCK(sc);
+		changed = avp->av_set_tim(ni, enable);
 	} else {
-		/* XXX all vaps must be slot 0 or slot !0 */
-		sc->sc_imask &= ~HAL_INT_SWBA;
+		/*
+		 * psq disable, node is currently in powersave, node
+		 * software queue isn't empty, so don't clear the TIM bit
+		 * for now.
+		 */
+		ATH_TX_UNLOCK(sc);
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: enable=%d, an_swq_depth > 0, ignoring\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":",
+		    enable);
+		changed = 0;
 	}
+
+	return (changed);
+#else
+	struct ath_vap *avp = ATH_VAP(ni->ni_vap);
+
+	/*
+	 * Some operating modes don't set av_set_tim(), so don't
+	 * update it here.
+	 */
+	if (avp->av_set_tim == NULL)
+		return (0);
+
+	return (avp->av_set_tim(ni, enable));
+#endif /* ATH_SW_PSQ */
 }
 
 /*
- * Max 802.11 overhead.  This assumes no 4-address frames and
- * the encapsulation done by ieee80211_encap (llc).  We also
- * include potential crypto overhead.
+ * Set or update the TIM from the software queue.
+ *
+ * Check the software queue depth before attempting to do lock
+ * anything; that avoids trying to obtain the lock.  Then,
+ * re-check afterwards to ensure nothing has changed in the
+ * meantime.
+ *
+ * set:   This is designed to be called from the TX path, after
+ *        a frame has been queued; to see if the swq > 0.
+ *
+ * clear: This is designed to be called from the buffer completion point
+ *        (right now it's ath_tx_default_comp()) where the state of
+ *        a software queue has changed.
+ *
+ * It makes sense to place it at buffer free / completion rather
+ * than after each software queue operation, as there's no real
+ * point in churning the TIM bit as the last frames in the software
+ * queue are transmitted.  If they fail and we retry them, we'd
+ * just be setting the TIM bit again anyway.
  */
-#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)
+void
+ath_tx_update_tim(struct ath_softc *sc, struct ieee80211_node *ni,
+     int enable)
 {
-	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;
+#ifdef	ATH_SW_PSQ
+	struct ath_node *an;
+	struct ath_vap *avp;
 
-	if (vap == NULL) {
-		vap = TAILQ_FIRST(&ic->ic_vaps);   /* XXX */
-		if (vap == NULL) {
-			if_printf(ifp, "%s: no vaps?\n", __func__);
-			return;
-		}
-	}
-	tp = vap->iv_bss->ni_txparms;
+	/* Don't do this for broadcast/etc frames */
+	if (ni == NULL)
+		return;
+
+	an = ATH_NODE(ni);
+	avp = ATH_VAP(ni->ni_vap);
+
 	/*
-	 * 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).
+	 * And for operating modes without the TIM handler set, let's
+	 * just skip those.
 	 */
-	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);
+	if (avp->av_set_tim == NULL)
+		return;
 
-	ath_hal_intrset(ah, 0);
+	ATH_TX_LOCK_ASSERT(sc);
 
-	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;
+	if (enable) {
+		if (an->an_is_powersave &&
+		    an->an_tim_set == 0 &&
+		    an->an_swq_depth != 0) {
+			DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+			    "%s: %6D: swq_depth>0, tim_set=0, set!\n",
+			    __func__,
+			    ni->ni_macaddr,
+			    ":");
+			an->an_tim_set = 1;
+			(void) avp->av_set_tim(ni, 1);
+		}
+	} else {
+		/*
+		 * Don't bother grabbing the lock unless the queue is empty.
+		 */
+		if (&an->an_swq_depth != 0)
+			return;
 
-	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);
+		if (an->an_is_powersave &&
+		    an->an_stack_psq == 0 &&
+		    an->an_tim_set == 1 &&
+		    an->an_swq_depth == 0) {
+			DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+			    "%s: %6D: swq_depth=0, tim_set=1, psq_set=0,"
+			    " clear!\n",
+			    __func__,
+			    ni->ni_macaddr,
+			    ":");
+			an->an_tim_set = 0;
+			(void) avp->av_set_tim(ni, 0);
+		}
+	}
+#else
+	return;
+#endif	/* ATH_SW_PSQ */
 }
 
 /*
- * 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.
+ * Received a ps-poll frame from net80211.
+ *
+ * Here we get a chance to serve out a software-queued frame ourselves
+ * before we punt it to net80211 to transmit us one itself - either
+ * because there's traffic in the net80211 psq, or a NULL frame to
+ * indicate there's nothing else.
  */
 static void
-ath_tdma_update(struct ieee80211_node *ni,
-	const struct ieee80211_tdma_param *tdma, int changed)
+ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m)
 {
-#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;
+#ifdef	ATH_SW_PSQ
+	struct ath_node *an;
+	struct ath_vap *avp;
 	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;
+	int tid;
 
-	sc->sc_stats.ast_tdma_update++;
+	/* Just paranoia */
+	if (ni == NULL)
+		return;
 
 	/*
-	 * Check for and adopt configuration changes.
+	 * Unassociated (temporary node) station.
 	 */
-	if (changed != 0) {
-		const struct ieee80211_tdma_state *ts = vap->iv_tdma;
+	if (ni->ni_associd == 0)
+		return;
 
-		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.
+	 * We do have an active node, so let's begin looking into it.
 	 */
-	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;
+	an = ATH_NODE(ni);
+	avp = ATH_VAP(ni->ni_vap);
 
 	/*
-	 * 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.
+	 * For now, we just call the original ps-poll method.
+	 * Once we're ready to flip this on:
+	 *
+	 * + Set leak to 1, as no matter what we're going to have
+	 *   to send a frame;
+	 * + Check the software queue and if there's something in it,
+	 *   schedule the highest TID thas has traffic from this node.
+	 *   Then make sure we schedule the software scheduler to
+	 *   run so it picks up said frame.
+	 *
+	 * That way whatever happens, we'll at least send _a_ frame
+	 * to the given node.
+	 *
+	 * Again, yes, it's crappy QoS if the node has multiple
+	 * TIDs worth of traffic - but let's get it working first
+	 * before we optimise it.
+	 *
+	 * Also yes, there's definitely latency here - we're not
+	 * direct dispatching to the hardware in this path (and
+	 * we're likely being called from the packet receive path,
+	 * so going back into TX may be a little hairy!) but again
+	 * I'd like to get this working first before optimising
+	 * turn-around time.
 	 */
-	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));
+	ATH_TX_LOCK(sc);
 
-	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);
+	/*
+	 * Legacy - we're called and the node isn't asleep.
+	 * Immediately punt.
+	 */
+	if (! an->an_is_powersave) {
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: not in powersave?\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":");
+		ATH_TX_UNLOCK(sc);
+		avp->av_recv_pspoll(ni, m);
+		return;
 	}
-	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.
+	 * We're in powersave.
 	 *
-	 * 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.
+	 * Leak a frame.
 	 */
-	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
-}
+	an->an_leak_count = 1;
 
-/*
- * 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.
+	 * Now, if there's no frames in the node, just punt to
+	 * recv_pspoll.
+	 *
+	 * Don't bother checking if the TIM bit is set, we really
+	 * only care if there are any frames here!
 	 */
-	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);
+	if (an->an_swq_depth == 0) {
+		ATH_TX_UNLOCK(sc);
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: SWQ empty; punting to net80211\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":");
+		avp->av_recv_pspoll(ni, m);
 		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
+	 * Ok, let's schedule the highest TID that has traffic
+	 * and then schedule something.
 	 */
-	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) {
+	for (tid = IEEE80211_TID_SIZE - 1; tid >= 0; tid--) {
+		struct ath_tid *atid = &an->an_tid[tid];
 		/*
-		 * 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.
+		 * No frames? Skip.
 		 */
-		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? */
-
+		if (atid->axq_depth == 0)
+			continue;
+		ath_tx_tid_sched(sc, atid);
 		/*
-		 * Record local TSF for our last send for use
-		 * in arbitrating slot collisions.
+		 * XXX we could do a direct call to the TXQ
+		 * scheduler code here to optimise latency
+		 * at the expense of a REALLY deep callstack.
 		 */
-		vap->iv_bss->ni_tstamp.tsf = ath_hal_gettsf64(ah);
+		ATH_TX_UNLOCK(sc);
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask);
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: leaking frame to TID %d\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":",
+		    tid);
+		return;
 	}
-}
-#endif /* IEEE80211_SUPPORT_TDMA */
 
-static void
-ath_dfs_tasklet(void *p, int npending)
-{
-	struct ath_softc *sc = (struct ath_softc *) p;
-	struct ifnet *ifp = sc->sc_ifp;
-	struct ieee80211com *ic = ifp->if_l2com;
+	ATH_TX_UNLOCK(sc);
 
 	/*
-	 * If previous processing has found a radar event,
-	 * signal this to the net80211 layer to begin DFS
-	 * processing.
+	 * XXX nothing in the TIDs at this point? Eek.
 	 */
-	if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) {
-		/* DFS event found, initiate channel change */
-		ieee80211_dfs_notify_radar(ic, sc->sc_curchan);
-	}
+	DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+	    "%s: %6D: TIDs empty, but ath_node showed traffic?!\n",
+	    __func__,
+	    ni->ni_macaddr,
+	    ":");
+	avp->av_recv_pspoll(ni, m);
+#else
+	avp->av_recv_pspoll(ni, m);
+#endif	/* ATH_SW_PSQ */
 }
 
 MODULE_VERSION(if_ath, 1);
 MODULE_DEPEND(if_ath, wlan, 1, 1, 1);          /* 802.11 media layer */
+#if	defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ)
+MODULE_DEPEND(if_ath, alq, 1, 1, 1);
+#endif

Modified: trunk/sys/dev/ath/if_ath_ahb.c
===================================================================
--- trunk/sys/dev/ath/if_ath_ahb.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_ahb.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd
@@ -29,12 +30,14 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_ahb.c 252239 2013-06-26 04:58: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>
@@ -83,11 +86,29 @@
 static int
 ath_ahb_probe(device_t dev)
 {
+	int vendor_id, device_id;
 	const char* devname;
 
-	/* Atheros / ar9130 */
-	devname = ath_hal_probe(VENDOR_ATHEROS, AR9130_DEVID);
+	/*
+	 * Check if a device/vendor ID is provided in hints.
+	 */
+	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "vendor_id", &vendor_id) != 0) {
+		vendor_id = VENDOR_ATHEROS;
+	}
 
+	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "device_id", &device_id) != 0) {
+		device_id = AR9130_DEVID;
+	}
+
+	device_printf(dev, "Vendor=0x%04x, Device=0x%04x\n",
+	    vendor_id & 0xffff,
+	    device_id & 0xffff);
+
+	/* Attempt to probe */
+	devname = ath_hal_probe(vendor_id, device_id);
+
 	if (devname != NULL) {
 		device_set_desc(dev, devname);
 		return BUS_PROBE_DEFAULT;
@@ -103,7 +124,9 @@
 	int error = ENXIO;
 	int rid;
 	long eepromaddr;
+	int eepromsize;
 	uint8_t *p;
+	int device_id, vendor_id;
 
 	sc->sc_dev = dev;
 
@@ -114,15 +137,27 @@
 		goto bad;
 	}
 
-        if (resource_long_value(device_get_name(dev), device_get_unit(dev),
-            "eepromaddr", &eepromaddr) != 0) {
+	if (resource_long_value(device_get_name(dev), device_get_unit(dev),
+	    "eepromaddr", &eepromaddr) != 0) {
 		device_printf(dev, "cannot fetch 'eepromaddr' from hints\n");
 		goto bad0;
-        }
+	}
+
+	/*
+	 * The default EEPROM size is 2048 * 16 bit words.
+	 * Later EEPROM/OTP/flash regions may be quite a bit bigger.
+	 */
+	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "eepromsize", &eepromsize) != 0) {
+		eepromsize = ATH_EEPROM_DATA_SIZE * 2;
+	}
+
+
 	rid = 0;
-	device_printf(sc->sc_dev, "eeprom @ %p\n", (void *) eepromaddr);
+	device_printf(sc->sc_dev, "eeprom @ %p (%d bytes)\n",
+	    (void *) eepromaddr, eepromsize);
 	psc->sc_eeprom = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, (uintptr_t) eepromaddr,
-	  (uintptr_t) eepromaddr + (uintptr_t) ((ATH_EEPROM_DATA_SIZE * 2) - 1), 0, RF_ACTIVE);
+	  (uintptr_t) eepromaddr + (uintptr_t) (eepromsize - 1), 0, RF_ACTIVE);
 	if (psc->sc_eeprom == NULL) {
 		device_printf(dev, "cannot map eeprom space\n");
 		goto bad0;
@@ -138,7 +173,7 @@
 	sc->sc_invalid = 1;
 
 	/* Copy the EEPROM data out */
-	sc->sc_eepromdata = malloc(ATH_EEPROM_DATA_SIZE * 2, M_TEMP, M_NOWAIT | M_ZERO);
+	sc->sc_eepromdata = malloc(eepromsize, M_TEMP, M_NOWAIT | M_ZERO);
 	if (sc->sc_eepromdata == NULL) {
 		device_printf(dev, "cannot allocate memory for eeprom data\n");
 		goto bad1;
@@ -149,10 +184,10 @@
 	bus_space_read_multi_1(
 	    rman_get_bustag(psc->sc_eeprom),
 	    rman_get_bushandle(psc->sc_eeprom),
-	    0, (u_int8_t *) sc->sc_eepromdata, ATH_EEPROM_DATA_SIZE * 2);
+	    0, (u_int8_t *) sc->sc_eepromdata, eepromsize);
 #endif
 	p = (void *) rman_get_bushandle(psc->sc_eeprom);
-	memcpy(sc->sc_eepromdata, p, ATH_EEPROM_DATA_SIZE * 2);
+	memcpy(sc->sc_eepromdata, p, eepromsize);
 
 	/*
 	 * Arrange interrupt line.
@@ -189,12 +224,35 @@
 		goto bad3;
 	}
 
+	/*
+	 * Check if a device/vendor ID is provided in hints.
+	 */
+	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "vendor_id", &vendor_id) != 0) {
+		vendor_id = VENDOR_ATHEROS;
+	}
+
+	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+	    "device_id", &device_id) != 0) {
+		device_id = AR9130_DEVID;
+	}
+
 	ATH_LOCK_INIT(sc);
+	ATH_PCU_LOCK_INIT(sc);
+	ATH_RX_LOCK_INIT(sc);
+	ATH_TX_LOCK_INIT(sc);
+	ATH_TX_IC_LOCK_INIT(sc);
+	ATH_TXSTATUS_LOCK_INIT(sc);
 
-	error = ath_attach(AR9130_DEVID, sc);
+	error = ath_attach(device_id, sc);
 	if (error == 0)					/* success */
 		return 0;
 
+	ATH_TXSTATUS_LOCK_DESTROY(sc);
+	ATH_RX_LOCK_DESTROY(sc);
+	ATH_TX_LOCK_DESTROY(sc);
+	ATH_TX_IC_LOCK_DESTROY(sc);
+	ATH_PCU_LOCK_DESTROY(sc);
 	ATH_LOCK_DESTROY(sc);
 	bus_dma_tag_destroy(sc->sc_dmat);
 bad3:
@@ -234,6 +292,11 @@
 	if (sc->sc_eepromdata)
 		free(sc->sc_eepromdata, M_TEMP);
 
+	ATH_TXSTATUS_LOCK_DESTROY(sc);
+	ATH_RX_LOCK_DESTROY(sc);
+	ATH_TX_LOCK_DESTROY(sc);
+	ATH_TX_IC_LOCK_DESTROY(sc);
+	ATH_PCU_LOCK_DESTROY(sc);
 	ATH_LOCK_DESTROY(sc);
 
 	return (0);

Added: trunk/sys/dev/ath/if_ath_alq.c
===================================================================
--- trunk/sys/dev/ath/if_ath_alq.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_alq.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,196 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 Adrian Chadd
+ * 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: stable/10/sys/dev/ath/if_ath_alq.c 250618 2013-05-13 21:17:27Z adrian $
+ */
+#include "opt_ah.h"
+#include "opt_ath.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/pcpu.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/alq.h>
+#include <sys/endian.h>
+#include <sys/time.h>
+
+#include <dev/ath/if_ath_alq.h>
+
+#ifdef	ATH_DEBUG_ALQ
+static struct ale *
+if_ath_alq_get(struct if_ath_alq *alq, int len)
+{
+	struct ale *ale;
+
+	if (alq->sc_alq_isactive == 0)
+		return (NULL);
+
+	ale = alq_getn(alq->sc_alq_alq, len, ALQ_NOWAIT);
+	if (! ale)
+		alq->sc_alq_numlost++;
+	return (ale);
+}
+
+void
+if_ath_alq_init(struct if_ath_alq *alq, const char *devname)
+{
+
+	bzero(alq, sizeof(*alq));
+
+	strncpy(alq->sc_alq_devname, devname, ATH_ALQ_DEVNAME_LEN);
+	printf("%s (%s): attached\n", __func__, alq->sc_alq_devname);
+	snprintf(alq->sc_alq_filename, ATH_ALQ_FILENAME_LEN,
+	    "/tmp/ath_%s_alq.log", alq->sc_alq_devname);
+
+	/* XXX too conservative, right? */
+	alq->sc_alq_qsize = (64*1024);
+}
+
+void
+if_ath_alq_setcfg(struct if_ath_alq *alq, uint32_t macVer,
+    uint32_t macRev, uint32_t phyRev, uint32_t halMagic)
+{
+
+	/* Store these in network order */
+	alq->sc_alq_cfg.sc_mac_version = htobe32(macVer);
+	alq->sc_alq_cfg.sc_mac_revision = htobe32(macRev);
+	alq->sc_alq_cfg.sc_phy_rev = htobe32(phyRev);
+	alq->sc_alq_cfg.sc_hal_magic = htobe32(halMagic);
+}
+
+void
+if_ath_alq_tidyup(struct if_ath_alq *alq)
+{
+
+	if_ath_alq_stop(alq);
+	printf("%s (%s): detached\n", __func__, alq->sc_alq_devname);
+	bzero(alq, sizeof(*alq));
+}
+
+int
+if_ath_alq_start(struct if_ath_alq *alq)
+{
+	int error;
+
+	if (alq->sc_alq_isactive)
+		return (0);
+
+	/*
+	 * Create a variable-length ALQ.
+	 */
+	error = alq_open(&alq->sc_alq_alq, alq->sc_alq_filename,
+	    curthread->td_ucred, ALQ_DEFAULT_CMODE,
+	    alq->sc_alq_qsize, 0);
+
+	if (error != 0) {
+		printf("%s (%s): failed, err=%d\n", __func__,
+		    alq->sc_alq_devname, error);
+	} else {
+		printf("%s (%s): opened\n", __func__, alq->sc_alq_devname);
+		alq->sc_alq_isactive = 1;
+		if_ath_alq_post(alq, ATH_ALQ_INIT_STATE,
+		    sizeof (struct if_ath_alq_init_state),
+		    (char *) &alq->sc_alq_cfg);
+	}
+	return (error);
+}
+
+int
+if_ath_alq_stop(struct if_ath_alq *alq)
+{
+
+	if (alq->sc_alq_isactive == 0)
+		return (0);
+
+	printf("%s (%s): closed\n", __func__, alq->sc_alq_devname);
+
+	alq->sc_alq_isactive = 0;
+	alq_close(alq->sc_alq_alq);
+	alq->sc_alq_alq = NULL;
+
+	return (0);
+}
+
+/*
+ * Post a debug message to the ALQ.
+ *
+ * "len" is the size of the buf payload in bytes.
+ */
+void
+if_ath_alq_post(struct if_ath_alq *alq, uint16_t op, uint16_t len,
+    const char *buf)
+{
+	struct if_ath_alq_hdr *ap;
+	struct ale *ale;
+	struct timeval tv;
+
+	if (! if_ath_alq_checkdebug(alq, op))
+		return;
+
+	microtime(&tv);
+
+	/*
+	 * Enforce some semblence of sanity on 'len'.
+	 * Although strictly speaking, any length is possible -
+	 * just be conservative so things don't get out of hand.
+	 */
+	if (len > ATH_ALQ_PAYLOAD_LEN)
+		len = ATH_ALQ_PAYLOAD_LEN;
+
+	ale = if_ath_alq_get(alq, len + sizeof(struct if_ath_alq_hdr));
+
+	if (ale == NULL)
+		return;
+
+	ap = (struct if_ath_alq_hdr *) ale->ae_data;
+	ap->threadid = htobe64((uint64_t) curthread->td_tid);
+	ap->tstamp_sec = htobe32((uint32_t) tv.tv_sec);
+	ap->tstamp_usec = htobe32((uint32_t) tv.tv_usec);
+	ap->op = htobe16(op);
+	ap->len = htobe16(len);
+
+	/*
+	 * Copy the payload _after_ the header field.
+	 */
+	if (buf != NULL) {
+		memcpy(((char *) ap) + sizeof(struct if_ath_alq_hdr),
+		    buf,
+		    len);
+	}
+
+	alq_post(alq->sc_alq_alq, ale);
+}
+#endif	/* ATH_DEBUG */


Property changes on: trunk/sys/dev/ath/if_ath_alq.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_alq.h
===================================================================
--- trunk/sys/dev/ath/if_ath_alq.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_alq.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,192 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2012 Adrian Chadd
+ * 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: stable/10/sys/dev/ath/if_ath_alq.h 250619 2013-05-13 21:18:00Z adrian $
+ */
+#ifndef	__IF_ATH_ALQ_H__
+#define	__IF_ATH_ALQ_H__
+
+#define	ATH_ALQ_INIT_STATE		1
+struct if_ath_alq_init_state {
+	uint32_t	sc_mac_version;
+	uint32_t	sc_mac_revision;
+	uint32_t	sc_phy_rev;
+	uint32_t	sc_hal_magic;
+};
+
+#define	ATH_ALQ_EDMA_TXSTATUS		2
+#define	ATH_ALQ_EDMA_RXSTATUS		3
+#define	ATH_ALQ_EDMA_TXDESC		4
+
+#define	ATH_ALQ_TDMA_BEACON_STATE	5
+struct if_ath_alq_tdma_beacon_state {
+	uint64_t	rx_tsf;		/* RX TSF of beacon frame */
+	uint64_t	beacon_tsf;	/* TSF inside beacon frame */
+	uint64_t	tsf64;
+	uint64_t	nextslot_tsf;
+	uint32_t	nextslot_tu;
+	uint32_t	txtime;
+};
+
+#define	ATH_ALQ_TDMA_TIMER_CONFIG	6
+struct if_ath_alq_tdma_timer_config {
+	uint32_t	tdma_slot;
+	uint32_t	tdma_slotlen;
+	uint32_t	tdma_slotcnt;
+	uint32_t	tdma_bintval;
+	uint32_t	tdma_guard;
+	uint32_t	tdma_scbintval;
+	uint32_t	tdma_dbaprep;
+};
+
+#define	ATH_ALQ_TDMA_SLOT_CALC		7
+struct if_ath_alq_tdma_slot_calc {
+	uint64_t	nexttbtt;
+	uint64_t	next_slot;
+	int32_t		tsfdelta;
+	int32_t		avg_plus;
+	int32_t		avg_minus;
+};
+
+#define	ATH_ALQ_TDMA_TSF_ADJUST		8
+struct if_ath_alq_tdma_tsf_adjust {
+	uint64_t	tsf64_old;
+	uint64_t	tsf64_new;
+	int32_t		tsfdelta;
+};
+
+#define	ATH_ALQ_TDMA_TIMER_SET		9
+struct if_ath_alq_tdma_timer_set {
+	uint32_t	bt_intval;
+	uint32_t	bt_nexttbtt;
+	uint32_t	bt_nextdba;
+	uint32_t	bt_nextswba;
+	uint32_t	bt_nextatim;
+	uint32_t	bt_flags;
+	uint32_t	sc_tdmadbaprep;
+	uint32_t	sc_tdmaswbaprep;
+};
+
+#define	ATH_ALQ_INTR_STATUS		10
+struct if_ath_alq_interrupt {
+	uint32_t	intr_status;
+	uint32_t	intr_state[8];
+	uint32_t	intr_syncstate;
+};
+
+#define	ATH_ALQ_MIB_COUNTERS		11
+struct if_ath_alq_mib_counters {
+	uint32_t	valid;
+	uint32_t	tx_busy;
+	uint32_t	rx_busy;
+	uint32_t	chan_busy;
+	uint32_t	ext_chan_busy;
+	uint32_t	cycle_count;
+};
+
+#define	ATH_ALQ_MISSED_BEACON		12
+#define	ATH_ALQ_STUCK_BEACON		13
+#define	ATH_ALQ_RESUME_BEACON		14
+
+/*
+ * These will always be logged, regardless.
+ */
+#define	ATH_ALQ_LOG_ALWAYS_MASK		0x00000001
+
+#define	ATH_ALQ_FILENAME_LEN	128
+#define	ATH_ALQ_DEVNAME_LEN	32
+
+struct if_ath_alq {
+	uint32_t	sc_alq_debug;		/* Debug flags to report */
+	struct alq *	sc_alq_alq;		/* alq state */
+	unsigned int	sc_alq_qsize;		/* queue size */
+	unsigned int	sc_alq_numlost;		/* number of "lost" entries */
+	int		sc_alq_isactive;
+	char		sc_alq_devname[ATH_ALQ_DEVNAME_LEN];
+	char		sc_alq_filename[ATH_ALQ_FILENAME_LEN];
+	struct if_ath_alq_init_state sc_alq_cfg;
+};
+
+/* 128 bytes in total */
+#define	ATH_ALQ_PAYLOAD_LEN		112
+
+struct if_ath_alq_hdr {
+	uint64_t	threadid;
+	uint32_t	tstamp_sec;
+	uint32_t	tstamp_usec;
+	uint16_t	op;
+	uint16_t	len;	/* Length of (optional) payload */
+};
+
+struct if_ath_alq_payload {
+	struct if_ath_alq_hdr hdr;
+	char		payload[];
+};
+
+#ifdef	_KERNEL
+static inline int
+if_ath_alq_checkdebug(struct if_ath_alq *alq, uint16_t op)
+{
+
+	return ((alq->sc_alq_debug | ATH_ALQ_LOG_ALWAYS_MASK)
+	    & (1 << (op - 1)));
+}
+
+extern	void if_ath_alq_init(struct if_ath_alq *alq, const char *devname);
+extern	void if_ath_alq_setcfg(struct if_ath_alq *alq, uint32_t macVer,
+	    uint32_t macRev, uint32_t phyRev, uint32_t halMagic);
+extern	void if_ath_alq_tidyup(struct if_ath_alq *alq);
+extern	int if_ath_alq_start(struct if_ath_alq *alq);
+extern	int if_ath_alq_stop(struct if_ath_alq *alq);
+extern	void if_ath_alq_post(struct if_ath_alq *alq, uint16_t op,
+	    uint16_t len, const char *buf);
+
+/* XXX maybe doesn't belong here? */
+static inline void
+if_ath_alq_post_intr(struct if_ath_alq *alq, uint32_t status,
+    uint32_t *state, uint32_t sync_state)
+{
+	int i;
+	struct if_ath_alq_interrupt intr;
+
+	if (! if_ath_alq_checkdebug(alq, ATH_ALQ_INTR_STATUS))
+		return;
+
+	intr.intr_status = htobe32(status);
+	for (i = 0; i < 8; i++)
+		intr.intr_state[i] = htobe32(state[i]);
+	intr.intr_syncstate = htobe32(sync_state);
+
+	if_ath_alq_post(alq, ATH_ALQ_INTR_STATUS, sizeof(intr),
+	    (const char *) &intr);
+}
+
+#endif	/* _KERNEL */
+
+#endif


Property changes on: trunk/sys/dev/ath/if_ath_alq.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_beacon.c
===================================================================
--- trunk/sys/dev/ath/if_ath_beacon.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_beacon.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,1120 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_beacon.c 250783 2013-05-18 18:27:53Z 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_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_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;
+	if (sc->sc_isedma)
+		qi.tqi_qflags |= HAL_TXQ_TXOKINT_ENABLE |
+		    HAL_TXQ_TXERRINT_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;
+	HAL_DMA_ADDR bufAddrList[4];
+	uint32_t segLenList[4];
+	HAL_11N_RATE_SERIES rc[4];
+
+	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));
+
+	/*
+	 * 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 */
+		, ieee80211_get_node_txpower(ni)	/* 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 */
+	);
+
+	/*
+	 * The EDMA HAL currently assumes that _all_ rate control
+	 * settings are done in ath_hal_set11nratescenario(), rather
+	 * than in ath_hal_setuptxdesc().
+	 */
+	if (sc->sc_isedma) {
+		memset(&rc, 0, sizeof(rc));
+
+		rc[0].ChSel = sc->sc_txchainmask;
+		rc[0].Tries = 1;
+		rc[0].Rate = rt->info[rix].rateCode;
+		rc[0].RateIndex = rix;
+		rc[0].tx_power_cap = 0x3f;
+		rc[0].PktDuration =
+		    ath_hal_computetxtime(ah, rt, roundup(m->m_len, 4),
+		        rix, 0);
+		ath_hal_set11nratescenario(ah, ds, 0, 0, rc, 4, flags);
+	}
+
+	/* NB: beacon's BufLen must be a multiple of 4 bytes */
+	segLenList[0] = roundup(m->m_len, 4);
+	segLenList[1] = segLenList[2] = segLenList[3] = 0;
+	bufAddrList[0] = bf->bf_segs[0].ds_addr;
+	bufAddrList[1] = bufAddrList[2] = bufAddrList[3] = 0;
+	ath_hal_filltxdesc(ah, ds
+		, bufAddrList
+		, segLenList
+		, 0				/* XXX desc id */
+		, sc->sc_bhalq			/* hardware TXQ */
+		, 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);
+}
+
+/*
+ * Handle a beacon miss.
+ */
+static void
+ath_beacon_miss(struct ath_softc *sc)
+{
+	HAL_SURVEY_SAMPLE hs;
+	HAL_BOOL ret;
+	uint32_t hangs;
+
+	bzero(&hs, sizeof(hs));
+
+	ret = ath_hal_get_mib_cycle_counts(sc->sc_ah, &hs);
+
+	if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) {
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+		    "%s: hang=0x%08x\n",
+		    __func__,
+		    hangs);
+	}
+
+#ifdef	ATH_DEBUG_ALQ
+	if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_MISSED_BEACON))
+		if_ath_alq_post(&sc->sc_alq, ATH_ALQ_MISSED_BEACON, 0, NULL);
+#endif
+
+	DPRINTF(sc, ATH_DEBUG_BEACON,
+	    "%s: valid=%d, txbusy=%u, rxbusy=%u, chanbusy=%u, "
+	    "extchanbusy=%u, cyclecount=%u\n",
+	    __func__,
+	    ret,
+	    hs.tx_busy,
+	    hs.rx_busy,
+	    hs.chan_busy,
+	    hs.ext_chan_busy,
+	    hs.cycle_count);
+}
+
+/*
+ * 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++;
+		ath_beacon_miss(sc);
+		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;
+#ifdef	ATH_DEBUG_ALQ
+		if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_RESUME_BEACON))
+			if_ath_alq_post(&sc->sc_alq, ATH_ALQ_RESUME_BEACON, 0, NULL);
+#endif
+	}
+
+	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);
+				/*
+				 * XXX TODO: this should use settxdesclinkptr()
+				 * otherwise it won't work for EDMA chipsets!
+				 */
+				if (bf != NULL) {
+					/* XXX should do this using the ds */
+					*bflink = bf->bf_daddr;
+					ath_hal_gettxdesclinkptr(sc->sc_ah,
+					    bf->bf_desc, &bflink);
+				}
+			}
+		}
+		/*
+		 * XXX TODO: this should use settxdesclinkptr()
+		 * otherwise it won't work for EDMA chipsets!
+		 */
+		*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;
+	}
+
+	/* Program the CABQ with the contents of the CABQ txq and start it */
+	ATH_TXQ_LOCK(sc->sc_cabq);
+	ath_beacon_cabq_start(sc);
+	ATH_TXQ_UNLOCK(sc->sc_cabq);
+
+	/* Program the new beacon frame if we have one for this interval */
+	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 (! sc->sc_isedma) {
+			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 void
+ath_beacon_cabq_start_edma(struct ath_softc *sc)
+{
+	struct ath_buf *bf, *bf_last;
+	struct ath_txq *cabq = sc->sc_cabq;
+#if 0
+	struct ath_buf *bfi;
+	int i = 0;
+#endif
+
+	ATH_TXQ_LOCK_ASSERT(cabq);
+
+	if (TAILQ_EMPTY(&cabq->axq_q))
+		return;
+	bf = TAILQ_FIRST(&cabq->axq_q);
+	bf_last = TAILQ_LAST(&cabq->axq_q, axq_q_s);
+
+	/*
+	 * This is a dirty, dirty hack to push the contents of
+	 * the cabq staging queue into the FIFO.
+	 *
+	 * This ideally should live in the EDMA code file
+	 * and only push things into the CABQ if there's a FIFO
+	 * slot.
+	 *
+	 * We can't treat this like a normal TX queue because
+	 * in the case of multi-VAP traffic, we may have to flush
+	 * the CABQ each new (staggered) beacon that goes out.
+	 * But for non-staggered beacons, we could in theory
+	 * handle multicast traffic for all VAPs in one FIFO
+	 * push.  Just keep all of this in mind if you're wondering
+	 * how to correctly/better handle multi-VAP CABQ traffic
+	 * with EDMA.
+	 */
+
+	/*
+	 * Is the CABQ FIFO free? If not, complain loudly and
+	 * don't queue anything.  Maybe we'll flush the CABQ
+	 * traffic, maybe we won't.  But that'll happen next
+	 * beacon interval.
+	 */
+	if (cabq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) {
+		device_printf(sc->sc_dev,
+		    "%s: Q%d: CAB FIFO queue=%d?\n",
+		    __func__,
+		    cabq->axq_qnum,
+		    cabq->axq_fifo_depth);
+		return;
+	}
+
+	/*
+	 * Ok, so here's the gymnastics reqiured to make this
+	 * all sensible.
+	 */
+
+	/*
+	 * Tag the first/last buffer appropriately.
+	 */
+	bf->bf_flags |= ATH_BUF_FIFOPTR;
+	bf_last->bf_flags |= ATH_BUF_FIFOEND;
+
+#if 0
+	i = 0;
+	TAILQ_FOREACH(bfi, &cabq->axq_q, bf_list) {
+		ath_printtxbuf(sc, bf, cabq->axq_qnum, i, 0);
+		i++;
+	}
+#endif
+
+	/*
+	 * We now need to push this set of frames onto the tail
+	 * of the FIFO queue.  We don't adjust the aggregate
+	 * count, only the queue depth counter(s).
+	 * We also need to blank the link pointer now.
+	 */
+	TAILQ_CONCAT(&cabq->fifo.axq_q, &cabq->axq_q, bf_list);
+	cabq->axq_link = NULL;
+	cabq->fifo.axq_depth += cabq->axq_depth;
+	cabq->axq_depth = 0;
+
+	/* Bump FIFO queue */
+	cabq->axq_fifo_depth++;
+
+	/* Push the first entry into the hardware */
+	ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr);
+	cabq->axq_flags |= ATH_TXQ_PUTRUNNING;
+
+	/* NB: gated by beacon so safe to start here */
+	ath_hal_txstart(sc->sc_ah, cabq->axq_qnum);
+
+}
+
+static void
+ath_beacon_cabq_start_legacy(struct ath_softc *sc)
+{
+	struct ath_buf *bf;
+	struct ath_txq *cabq = sc->sc_cabq;
+
+	ATH_TXQ_LOCK_ASSERT(cabq);
+	if (TAILQ_EMPTY(&cabq->axq_q))
+		return;
+	bf = TAILQ_FIRST(&cabq->axq_q);
+
+	/* Push the first entry into the hardware */
+	ath_hal_puttxbuf(sc->sc_ah, cabq->axq_qnum, bf->bf_daddr);
+	cabq->axq_flags |= ATH_TXQ_PUTRUNNING;
+
+	/* NB: gated by beacon so safe to start here */
+	ath_hal_txstart(sc->sc_ah, cabq->axq_qnum);
+}
+
+/*
+ * Start CABQ transmission - this assumes that all frames are prepped
+ * and ready in the CABQ.
+ */
+void
+ath_beacon_cabq_start(struct ath_softc *sc)
+{
+	struct ath_txq *cabq = sc->sc_cabq;
+
+	ATH_TXQ_LOCK_ASSERT(cabq);
+
+	if (TAILQ_EMPTY(&cabq->axq_q))
+		return;
+
+	if (sc->sc_isedma)
+		ath_beacon_cabq_start_edma(sc);
+	else
+		ath_beacon_cabq_start_legacy(sc);
+}
+
+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
+			 */
+			/*
+			 * XXX TODO: this doesn't stop CABQ DMA - it assumes
+			 * that since we're about to transmit a beacon, we've
+			 * already stopped transmitting on the CABQ.  But this
+			 * doesn't at all mean that the CABQ DMA QCU will
+			 * accept a new TXDP!  So what, should we do a DMA
+			 * stop? What if it fails?
+			 *
+			 * More thought is required here.
+			 */
+			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) {
+
+		/* NB: only at DTIM */
+		ATH_TXQ_LOCK(&avp->av_mcastq);
+		if (nmcastq) {
+			struct ath_buf *bfm, *bfc_last;
+
+			/*
+			 * Move frames from the s/w mcast q to the h/w cab q.
+			 *
+			 * XXX TODO: if we chain together multiple VAPs
+			 * worth of CABQ traffic, should we keep the
+			 * MORE data bit set on the last frame of each
+			 * intermediary VAP (ie, only clear the MORE
+			 * bit of the last frame on the last vap?)
+			 */
+			bfm = TAILQ_FIRST(&avp->av_mcastq.axq_q);
+			ATH_TXQ_LOCK(cabq);
+
+			/*
+			 * If there's already a frame on the CABQ, we
+			 * need to link to the end of the last frame.
+			 * We can't use axq_link here because
+			 * EDMA descriptors require some recalculation
+			 * (checksum) to occur.
+			 */
+			bfc_last = ATH_TXQ_LAST(cabq, axq_q_s);
+			if (bfc_last != NULL) {
+				ath_hal_settxdesclink(sc->sc_ah,
+				    bfc_last->bf_lastds,
+				    bfm->bf_daddr);
+			}
+			ath_txqmove(cabq, &avp->av_mcastq);
+			ATH_TXQ_UNLOCK(cabq);
+			/*
+			 * XXX not entirely accurate, in case a mcast
+			 * queue frame arrived before we grabbed the TX
+			 * lock.
+			 */
+			sc->sc_stats.ast_cabq_xmit += nmcastq;
+		}
+		ATH_TXQ_UNLOCK(&avp->av_mcastq);
+	}
+	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_int32_t nexttbtt_u8, intval_u8;
+	u_int64_t tsf;
+
+	if (vap == NULL)
+		vap = TAILQ_FIRST(&ic->ic_vaps);	/* XXX */
+	/*
+	 * Just ensure that we aren't being called when the last
+	 * VAP is destroyed.
+	 */
+	if (vap == NULL) {
+		device_printf(sc->sc_dev, "%s: called with no VAPs\n",
+		    __func__);
+		return;
+	}
+
+	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);
+		}
+
+		/*
+		 * Now dirty things because for now, the EDMA HAL has
+		 * nexttbtt and intval is TU/8.
+		 */
+		if (sc->sc_isedma) {
+			nexttbtt_u8 = (nexttbtt << 3);
+			intval_u8 = (intval << 3);
+			if (intval & HAL_BEACON_ENA)
+				intval_u8 |= HAL_BEACON_ENA;
+			if (intval & HAL_BEACON_RESET_TSF)
+				intval_u8 |= HAL_BEACON_RESET_TSF;
+			ath_hal_beaconinit(ah, nexttbtt_u8, intval_u8);
+		} else
+			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
+}


Property changes on: trunk/sys/dev/ath/if_ath_beacon.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_beacon.h
===================================================================
--- trunk/sys/dev/ath/if_ath_beacon.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_beacon.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,53 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_beacon.h 248671 2013-03-24 00:03:12Z adrian $
+ */
+#ifndef	__IF_ATH_BEACON_H__
+#define	__IF_ATH_BEACON_H__
+
+extern	int ath_bstuck_threshold;
+
+extern	int ath_beaconq_setup(struct ath_softc *sc);
+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	void ath_beacon_cabq_start(struct ath_softc *sc);
+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


Property changes on: trunk/sys/dev/ath/if_ath_beacon.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_btcoex.c
===================================================================
--- trunk/sys/dev/ath/if_ath_btcoex.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_btcoex.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,347 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2013 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: stable/10/sys/dev/ath/if_ath_btcoex.c 332320 2018-04-09 12:53:15Z emaste $
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_btcoex.c 332320 2018-04-09 12:53:15Z emaste $");
+
+/*
+ * This implements some very basic bluetooth coexistence methods for
+ * the ath(4) hardware.
+ */
+#include "opt_ath.h"
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/errno.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <sys/socket.h>
+ 
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>		/* XXX for ether_sprintf */
+
+#include <net80211/ieee80211_var.h>
+
+#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_btcoex.h>
+
+/*
+ * Initial AR9285 / (WB195) bluetooth coexistence settings,
+ * just for experimentation.
+ *
+ * Return 0 for OK; errno for error.
+ *
+ * XXX TODO: There needs to be a PCIe workaround to disable ASPM if
+ * bluetooth coexistence is enabled.
+ */
+static int
+ath_btcoex_cfg_wb195(struct ath_softc *sc)
+{
+	HAL_BT_COEX_INFO btinfo;
+	HAL_BT_COEX_CONFIG btconfig;
+	struct ath_hal *ah = sc->sc_ah;
+
+	if (! ath_hal_btcoex_supported(ah))
+		return (EINVAL);
+
+	bzero(&btinfo, sizeof(btinfo));
+	bzero(&btconfig, sizeof(btconfig));
+
+	device_printf(sc->sc_dev, "Enabling WB195 BTCOEX\n");
+
+	btinfo.bt_module = HAL_BT_MODULE_JANUS;
+	btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE;
+	/*
+	 * These are the three GPIO pins hooked up between the AR9285 and
+	 * the AR3011.
+	 */
+	btinfo.bt_gpio_bt_active = 6;
+	btinfo.bt_gpio_bt_priority = 7;
+	btinfo.bt_gpio_wlan_active = 5;
+	btinfo.bt_active_polarity = 1;	/* XXX not used */
+	btinfo.bt_single_ant = 1;	/* 1 antenna on ar9285 ? */
+	btinfo.bt_isolation = 0;	/* in dB, not used */
+
+	ath_hal_btcoex_set_info(ah, &btinfo);
+
+	btconfig.bt_time_extend = 0;
+	btconfig.bt_txstate_extend = 1;	/* true */
+	btconfig.bt_txframe_extend = 1;	/* true */
+	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
+	btconfig.bt_quiet_collision = 1;	/* true */
+	btconfig.bt_rxclear_polarity = 1;	/* true */
+	btconfig.bt_priority_time = 2;
+	btconfig.bt_first_slot_time = 5;
+	btconfig.bt_hold_rxclear = 1;	/* true */
+
+	ath_hal_btcoex_set_config(ah, &btconfig);
+
+	/*
+	 * Enable antenna diversity.
+	 */
+	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
+
+	return (0);
+}
+
+/*
+ * Initial AR9485 / (WB225) bluetooth coexistence settings,
+ * just for experimentation.
+ *
+ * Return 0 for OK; errno for error.
+ */
+static int
+ath_btcoex_cfg_wb225(struct ath_softc *sc)
+{
+	HAL_BT_COEX_INFO btinfo;
+	HAL_BT_COEX_CONFIG btconfig;
+	struct ath_hal *ah = sc->sc_ah;
+
+	if (! ath_hal_btcoex_supported(ah))
+		return (EINVAL);
+
+	bzero(&btinfo, sizeof(btinfo));
+	bzero(&btconfig, sizeof(btconfig));
+
+	device_printf(sc->sc_dev, "Enabling WB225 BTCOEX\n");
+
+	btinfo.bt_module = HAL_BT_MODULE_JANUS;	/* XXX not used? */
+	btinfo.bt_coex_config = HAL_BT_COEX_CFG_3WIRE;
+	/*
+	 * These are the three GPIO pins hooked up between the AR9485 and
+	 * the bluetooth module.
+	 */
+	btinfo.bt_gpio_bt_active = 4;
+	btinfo.bt_gpio_bt_priority = 8;
+	btinfo.bt_gpio_wlan_active = 5;
+
+	btinfo.bt_active_polarity = 1;	/* XXX not used */
+	btinfo.bt_single_ant = 1;	/* 1 antenna on ar9285 ? */
+	btinfo.bt_isolation = 0;	/* in dB, not used */
+
+	ath_hal_btcoex_set_info(ah, &btinfo);
+
+	btconfig.bt_time_extend = 0;
+	btconfig.bt_txstate_extend = 1;	/* true */
+	btconfig.bt_txframe_extend = 1;	/* true */
+	btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
+	btconfig.bt_quiet_collision = 1;	/* true */
+	btconfig.bt_rxclear_polarity = 1;	/* true */
+	btconfig.bt_priority_time = 2;
+	btconfig.bt_first_slot_time = 5;
+	btconfig.bt_hold_rxclear = 1;	/* true */
+
+	ath_hal_btcoex_set_config(ah, &btconfig);
+
+	/*
+	 * Enable antenna diversity.
+	 */
+	ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
+
+	return (0);
+}
+
+
+#if 0
+/*
+ * When using bluetooth coexistence, ASPM needs to be disabled
+ * otherwise the sleeping interferes with the bluetooth (USB)
+ * operation and the MAC sleep/wakeup hardware.
+ *
+ * The PCIe powersave routine also needs to not be called
+ * by the driver during suspend/resume, else things will get
+ * a little odd.  Check Linux ath9k for more details.
+ */
+static int
+ath_btcoex_aspm_wb195(struct ath_softc *sc)
+{
+
+	/* XXX TODO: clear device ASPM L0S and L1 */
+	/* XXX TODO: clear _parent_ ASPM L0S and L1 */
+}
+#endif
+
+/*
+ * Methods which are required
+ */
+
+/*
+ * Attach btcoex to the given interface
+ */
+int
+ath_btcoex_attach(struct ath_softc *sc)
+{
+	int ret;
+	struct ath_hal *ah = sc->sc_ah;
+	const char *profname;
+
+	/*
+	 * No chipset bluetooth coexistence? Then do nothing.
+	 */
+	if (! ath_hal_btcoex_supported(ah))
+		return (0);
+
+	/*
+	 * Look at the hints to determine which bluetooth
+	 * profile to configure.
+	 */
+	ret = resource_string_value(device_get_name(sc->sc_dev),
+	    device_get_unit(sc->sc_dev),
+	    "btcoex_profile",
+	    &profname);
+	if (ret != 0) {
+		/* nothing to do */
+		return (0);
+	}
+
+	if (strncmp(profname, "wb195", 5) == 0) {
+		ret = ath_btcoex_cfg_wb195(sc);
+	} else if (strncmp(profname, "wb225", 5) == 0) {
+		ret = ath_btcoex_cfg_wb225(sc);
+	} else {
+		return (0);
+	}
+
+	/*
+	 * Propagate up failure from the actual attach phase.
+	 */
+	if (ret != 0)
+		return (ret);
+
+	return (0);
+}
+
+/*
+ * Detach btcoex from the given interface
+ */
+int
+ath_btcoex_detach(struct ath_softc *sc)
+{
+
+	return (0);
+}
+
+/*
+ * Configure or disable bluetooth coexistence on the given channel.
+ *
+ * For AR9285/AR9287/AR9485, we'll never see a 5GHz channel, so we just
+ * assume bluetooth coexistence is always on.
+ *
+ * For AR9462, we may see a 5GHz channel; bluetooth coexistence should
+ * not be enabled on those channels.
+ */
+int
+ath_btcoex_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
+{
+
+	return (0);
+}
+
+/*
+ * Handle ioctl requests from the diagnostic interface.
+ *
+ * The initial part of this code resembles ath_ioctl_diag();
+ * it's likely a good idea to reduce duplication between
+ * these two routines.
+ */
+int
+ath_btcoex_ioctl(struct ath_softc *sc, struct ath_diag *ad)
+{
+	unsigned int id = ad->ad_id & ATH_DIAG_ID;
+	void *indata = NULL;
+	void *outdata = NULL;
+	u_int32_t insize = ad->ad_in_size;
+	u_int32_t outsize = ad->ad_out_size;
+	int error = 0;
+//	int val;
+
+	if (ad->ad_id & ATH_DIAG_IN) {
+		/*
+		 * Copy in data.
+		 */
+		indata = malloc(insize, M_TEMP, M_NOWAIT);
+		if (indata == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
+		error = copyin(ad->ad_in_data, indata, insize);
+		if (error)
+			goto bad;
+	}
+	if (ad->ad_id & ATH_DIAG_DYN) {
+		/*
+		 * Allocate a buffer for the results (otherwise the HAL
+		 * returns a pointer to a buffer where we can read the
+		 * results).  Note that we depend on the HAL leaving this
+		 * pointer for us to use below in reclaiming the buffer;
+		 * may want to be more defensive.
+		 */
+		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
+		if (outdata == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
+	}
+	switch (id) {
+		default:
+			error = EINVAL;
+			goto bad;
+	}
+	if (outsize < ad->ad_out_size)
+		ad->ad_out_size = outsize;
+	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
+		error = EFAULT;
+bad:
+	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
+		free(indata, M_TEMP);
+	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
+		free(outdata, M_TEMP);
+	return (error);
+}
+


Property changes on: trunk/sys/dev/ath/if_ath_btcoex.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_btcoex.h
===================================================================
--- trunk/sys/dev/ath/if_ath_btcoex.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_btcoex.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,41 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2013 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: stable/10/sys/dev/ath/if_ath_btcoex.h 251487 2013-06-07 09:02:02Z adrian $
+ */
+#ifndef	__IF_ATH_BTCOEX_H__
+#define	__IF_ATH_BTCOEX_H__
+
+extern	int ath_btcoex_attach(struct ath_softc *sc);
+extern	int ath_btcoex_detach(struct ath_softc *sc);
+extern	int ath_btcoex_ioctl(struct ath_softc *sc, struct ath_diag *ad);
+extern	int ath_btcoex_enable(struct ath_softc *sc,
+	    const struct ieee80211_channel *ch);
+
+#endif	/* __IF_ATH_BTCOEX_H__ */


Property changes on: trunk/sys/dev/ath/if_ath_btcoex.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/if_ath_debug.c
===================================================================
--- trunk/sys/dev/ath/if_ath_debug.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_debug.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -28,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_debug.c 242599 2012-11-05 07:08:45Z adrian $");
 
 #include "opt_inet.h"
 #include "opt_ath.h"
@@ -89,12 +90,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,39 +119,146 @@
 			    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]);
 		}
 	}
 }
 
-void
-ath_printtxbuf(struct ath_softc *sc, const struct ath_buf *bf,
+static void
+ath_printtxbuf_edma(struct ath_softc *sc, const struct ath_buf *first_bf,
 	u_int qnum, u_int ix, int done)
 {
-	const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
+	const struct ath_tx_status *ts =
+	    &first_bf->bf_last->bf_status.ds_txstat;
+	const struct ath_buf *bf = first_bf;
+	const char *ds;
+	const struct ath_desc_txedma *eds;
+	int i, n;
+
+	printf("Q%u[%3u] (nseg=%d)", qnum, ix, bf->bf_nseg);
+	while (bf != NULL) {
+		/*
+		 * XXX For now, assume the txmap size is 4.
+		 */
+
+		/*
+		 * Assume the TX map size is 4 for now and only walk
+		 * the appropriate number of segments.
+		 */
+		n = ((bf->bf_nseg - 1) / 4) + 1;
+
+		for (i = 0, ds = (const char *) bf->bf_desc;
+		    i < n;
+		    i ++, ds += sc->sc_tx_desclen) {
+			eds = (const struct ath_desc_txedma *) ds;
+			printf(" (DS.V:%p DS.P:%p) I: %08x L:%08x F:%04x%s\n",
+			    eds, (const struct ath_desc *)bf->bf_daddr + i,
+			    eds->ds_info, eds->ds_link,
+			    bf->bf_state.bfs_txflags,
+			    !done ? "" : (ts->ts_status == 0) ? " *" : " !");
+			printf(" (D[0] = %08x(%08x), D[1] = %08x(%08x)\n",
+			    eds->ds_hw[0], eds->ds_hw[1],
+			    eds->ds_hw[2], eds->ds_hw[3]);
+			printf(" (D[2] = %08x(%08x), D[3] = %08x(%08x)\n",
+			    eds->ds_hw[4], eds->ds_hw[5],
+			    eds->ds_hw[6], eds->ds_hw[7]);
+			printf("        Seq: %d swtry: %d ADDBAW?: %d DOBAW?: %d\n",
+			    bf->bf_state.bfs_seqno,
+			    bf->bf_state.bfs_retries,
+			    bf->bf_state.bfs_addedbaw,
+			    bf->bf_state.bfs_dobaw);
+			printf("        %08x %08x %08x %08x %08x %08x\n",
+			    eds->ds_hw[8], eds->ds_hw[9],
+			    eds->ds_hw[10], eds->ds_hw[11],
+			    eds->ds_hw[12], eds->ds_hw[13]);
+			printf("        %08x %08x %08x %08x %08x %08x %08x\n",
+			    eds->ds_hw[14], eds->ds_hw[15], eds->ds_hw[16],
+			    eds->ds_hw[17], eds->ds_hw[18], eds->ds_hw[19],
+			    eds->ds_hw[20]);
+#if 0
+			printf("        %08x %08x %08x %08x %08x %08x %08x %08x\n",
+			    ds->ds_hw[22],ds->ds_hw[23],ds->ds_hw[24],
+			    ds->ds_hw[25],ds->ds_hw[26],ds->ds_hw[27],
+			    ds->ds_hw[28], ds->ds_hw[29]);
+#endif
+		}
+		printf("  [end]\n");
+		bf = bf->bf_next;
+	}
+}
+
+static void
+ath_printtxbuf_legacy(struct ath_softc *sc, const struct ath_buf *first_bf,
+	u_int qnum, u_int ix, int done)
+{
+	const struct ath_tx_status *ts = &first_bf->bf_status.ds_txstat;
+	const struct ath_buf *bf = first_bf;
 	struct ath_hal *ah = sc->sc_ah;
 	const struct ath_desc *ds;
 	int i;
 
 	printf("Q%u[%3u]", qnum, ix);
-	for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
-		printf(" (DS.V:%p DS.P:%p) L:%08x D:%08x F:04%x%s\n"
-		       "        %08x %08x %08x %08x %08x %08x\n",
-		    ds, (const struct ath_desc *)bf->bf_daddr + i,
-		    ds->ds_link, ds->ds_data, bf->bf_txflags,
-		    !done ? "" : (ts->ts_status == 0) ? " *" : " !",
-		    ds->ds_ctl0, ds->ds_ctl1,
-		    ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3]);
-		if (ah->ah_magic == 0x20065416) {
-			printf("        %08x %08x %08x %08x %08x %08x %08x %08x\n",
-			    ds->ds_hw[4], ds->ds_hw[5], ds->ds_hw[6],
-			    ds->ds_hw[7], ds->ds_hw[8], ds->ds_hw[9],
-			    ds->ds_hw[10],ds->ds_hw[11]);
-			printf("        %08x %08x %08x %08x %08x %08x %08x %08x\n",
-			    ds->ds_hw[12],ds->ds_hw[13],ds->ds_hw[14],
-			    ds->ds_hw[15],ds->ds_hw[16],ds->ds_hw[17],
-			    ds->ds_hw[18], ds->ds_hw[19]);
+	while (bf != NULL) {
+		printf("    (bf=%p, lastds=%p)\n", bf, first_bf->bf_lastds);
+		printf("        Seq: %d swtry: %d ADDBAW?: %d DOBAW?: %d\n",
+		    bf->bf_state.bfs_seqno,
+		    bf->bf_state.bfs_retries,
+		    bf->bf_state.bfs_addedbaw,
+		    bf->bf_state.bfs_dobaw);
+		for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
+			printf(" (DS.V:%p DS.P:%p) L:%08x D:%08x F:%04x%s\n",
+			    ds, (const struct ath_desc *)bf->bf_daddr + i,
+			    ds->ds_link, ds->ds_data, bf->bf_state.bfs_txflags,
+			    !done ? "" : (ts->ts_status == 0) ? " *" : " !");
+			printf("        %08x %08x %08x %08x %08x %08x\n",
+			    ds->ds_ctl0, ds->ds_ctl1,
+			    ds->ds_hw[0], ds->ds_hw[1],
+			    ds->ds_hw[2], ds->ds_hw[3]);
+			if (ah->ah_magic == 0x20065416) {
+				printf("        %08x %08x %08x %08x %08x %08x %08x %08x\n",
+				    ds->ds_hw[4], ds->ds_hw[5], ds->ds_hw[6],
+				    ds->ds_hw[7], ds->ds_hw[8], ds->ds_hw[9],
+				    ds->ds_hw[10],ds->ds_hw[11]);
+				printf("        %08x %08x %08x %08x %08x %08x %08x %08x\n",
+				    ds->ds_hw[12],ds->ds_hw[13],ds->ds_hw[14],
+				    ds->ds_hw[15],ds->ds_hw[16],ds->ds_hw[17],
+				    ds->ds_hw[18], ds->ds_hw[19]);
+			}
 		}
+		printf("  [end]\n");
+		bf = bf->bf_next;
 	}
 }
 
+void
+ath_printtxbuf(struct ath_softc *sc, const struct ath_buf *first_bf,
+	u_int qnum, u_int ix, int done)
+{
+	if (sc->sc_ah->ah_magic == 0x19741014)
+		ath_printtxbuf_edma(sc, first_bf, qnum, ix, done);
+	else
+		ath_printtxbuf_legacy(sc, first_bf, qnum, ix, done);
+}
+
+void
+ath_printtxstatbuf(struct ath_softc *sc, const struct ath_buf *first_bf,
+	const uint32_t *ds, u_int qnum, u_int ix, int done)
+{
+
+	printf("Q%u[%3u] ", qnum, ix);
+	printf("        %08x %08x %08x %08x %08x %08x\n",
+	    ds[0], ds[1], ds[2], ds[3], ds[4], ds[5]);
+	printf("        %08x %08x %08x %08x %08x\n",
+	    ds[6], ds[7], ds[8], ds[9], ds[10]);
+}
+
 #endif	/* ATH_DEBUG */

Modified: trunk/sys/dev/ath/if_ath_debug.h
===================================================================
--- trunk/sys/dev/ath/if_ath_debug.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_debug.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_ath_debug.h 251655 2013-06-12 14:52:57Z adrian $
  */
 #ifndef	__IF_ATH_DEBUG_H__
 #define	__IF_ATH_DEBUG_H__
@@ -34,35 +35,62 @@
 #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_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_SW_TX_FILT	= 0x400000000ULL,	/* SW TX FF */
+	ATH_DEBUG_NODE_PWRSAVE	= 0x800000000ULL,	/* node powersave */
+	ATH_DEBUG_DIVERSITY	= 0x1000000000ULL,	/* Diversity logic */
+
+	ATH_DEBUG_ANY		= 0xffffffffffffffffULL
 };
 
-extern int	ath_debug;
+enum {
+	ATH_KTR_RXPROC		= 0x00000001,
+	ATH_KTR_TXPROC		= 0x00000002,
+	ATH_KTR_TXCOMP		= 0x00000004,
+	ATH_KTR_SWQ		= 0x00000008,
+	ATH_KTR_INTERRUPTS	= 0x00000010,
+	ATH_KTR_ERROR		= 0x00000020,
+	ATH_KTR_NODE		= 0x00000040,
+	ATH_KTR_TX		= 0x00000080,
+};
 
+#define	ATH_KTR(_sc, _km, _kf, ...)	do {	\
+	if (sc->sc_ktrdebug & (_km))		\
+		CTR##_kf(KTR_DEV, __VA_ARGS__);	\
+	} while (0)
+
+extern uint64_t ath_debug;
+
 #define	IFF_DUMPPKTS(sc, m) \
 	((sc->sc_debug & (m)) || \
 	    (sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
@@ -79,7 +107,11 @@
 	u_int ix, int);
 extern	void ath_printtxbuf(struct ath_softc *, const struct ath_buf *bf,
 	u_int qnum, u_int ix, int done);
+extern	void ath_printtxstatbuf(struct ath_softc *sc, const struct ath_buf *bf,
+	const uint32_t *ds, u_int qnum, u_int ix, int done);
 #else	/* ATH_DEBUG */
+#define	ATH_KTR(_sc, _km, _kf, ...)	do { } while (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 {				\

Modified: trunk/sys/dev/ath/if_ath_keycache.c
===================================================================
--- trunk/sys/dev/ath/if_ath_keycache.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_keycache.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -28,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_keycache.c 243318 2012-11-19 23:54:05Z adrian $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -178,7 +179,8 @@
  * cache slots for TKIP with hardware MIC support.
  */
 int
-ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k,
+ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap,
+	const struct ieee80211_key *k,
 	struct ieee80211_node *bss)
 {
 #define	N(a)	(sizeof(a)/sizeof(a[0]))
@@ -212,7 +214,32 @@
 	} else
 		hk.kv_type = HAL_CIPHER_CLR;
 
-	if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) {
+	/*
+	 * If we're installing a clear cipher key and
+	 * the hardware doesn't support that, just succeed.
+	 * Leave it up to the net80211 layer to figure it out.
+	 */
+	if (hk.kv_type == HAL_CIPHER_CLR && sc->sc_hasclrkey == 0) {
+		return (1);
+	}
+
+	/*
+	 * XXX TODO: check this:
+	 * 
+	 * Group keys on hardware that supports multicast frame
+	 * key search should only be done in adhoc/hostap mode,
+	 * not STA mode.
+	 *
+	 * XXX TODO: what about mesh, tdma?
+	 */
+#if 0
+	if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+	     vap->iv_opmode == IEEE80211_M_IBSS) &&
+#else
+	if (
+#endif
+	    (k->wk_flags & IEEE80211_KEY_GROUP) &&
+	    sc->sc_mcastkey) {
 		/*
 		 * Group keys on hardware that supports multicast frame
 		 * key search use a MAC that is the sender's address with
@@ -347,6 +374,14 @@
 #define	N(a)	(sizeof(a)/sizeof(a[0]))
 	u_int i, keyix;
 
+	if (sc->sc_hasclrkey == 0) {
+		/*
+		 * Map to slot 0 for the AR5210.
+		 */
+		*txkeyix = *rxkeyix = 0;
+		return (1);
+	}
+
 	/* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
 	for (i = 0; i < N(sc->sc_keymap); i++) {
 		u_int8_t b = sc->sc_keymap[i];
@@ -493,5 +528,5 @@
 {
 	struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc;
 
-	return ath_keyset(sc, k, vap->iv_bss);
+	return ath_keyset(sc, vap, k, vap->iv_bss);
 }

Modified: trunk/sys/dev/ath/if_ath_keycache.h
===================================================================
--- trunk/sys/dev/ath/if_ath_keycache.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_keycache.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2011 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_ath_keycache.h 227357 2011-11-08 19:25:52Z adrian $
  */
 
 #ifndef	__IF_ATH_CRYPTO_H__
@@ -37,7 +38,7 @@
 extern int ath_key_delete(struct ieee80211vap *, const struct ieee80211_key *);
 extern int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *,
     const u_int8_t mac[IEEE80211_ADDR_LEN]);
-extern int ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k,
-    struct ieee80211_node *bss);
+extern int ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap,
+    const struct ieee80211_key *k, struct ieee80211_node *bss);
 
 #endif

Added: trunk/sys/dev/ath/if_ath_led.c
===================================================================
--- trunk/sys/dev/ath/if_ath_led.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_led.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,191 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_led.c 237611 2012-06-26 22:16:53Z 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().
+ */
+#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_led.h>
+
+/*
+ * Software LED driver routines.
+ */
+
+/*
+ * XXX TODO: move the LED sysctls here.
+ */
+
+
+/*
+ * Configure the hardware for software and LED blinking.
+ * The user may choose to configure part of each, depending upon the
+ * NIC being used.
+ *
+ * This requires the configuration to be set before this function
+ * is called.
+ */
+void
+ath_led_config(struct ath_softc *sc)
+{
+	/* Software LED blinking - GPIO controlled LED */
+	if (sc->sc_softled) {
+		ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin,
+		    HAL_GPIO_OUTPUT_MUX_AS_OUTPUT);
+		ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
+	}
+
+	/* Hardware LED blinking - MAC controlled LED */
+	if (sc->sc_hardled) {
+		/*
+		 * Only enable each LED if required.
+		 *
+		 * Some NICs only have one LED connected; others may
+		 * have GPIO1/GPIO2 connected to other hardware.
+		 */
+		if (sc->sc_led_pwr_pin > 0)
+			ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_led_pwr_pin,
+			    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_OUTPUT_MUX_MAC_NETWORK_LED);
+	}
+}
+
+static void
+ath_led_done(void *arg)
+{
+	struct ath_softc *sc = arg;
+
+	sc->sc_blinking = 0;
+}
+
+/*
+ * Turn the LED off: flip the pin and then set a timer so no
+ * update will happen for the specified duration.
+ */
+static void
+ath_led_off(void *arg)
+{
+	struct ath_softc *sc = arg;
+
+	ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
+	callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc);
+}
+
+/*
+ * Blink the LED according to the specified on/off times.
+ */
+static void
+ath_led_blink(struct ath_softc *sc, int on, int off)
+{
+	DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off);
+	ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon);
+	sc->sc_blinking = 1;
+	sc->sc_ledoff = off;
+	callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc);
+}
+
+void
+ath_led_event(struct ath_softc *sc, int rix)
+{
+	sc->sc_ledevent = ticks;	/* time of last event */
+	if (sc->sc_blinking)		/* don't interrupt active blink */
+		return;
+	ath_led_blink(sc, sc->sc_hwmap[rix].ledon, sc->sc_hwmap[rix].ledoff);
+}


Property changes on: trunk/sys/dev/ath/if_ath_led.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_led.h
===================================================================
--- trunk/sys/dev/ath/if_ath_led.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_led.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,38 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_led.h 228888 2011-12-26 05:46:22Z adrian $
+ */
+#ifndef	__IF_ATH_LED_H__
+#define	__IF_ATH_LED_H__
+
+extern	void ath_led_event(struct ath_softc *sc, int rix);
+extern	void ath_led_config(struct ath_softc *sc);
+
+#endif


Property changes on: trunk/sys/dev/ath/if_ath_led.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_lna_div.c
===================================================================
--- trunk/sys/dev/ath/if_ath_lna_div.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_lna_div.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,1014 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2013 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: stable/10/sys/dev/ath/if_ath_lna_div.c 332320 2018-04-09 12:53:15Z emaste $
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_lna_div.c 332320 2018-04-09 12:53:15Z emaste $");
+
+/*
+ * This module handles LNA diversity for those chips which implement LNA
+ * mixing (AR9285/AR9485.)
+ */
+#include "opt_ath.h"
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/errno.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <sys/socket.h>
+ 
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>		/* XXX for ether_sprintf */
+
+#include <net80211/ieee80211_var.h>
+
+#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_lna_div.h>
+
+/* Linux compability macros */
+/*
+ * XXX these don't handle rounding, underflow, overflow, wrapping!
+ */
+#define	msecs_to_jiffies(a)		( (a) * hz / 1000 )
+
+/*
+ * Methods which are required
+ */
+
+/*
+ * Attach the LNA diversity to the given interface
+ */
+int
+ath_lna_div_attach(struct ath_softc *sc)
+{
+	struct if_ath_ant_comb_state *ss;
+	HAL_ANT_COMB_CONFIG div_ant_conf;
+
+	/* Only do this if diversity is enabled */
+	if (! ath_hal_hasdivantcomb(sc->sc_ah))
+		return (0);
+
+	ss = malloc(sizeof(struct if_ath_ant_comb_state),
+	    M_TEMP, M_WAITOK | M_ZERO);
+	if (ss == NULL) {
+		device_printf(sc->sc_dev, "%s: failed to allocate\n",
+		    __func__);
+		/* Don't fail at this point */
+		return (0);
+	}
+
+	/* Fetch the hardware configuration */
+	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
+	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
+
+	/* Figure out what the hardware specific bits should be */
+	if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
+	    (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
+		ss->lna1_lna2_delta = -9;
+	} else {
+		ss->lna1_lna2_delta = -3;
+	}
+
+	/* Let's flip this on */
+	sc->sc_lna_div = ss;
+	sc->sc_dolnadiv = 1;
+
+	return (0);
+}
+
+/*
+ * Detach the LNA diversity state from the given interface
+ */
+int
+ath_lna_div_detach(struct ath_softc *sc)
+{
+	if (sc->sc_lna_div != NULL) {
+		free(sc->sc_lna_div, M_TEMP);
+		sc->sc_lna_div = NULL;
+	}
+	sc->sc_dolnadiv = 0;
+	return (0);
+}
+
+/*
+ * Enable LNA diversity on the current channel if it's required.
+ */
+int
+ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
+{
+
+	return (0);
+}
+
+/*
+ * Handle ioctl requests from the diagnostic interface.
+ *
+ * The initial part of this code resembles ath_ioctl_diag();
+ * it's likely a good idea to reduce duplication between
+ * these two routines.
+ */
+int
+ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
+{
+	unsigned int id = ad->ad_id & ATH_DIAG_ID;
+	void *indata = NULL;
+	void *outdata = NULL;
+	u_int32_t insize = ad->ad_in_size;
+	u_int32_t outsize = ad->ad_out_size;
+	int error = 0;
+//	int val;
+
+	if (ad->ad_id & ATH_DIAG_IN) {
+		/*
+		 * Copy in data.
+		 */
+		indata = malloc(insize, M_TEMP, M_NOWAIT);
+		if (indata == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
+		error = copyin(ad->ad_in_data, indata, insize);
+		if (error)
+			goto bad;
+	}
+	if (ad->ad_id & ATH_DIAG_DYN) {
+		/*
+		 * Allocate a buffer for the results (otherwise the HAL
+		 * returns a pointer to a buffer where we can read the
+		 * results).  Note that we depend on the HAL leaving this
+		 * pointer for us to use below in reclaiming the buffer;
+		 * may want to be more defensive.
+		 */
+		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
+		if (outdata == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
+	}
+	switch (id) {
+		default:
+			error = EINVAL;
+			goto bad;
+	}
+	if (outsize < ad->ad_out_size)
+		ad->ad_out_size = outsize;
+	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
+		error = EFAULT;
+bad:
+	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
+		free(indata, M_TEMP);
+	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
+		free(outdata, M_TEMP);
+	return (error);
+}
+
+static HAL_BOOL
+ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
+    int main_rssi_avg, int alt_rssi_avg, int pkt_count)
+{
+	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
+		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
+}
+
+static void
+ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
+    HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
+{
+	antcomb->quick_scan_cnt = 0;
+
+	if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
+		antcomb->rssi_lna2 = main_rssi_avg;
+	else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
+		antcomb->rssi_lna1 = main_rssi_avg;
+
+	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
+	case (0x10): /* LNA2 A-B */
+		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->first_quick_scan_conf =
+			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
+		break;
+	case (0x20): /* LNA1 A-B */
+		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->first_quick_scan_conf =
+			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
+		break;
+	case (0x21): /* LNA1 LNA2 */
+		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
+		antcomb->first_quick_scan_conf =
+			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->second_quick_scan_conf =
+			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		break;
+	case (0x12): /* LNA2 LNA1 */
+		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
+		antcomb->first_quick_scan_conf =
+			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->second_quick_scan_conf =
+			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		break;
+	case (0x13): /* LNA2 A+B */
+		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		antcomb->first_quick_scan_conf =
+			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
+		break;
+	case (0x23): /* LNA1 A+B */
+		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+		antcomb->first_quick_scan_conf =
+			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
+    HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
+    int alt_rssi_avg, int alt_ratio)
+{
+	/* alt_good */
+	switch (antcomb->quick_scan_cnt) {
+	case 0:
+		/* set alt to main, and alt to first conf */
+		div_ant_conf->main_lna_conf = antcomb->main_conf;
+		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
+		break;
+	case 1:
+		/* set alt to main, and alt to first conf */
+		div_ant_conf->main_lna_conf = antcomb->main_conf;
+		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
+		antcomb->rssi_first = main_rssi_avg;
+		antcomb->rssi_second = alt_rssi_avg;
+
+		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
+			/* main is LNA1 */
+			if (ath_is_alt_ant_ratio_better(alt_ratio,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+						main_rssi_avg, alt_rssi_avg,
+						antcomb->total_pkt_count))
+				antcomb->first_ratio = AH_TRUE;
+			else
+				antcomb->first_ratio = AH_FALSE;
+		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
+			if (ath_is_alt_ant_ratio_better(alt_ratio,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+						main_rssi_avg, alt_rssi_avg,
+						antcomb->total_pkt_count))
+				antcomb->first_ratio = AH_TRUE;
+			else
+				antcomb->first_ratio = AH_FALSE;
+		} else {
+			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+			    (alt_rssi_avg > main_rssi_avg +
+			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+			    (alt_rssi_avg > main_rssi_avg)) &&
+			    (antcomb->total_pkt_count > 50))
+				antcomb->first_ratio = AH_TRUE;
+			else
+				antcomb->first_ratio = AH_FALSE;
+		}
+		break;
+	case 2:
+		antcomb->alt_good = AH_FALSE;
+		antcomb->scan_not_start = AH_FALSE;
+		antcomb->scan = AH_FALSE;
+		antcomb->rssi_first = main_rssi_avg;
+		antcomb->rssi_third = alt_rssi_avg;
+
+		if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
+			antcomb->rssi_lna1 = alt_rssi_avg;
+		else if (antcomb->second_quick_scan_conf ==
+			 HAL_ANT_DIV_COMB_LNA2)
+			antcomb->rssi_lna2 = alt_rssi_avg;
+		else if (antcomb->second_quick_scan_conf ==
+			 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
+			if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
+				antcomb->rssi_lna2 = main_rssi_avg;
+			else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
+				antcomb->rssi_lna1 = main_rssi_avg;
+		}
+
+		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
+		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
+			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
+		else
+			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
+
+		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
+			if (ath_is_alt_ant_ratio_better(alt_ratio,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+						main_rssi_avg, alt_rssi_avg,
+						antcomb->total_pkt_count))
+				antcomb->second_ratio = AH_TRUE;
+			else
+				antcomb->second_ratio = AH_FALSE;
+		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
+			if (ath_is_alt_ant_ratio_better(alt_ratio,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+						main_rssi_avg, alt_rssi_avg,
+						antcomb->total_pkt_count))
+				antcomb->second_ratio = AH_TRUE;
+			else
+				antcomb->second_ratio = AH_FALSE;
+		} else {
+			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+			    (alt_rssi_avg > main_rssi_avg +
+			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+			    (alt_rssi_avg > main_rssi_avg)) &&
+			    (antcomb->total_pkt_count > 50))
+				antcomb->second_ratio = AH_TRUE;
+			else
+				antcomb->second_ratio = AH_FALSE;
+		}
+
+		/* set alt to the conf with maximun ratio */
+		if (antcomb->first_ratio && antcomb->second_ratio) {
+			if (antcomb->rssi_second > antcomb->rssi_third) {
+				/* first alt*/
+				if ((antcomb->first_quick_scan_conf ==
+				    HAL_ANT_DIV_COMB_LNA1) ||
+				    (antcomb->first_quick_scan_conf ==
+				    HAL_ANT_DIV_COMB_LNA2))
+					/* Set alt LNA1 or LNA2*/
+					if (div_ant_conf->main_lna_conf ==
+					    HAL_ANT_DIV_COMB_LNA2)
+						div_ant_conf->alt_lna_conf =
+							HAL_ANT_DIV_COMB_LNA1;
+					else
+						div_ant_conf->alt_lna_conf =
+							HAL_ANT_DIV_COMB_LNA2;
+				else
+					/* Set alt to A+B or A-B */
+					div_ant_conf->alt_lna_conf =
+						antcomb->first_quick_scan_conf;
+			} else if ((antcomb->second_quick_scan_conf ==
+				   HAL_ANT_DIV_COMB_LNA1) ||
+				   (antcomb->second_quick_scan_conf ==
+				   HAL_ANT_DIV_COMB_LNA2)) {
+				/* Set alt LNA1 or LNA2 */
+				if (div_ant_conf->main_lna_conf ==
+				    HAL_ANT_DIV_COMB_LNA2)
+					div_ant_conf->alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+				else
+					div_ant_conf->alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+			} else {
+				/* Set alt to A+B or A-B */
+				div_ant_conf->alt_lna_conf =
+					antcomb->second_quick_scan_conf;
+			}
+		} else if (antcomb->first_ratio) {
+			/* first alt */
+			if ((antcomb->first_quick_scan_conf ==
+			    HAL_ANT_DIV_COMB_LNA1) ||
+			    (antcomb->first_quick_scan_conf ==
+			    HAL_ANT_DIV_COMB_LNA2))
+					/* Set alt LNA1 or LNA2 */
+				if (div_ant_conf->main_lna_conf ==
+				    HAL_ANT_DIV_COMB_LNA2)
+					div_ant_conf->alt_lna_conf =
+							HAL_ANT_DIV_COMB_LNA1;
+				else
+					div_ant_conf->alt_lna_conf =
+							HAL_ANT_DIV_COMB_LNA2;
+			else
+				/* Set alt to A+B or A-B */
+				div_ant_conf->alt_lna_conf =
+						antcomb->first_quick_scan_conf;
+		} else if (antcomb->second_ratio) {
+				/* second alt */
+			if ((antcomb->second_quick_scan_conf ==
+			    HAL_ANT_DIV_COMB_LNA1) ||
+			    (antcomb->second_quick_scan_conf ==
+			    HAL_ANT_DIV_COMB_LNA2))
+				/* Set alt LNA1 or LNA2 */
+				if (div_ant_conf->main_lna_conf ==
+				    HAL_ANT_DIV_COMB_LNA2)
+					div_ant_conf->alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+				else
+					div_ant_conf->alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+			else
+				/* Set alt to A+B or A-B */
+				div_ant_conf->alt_lna_conf =
+						antcomb->second_quick_scan_conf;
+		} else {
+			/* main is largest */
+			if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
+			    (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
+				/* Set alt LNA1 or LNA2 */
+				if (div_ant_conf->main_lna_conf ==
+				    HAL_ANT_DIV_COMB_LNA2)
+					div_ant_conf->alt_lna_conf =
+							HAL_ANT_DIV_COMB_LNA1;
+				else
+					div_ant_conf->alt_lna_conf =
+							HAL_ANT_DIV_COMB_LNA2;
+			else
+				/* Set alt to A+B or A-B */
+				div_ant_conf->alt_lna_conf = antcomb->main_conf;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
+    int alt_ratio, int alt_ant_ratio_th, u_int config_group,
+    HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
+{
+
+	if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
+		switch ((pdiv_ant_conf->main_lna_conf << 4)
+		    | pdiv_ant_conf->alt_lna_conf) {
+		case (0x01): //A-B LNA2
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x02): //A-B LNA1
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x03): //A-B A+B
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x10): //LNA2 A-B
+			if ((antcomb->scan == 0)
+			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
+				pdiv_ant_conf->fast_div_bias = 0x3f;
+			} else {
+				pdiv_ant_conf->fast_div_bias = 0x1;
+			}
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x12): //LNA2 LNA1
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+			case (0x13): //LNA2 A+B
+			if ((antcomb->scan == 0)
+			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
+				pdiv_ant_conf->fast_div_bias = 0x3f;
+			} else {
+				pdiv_ant_conf->fast_div_bias = 0x1;
+			}
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x20): //LNA1 A-B
+			if ((antcomb->scan == 0)
+			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
+				pdiv_ant_conf->fast_div_bias = 0x3f;
+			} else {
+				pdiv_ant_conf->fast_div_bias = 0x1;
+			}
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x21): //LNA1 LNA2
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x23): //LNA1 A+B
+			if ((antcomb->scan == 0)
+			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
+				pdiv_ant_conf->fast_div_bias = 0x3f;
+			} else {
+				pdiv_ant_conf->fast_div_bias = 0x1;
+			}
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x30): //A+B A-B
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x31): //A+B LNA2
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x32): //A+B LNA1
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		default:
+			break;
+		}
+	} else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
+		switch ((pdiv_ant_conf->main_lna_conf << 4)
+		    | pdiv_ant_conf->alt_lna_conf) {
+		case (0x01): //A-B LNA2
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x02): //A-B LNA1
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x03): //A-B A+B
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x10): //LNA2 A-B
+			if ((antcomb->scan == 0)
+			    && (alt_ratio > alt_ant_ratio_th)) {
+				pdiv_ant_conf->fast_div_bias = 0x1;
+			} else {
+				pdiv_ant_conf->fast_div_bias = 0x2;
+			}
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x12): //LNA2 LNA1
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x13): //LNA2 A+B
+			if ((antcomb->scan == 0)
+			    && (alt_ratio > alt_ant_ratio_th)) {
+				pdiv_ant_conf->fast_div_bias = 0x1;
+			} else {
+				pdiv_ant_conf->fast_div_bias = 0x2;
+			}
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x20): //LNA1 A-B
+			if ((antcomb->scan == 0)
+			    && (alt_ratio > alt_ant_ratio_th)) {
+				pdiv_ant_conf->fast_div_bias = 0x1;
+			} else {
+				pdiv_ant_conf->fast_div_bias = 0x2;
+			}
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x21): //LNA1 LNA2
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x23): //LNA1 A+B
+			if ((antcomb->scan == 0)
+			    && (alt_ratio > alt_ant_ratio_th)) {
+				pdiv_ant_conf->fast_div_bias = 0x1;
+			} else {
+				pdiv_ant_conf->fast_div_bias = 0x2;
+			}
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x30): //A+B A-B
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x31): //A+B LNA2
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		case (0x32): //A+B LNA1
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			pdiv_ant_conf->main_gaintb   = 0;
+			pdiv_ant_conf->alt_gaintb    = 0;
+			break;
+		default:
+			break;
+		}
+	} else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
+		switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
+		case (0x01): //A-B LNA2
+			pdiv_ant_conf->fast_div_bias = 0x3b;
+			break;
+		case (0x02): //A-B LNA1
+			pdiv_ant_conf->fast_div_bias = 0x3d;
+			break;
+		case (0x03): //A-B A+B
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			break;
+		case (0x10): //LNA2 A-B
+			pdiv_ant_conf->fast_div_bias = 0x7;
+			break;
+		case (0x12): //LNA2 LNA1
+			pdiv_ant_conf->fast_div_bias = 0x2;
+			break;
+		case (0x13): //LNA2 A+B
+			pdiv_ant_conf->fast_div_bias = 0x7;
+			break;
+		case (0x20): //LNA1 A-B
+			pdiv_ant_conf->fast_div_bias = 0x6;
+			break;
+		case (0x21): //LNA1 LNA2
+			pdiv_ant_conf->fast_div_bias = 0x0;
+			break;
+		case (0x23): //LNA1 A+B
+			pdiv_ant_conf->fast_div_bias = 0x6;
+			break;
+		case (0x30): //A+B A-B
+			pdiv_ant_conf->fast_div_bias = 0x1;
+			break;
+		case (0x31): //A+B LNA2
+			pdiv_ant_conf->fast_div_bias = 0x3b;
+			break;
+		case (0x32): //A+B LNA1
+			pdiv_ant_conf->fast_div_bias = 0x3d;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * AR9485/AR933x TODO:
+ * + Select a ratio based on whether RSSI is low or not; but I need
+ *   to figure out what "low_rssi_th" is sourced from.
+ * + What's ath_ant_div_comb_alt_check() in the reference driver do?
+ * + .. and there's likely a bunch of other things to include in this.
+ */
+
+/* Antenna diversity and combining */
+void
+ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
+    unsigned long ticks, int hz)
+{
+	HAL_ANT_COMB_CONFIG div_ant_conf;
+	struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
+	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
+	int curr_main_set, curr_bias;
+	int main_rssi = rs->rs_rssi_ctl[0];
+	int alt_rssi = rs->rs_rssi_ctl[1];
+	int rx_ant_conf, main_ant_conf, alt_ant_conf;
+	HAL_BOOL short_scan = AH_FALSE;
+
+	rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
+	main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
+	alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
+
+#if 0
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
+	    "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
+	    "FastDiv: %d\n",
+	    __func__,
+	    main_rssi,
+	    alt_rssi,
+	    main_ant_conf,
+	    alt_ant_conf,
+	    rx_ant_conf,
+	    !!(rs->rs_rssi_ctl[2] & 0x80),
+	    !!(rs->rs_rssi_ctl[2] & 0x40),
+	    !!(rs->rs_rssi_ext[2] & 0x40));
+#endif
+
+	/*
+	 * If LNA diversity combining isn't enabled, don't run this.
+	 */
+	if (! sc->sc_dolnadiv)
+		return;
+
+	/*
+	 * XXX this is ugly, but the HAL code attaches the
+	 * LNA diversity to the TX antenna settings.
+	 * I don't know why.
+	 */
+	if (sc->sc_txantenna != HAL_ANT_VARIABLE)
+		return;
+
+	/* Record packet only when alt_rssi is positive */
+	if (main_rssi > 0 && alt_rssi > 0) {
+		antcomb->total_pkt_count++;
+		antcomb->main_total_rssi += main_rssi;
+		antcomb->alt_total_rssi  += alt_rssi;
+		if (main_ant_conf == rx_ant_conf)
+			antcomb->main_recv_cnt++;
+		else
+			antcomb->alt_recv_cnt++;
+	}
+
+	/* Short scan check */
+	if (antcomb->scan && antcomb->alt_good) {
+		if (time_after(ticks, antcomb->scan_start_time +
+		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
+			short_scan = AH_TRUE;
+		else
+			if (antcomb->total_pkt_count ==
+			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
+				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+					    antcomb->total_pkt_count);
+				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
+					short_scan = AH_TRUE;
+			}
+	}
+
+#if 0
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
+	    "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
+	    __func__,
+	    antcomb->total_pkt_count,
+	    !! (rs->rs_moreaggr),
+	    !! (short_scan));
+#endif
+
+	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
+	    rs->rs_moreaggr) && !short_scan)
+		return;
+
+	if (antcomb->total_pkt_count) {
+		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+			     antcomb->total_pkt_count);
+		main_rssi_avg = (antcomb->main_total_rssi /
+				 antcomb->total_pkt_count);
+		alt_rssi_avg = (antcomb->alt_total_rssi /
+				 antcomb->total_pkt_count);
+	}
+
+	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
+
+	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
+	curr_alt_set = div_ant_conf.alt_lna_conf;
+	curr_main_set = div_ant_conf.main_lna_conf;
+	curr_bias = div_ant_conf.fast_div_bias;
+
+	antcomb->count++;
+
+	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
+		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+			ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
+						  main_rssi_avg);
+			antcomb->alt_good = AH_TRUE;
+		} else {
+			antcomb->alt_good = AH_FALSE;
+		}
+
+		antcomb->count = 0;
+		antcomb->scan = AH_TRUE;
+		antcomb->scan_not_start = AH_TRUE;
+	}
+
+	if (!antcomb->scan) {
+		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+			if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
+				/* Switch main and alt LNA */
+				div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+				div_ant_conf.alt_lna_conf  =
+						HAL_ANT_DIV_COMB_LNA1;
+			} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
+				div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+				div_ant_conf.alt_lna_conf  =
+						HAL_ANT_DIV_COMB_LNA2;
+			}
+
+			goto div_comb_done;
+		} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
+			   (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
+			/* Set alt to another LNA */
+			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
+				div_ant_conf.alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+			else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
+				div_ant_conf.alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+
+			goto div_comb_done;
+		}
+
+		if ((alt_rssi_avg < (main_rssi_avg +
+		    antcomb->lna1_lna2_delta)))
+			goto div_comb_done;
+	}
+
+	if (!antcomb->scan_not_start) {
+		switch (curr_alt_set) {
+		case HAL_ANT_DIV_COMB_LNA2:
+			antcomb->rssi_lna2 = alt_rssi_avg;
+			antcomb->rssi_lna1 = main_rssi_avg;
+			antcomb->scan = AH_TRUE;
+			/* set to A+B */
+			div_ant_conf.main_lna_conf =
+				HAL_ANT_DIV_COMB_LNA1;
+			div_ant_conf.alt_lna_conf  =
+				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+			break;
+		case HAL_ANT_DIV_COMB_LNA1:
+			antcomb->rssi_lna1 = alt_rssi_avg;
+			antcomb->rssi_lna2 = main_rssi_avg;
+			antcomb->scan = AH_TRUE;
+			/* set to A+B */
+			div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
+			div_ant_conf.alt_lna_conf  =
+				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+			break;
+		case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
+			antcomb->rssi_add = alt_rssi_avg;
+			antcomb->scan = AH_TRUE;
+			/* set to A-B */
+			div_ant_conf.alt_lna_conf =
+				HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+			break;
+		case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
+			antcomb->rssi_sub = alt_rssi_avg;
+			antcomb->scan = AH_FALSE;
+			if (antcomb->rssi_lna2 >
+			    (antcomb->rssi_lna1 +
+			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
+				/* use LNA2 as main LNA */
+				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
+				    (antcomb->rssi_add > antcomb->rssi_sub)) {
+					/* set to A+B */
+					div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+					div_ant_conf.alt_lna_conf  =
+						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+				} else if (antcomb->rssi_sub >
+					   antcomb->rssi_lna1) {
+					/* set to A-B */
+					div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+					div_ant_conf.alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+				} else {
+					/* set to LNA1 */
+					div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+					div_ant_conf.alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+				}
+			} else {
+				/* use LNA1 as main LNA */
+				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
+				    (antcomb->rssi_add > antcomb->rssi_sub)) {
+					/* set to A+B */
+					div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+					div_ant_conf.alt_lna_conf  =
+						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+				} else if (antcomb->rssi_sub >
+					   antcomb->rssi_lna1) {
+					/* set to A-B */
+					div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+					div_ant_conf.alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+				} else {
+					/* set to LNA2 */
+					div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+					div_ant_conf.alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	} else {
+		if (!antcomb->alt_good) {
+			antcomb->scan_not_start = AH_FALSE;
+			/* Set alt to another LNA */
+			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
+				div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+				div_ant_conf.alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+			} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
+				div_ant_conf.main_lna_conf =
+						HAL_ANT_DIV_COMB_LNA1;
+				div_ant_conf.alt_lna_conf =
+						HAL_ANT_DIV_COMB_LNA2;
+			}
+			goto div_comb_done;
+		}
+	}
+
+	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
+					   main_rssi_avg, alt_rssi_avg,
+					   alt_ratio);
+
+	antcomb->quick_scan_cnt++;
+
+div_comb_done:
+#if 0
+	ath_ant_div_conf_fast_divbias(&div_ant_conf);
+#endif
+
+	ath_ant_adjust_fast_divbias(antcomb,
+	    alt_ratio,
+	    ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
+	    div_ant_conf.antdiv_configgroup,
+	    &div_ant_conf);
+
+	ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
+
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
+	   __func__, antcomb->total_pkt_count);
+
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
+	   __func__, antcomb->main_total_rssi);
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
+	   __func__, antcomb->alt_total_rssi);
+
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
+	   __func__, main_rssi_avg);
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
+	   __func__, alt_rssi_avg);
+
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
+	   __func__, antcomb->main_recv_cnt);
+	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
+	   __func__, antcomb->alt_recv_cnt);
+
+//	if (curr_alt_set != div_ant_conf.alt_lna_conf)
+		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
+		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
+//	if (curr_main_set != div_ant_conf.main_lna_conf)
+		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
+		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
+//	if (curr_bias != div_ant_conf.fast_div_bias)
+		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
+		    __func__, curr_bias, div_ant_conf.fast_div_bias);
+
+	antcomb->scan_start_time = ticks;
+	antcomb->total_pkt_count = 0;
+	antcomb->main_total_rssi = 0;
+	antcomb->alt_total_rssi = 0;
+	antcomb->main_recv_cnt = 0;
+	antcomb->alt_recv_cnt = 0;
+}
+


Property changes on: trunk/sys/dev/ath/if_ath_lna_div.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_lna_div.h
===================================================================
--- trunk/sys/dev/ath/if_ath_lna_div.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_lna_div.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,90 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2013 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: stable/10/sys/dev/ath/if_ath_lna_div.h 251730 2013-06-14 03:42:10Z adrian $
+ */
+#ifndef	__IF_ATH_LNA_DIV_H__
+#define	__IF_ATH_LNA_DIV_H__
+
+#define	ATH_ANT_RX_CURRENT_SHIFT		4
+#define	ATH_ANT_RX_MAIN_SHIFT			2
+#define	ATH_ANT_RX_MASK				0x3
+
+#define	ATH_ANT_DIV_COMB_SHORT_SCAN_INTR	50
+#define	ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT	0x100
+#define	ATH_ANT_DIV_COMB_MAX_PKTCOUNT		0x200
+#define	ATH_ANT_DIV_COMB_INIT_COUNT		95
+#define	ATH_ANT_DIV_COMB_MAX_COUNT		100
+#define	ATH_ANT_DIV_COMB_ALT_ANT_RATIO		30
+#define	ATH_ANT_DIV_COMB_ALT_ANT_RATIO2		20
+
+#define	ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA	-1
+#define	ATH_ANT_DIV_COMB_LNA1_DELTA_HI		-4
+#define	ATH_ANT_DIV_COMB_LNA1_DELTA_MID		-2
+#define	ATH_ANT_DIV_COMB_LNA1_DELTA_LOW		2
+
+struct if_ath_ant_comb_state {
+	uint16_t count;
+	uint16_t total_pkt_count;
+	HAL_BOOL scan;
+	HAL_BOOL scan_not_start;
+	int main_total_rssi;
+	int alt_total_rssi;
+	int alt_recv_cnt;
+	int main_recv_cnt;
+	int rssi_lna1;
+	int rssi_lna2;
+	int rssi_add;
+	int rssi_sub;
+	int rssi_first;
+	int rssi_second;
+	int rssi_third;
+	HAL_BOOL alt_good;
+	int quick_scan_cnt;
+	int main_conf;
+	HAL_ANT_DIV_COMB_LNA_CONF first_quick_scan_conf;
+	HAL_ANT_DIV_COMB_LNA_CONF second_quick_scan_conf;
+	int first_bias;
+	int second_bias;
+	HAL_BOOL first_ratio;
+	HAL_BOOL second_ratio;
+	unsigned long scan_start_time;
+	int lna1_lna2_delta;
+};
+
+extern	int ath_lna_div_attach(struct ath_softc *sc);
+extern	int ath_lna_div_detach(struct ath_softc *sc);
+extern	int ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad);
+extern	int ath_lna_div_enable(struct ath_softc *sc,
+	    const struct ieee80211_channel *ch);
+
+extern	void ath_lna_rx_comb_scan(struct ath_softc *sc,
+	    struct ath_rx_status *rs, unsigned long ticks, int hz);
+
+#endif	/* __IF_ATH_LNA_DIV_H__ */


Property changes on: trunk/sys/dev/ath/if_ath_lna_div.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/if_ath_misc.h
===================================================================
--- trunk/sys/dev/ath/if_ath_misc.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_misc.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_ath_misc.h 251014 2013-05-26 22:23:39Z adrian $
  */
 #ifndef	__IF_ATH_MISC_H__
 #define	__IF_ATH_MISC_H__
@@ -48,11 +49,103 @@
 	 ((((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,
+	    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 *);
+extern int ath_reset(struct ifnet *, ATH_RESET_TYPE);
+extern void ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf,
+	    int fail);
+extern void ath_tx_update_ratectrl(struct ath_softc *sc,
+	    struct ieee80211_node *ni, struct ath_rc_series *rc,
+	    struct ath_tx_status *ts, int frmlen, int nframes, int nbad);
 
+extern	int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask,
+	    uint32_t *hangs);
+
+extern void ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf,
+    int status);
+extern	void ath_txq_freeholdingbuf(struct ath_softc *sc,
+	    struct ath_txq *txq);
+
+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_alloc_desc(struct ath_softc *sc,
+	    struct ath_descdma *dd, ath_bufhead *head, const char *name,
+	    int ds_size, int ndesc);
+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);
+
+extern	void ath_legacy_attach_comp_func(struct ath_softc *sc);
+
+extern	void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq);
+
+extern	void ath_legacy_tx_drain(struct ath_softc *sc,
+	    ATH_RESET_TYPE reset_type);
+
+extern	void ath_tx_process_buf_completion(struct ath_softc *sc,
+	    struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf);
+
+extern	int ath_stoptxdma(struct ath_softc *sc);
+
+extern	void ath_tx_update_tim(struct ath_softc *sc,
+	    struct ieee80211_node *ni, int enable);
+
+/*
+ * 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);
+extern	void ath_start_task(void *arg, int npending);
+
+extern void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq);
+
+/*
+ * Kick the frame TX task.
+ */
+static inline void
+ath_tx_kick(struct ath_softc *sc)
+{
+
+	/* XXX NULL for now */
+}
+
+/*
+ * Kick the software TX queue task.
+ */
+static inline void
+ath_tx_swq_kick(struct ath_softc *sc)
+{
+
+	taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask);
+}
+
 #endif

Modified: trunk/sys/dev/ath/if_ath_pci.c
===================================================================
--- trunk/sys/dev/ath/if_ath_pci.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_pci.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -28,11 +29,12 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_pci.c 246453 2013-02-07 07:50:16Z adrian $");
 
 /*
  * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver.
  */
+#include "opt_ath.h"
 
 #include <sys/param.h>
 #include <sys/systm.h> 
@@ -60,6 +62,12 @@
 #include <dev/pci/pcivar.h>
 #include <dev/pci/pcireg.h>
 
+/* For EEPROM firmware */
+#ifdef	ATH_EEPROM_FIRMWARE
+#include <sys/linker.h>
+#include <sys/firmware.h>
+#endif	/* ATH_EEPROM_FIRMWARE */
+
 /*
  * PCI glue.
  */
@@ -73,7 +81,50 @@
 
 #define	BS_BAR	0x10
 #define	PCIR_RETRY_TIMEOUT	0x41
+#define	PCIR_CFG_PMCSR		0x48
 
+#define	DEFAULT_CACHESIZE	32
+
+static void
+ath_pci_setup(device_t dev)
+{
+	uint8_t cz;
+
+	/* XXX TODO: need to override the _system_ saved copies of this */
+
+	/*
+	 * If the cache line size is 0, force it to a reasonable
+	 * value.
+	 */
+	cz = pci_read_config(dev, PCIR_CACHELNSZ, 1);
+	if (cz == 0) {
+		pci_write_config(dev, PCIR_CACHELNSZ,
+		    DEFAULT_CACHESIZE / 4, 1);
+	}
+
+	/* Override the system latency timer */
+	pci_write_config(dev, PCIR_LATTIMER, 0xa8, 1);
+
+	/* If a PCI NIC, force wakeup */
+#ifdef	ATH_PCI_WAKEUP_WAR
+	/* XXX TODO: don't do this for non-PCI (ie, PCIe, Cardbus!) */
+	if (1) {
+		uint16_t pmcsr;
+		pmcsr = pci_read_config(dev, PCIR_CFG_PMCSR, 2);
+		pmcsr |= 3;
+		pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2);
+		pmcsr &= ~3;
+		pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2);
+	}
+#endif
+
+	/*
+	 * Disable retry timeout to keep PCI Tx retries from
+	 * interfering with C3 CPU state.
+	 */
+	pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1);
+}
+
 static int
 ath_pci_probe(device_t dev)
 {
@@ -94,6 +145,10 @@
 	struct ath_softc *sc = &psc->sc_sc;
 	int error = ENXIO;
 	int rid;
+#ifdef	ATH_EEPROM_FIRMWARE
+	const struct firmware *fw = NULL;
+	const char *buf;
+#endif
 
 	sc->sc_dev = dev;
 
@@ -103,10 +158,9 @@
 	pci_enable_busmaster(dev);
 
 	/*
-	 * Disable retry timeout to keep PCI Tx retries from
-	 * interfering with C3 CPU state.
+	 * Setup other PCI bus configuration parameters.
 	 */
-	pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1);
+	ath_pci_setup(dev);
 
 	/* 
 	 * Setup memory-mapping of PCI registers.
@@ -163,12 +217,53 @@
 		goto bad3;
 	}
 
+#ifdef	ATH_EEPROM_FIRMWARE
+	/*
+	 * If there's an EEPROM firmware image, load that in.
+	 */
+	if (resource_string_value(device_get_name(dev), device_get_unit(dev),
+	    "eeprom_firmware", &buf) == 0) {
+		if (bootverbose)
+			device_printf(dev, "%s: looking up firmware @ '%s'\n",
+			    __func__, buf);
+
+		fw = firmware_get(buf);
+		if (fw == NULL) {
+			device_printf(dev, "%s: couldn't find firmware\n",
+			    __func__);
+			goto bad3;
+		}
+
+		device_printf(dev, "%s: EEPROM firmware @ %p\n",
+		    __func__, fw->data);
+		sc->sc_eepromdata =
+		    malloc(fw->datasize, M_TEMP, M_WAITOK | M_ZERO);
+		if (! sc->sc_eepromdata) {
+			device_printf(dev, "%s: can't malloc eepromdata\n",
+			    __func__);
+			goto bad3;
+		}
+		memcpy(sc->sc_eepromdata, fw->data, fw->datasize);
+		firmware_put(fw, 0);
+	}
+#endif /* ATH_EEPROM_FIRMWARE */
+
 	ATH_LOCK_INIT(sc);
+	ATH_PCU_LOCK_INIT(sc);
+	ATH_RX_LOCK_INIT(sc);
+	ATH_TX_LOCK_INIT(sc);
+	ATH_TX_IC_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_TX_IC_LOCK_DESTROY(sc);
+	ATH_TX_LOCK_DESTROY(sc);
 	ATH_LOCK_DESTROY(sc);
 	bus_dma_tag_destroy(sc->sc_dmat);
 bad3:
@@ -190,6 +285,11 @@
 	/* check if device was removed */
 	sc->sc_invalid = !bus_child_present(dev);
 
+	/*
+	 * Do a config read to clear pre-existing pci error status.
+	 */
+	(void) pci_read_config(dev, PCIR_COMMAND, 4);
+
 	ath_detach(sc);
 
 	bus_generic_detach(dev);
@@ -199,6 +299,14 @@
 	bus_dma_tag_destroy(sc->sc_dmat);
 	bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr);
 
+	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_TX_IC_LOCK_DESTROY(sc);
+	ATH_TX_LOCK_DESTROY(sc);
 	ATH_LOCK_DESTROY(sc);
 
 	return (0);
@@ -228,6 +336,11 @@
 {
 	struct ath_pci_softc *psc = device_get_softc(dev);
 
+	/*
+	 * Suspend/resume resets the PCI configuration space.
+	 */
+	ath_pci_setup(dev);
+
 	ath_resume(&psc->sc_sc);
 
 	return (0);

Added: trunk/sys/dev/ath/if_ath_rx.c
===================================================================
--- trunk/sys/dev/ath/if_ath_rx.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_rx.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,1262 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_rx.c 251655 2013-06-12 14:52:57Z 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	ATH_DEBUG_ALQ
+#include <dev/ath/if_ath_alq.h>
+#endif
+
+#include <dev/ath/if_ath_lna_div.h>
+
+/*
+ * 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;
+
+	/*
+	 * Enable spectral PHY errors if requested by the
+	 * spectral module.
+	 */
+	if (sc->sc_dospectral)
+		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_NOWAIT, 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;
+	/* These are only populated from the AR9300 or later */
+	sc->sc_rx_th.wr_v.evm[3] = rs->rs_evm3;
+	sc->sc_rx_th.wr_v.evm[4] = rs->rs_evm4;
+
+	/* direction */
+	sc->sc_rx_th.wr_v.vh_flags = ATH_VENDOR_PKT_RX;
+
+	/* RX rate */
+	sc->sc_rx_th.wr_v.vh_rx_hwrate = rs->rs_rate;
+
+	/* RX flags */
+	sc->sc_rx_th.wr_v.vh_rs_flags = rs->rs_flags;
+
+	if (rs->rs_isaggr)
+		sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_ISAGGR;
+	if (rs->rs_moreaggr)
+		sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_MOREAGGR;
+
+	/* phyerr info */
+	if (rs->rs_status & HAL_RXERR_PHY) {
+		sc->sc_rx_th.wr_v.vh_phyerr_code = rs->rs_phyerr;
+		sc->sc_rx_th.wr_v.vh_flags |= ATH_VENDOR_PKT_RXPHYERR;
+	} 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 (rs->rs_status & HAL_RXERR_PHY) {
+		/*
+		 * PHY error - make sure the channel flags
+		 * reflect the actual channel configuration,
+		 * not the received frame.
+		 */
+		if (IEEE80211_IS_CHAN_HT40U(sc->sc_curchan))
+			sc->sc_rx_th.wr_chan_flags |= CHAN_HT40U;
+		else if (IEEE80211_IS_CHAN_HT40D(sc->sc_curchan))
+			sc->sc_rx_th.wr_chan_flags |= CHAN_HT40D;
+		else if (IEEE80211_IS_CHAN_HT20(sc->sc_curchan))
+			sc->sc_rx_th.wr_chan_flags |= CHAN_HT20;
+	} else 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);
+	}
+}
+
+/*
+ * Process a single packet.
+ *
+ * The mbuf must already be synced, unmapped and removed from bf->bf_m
+ * by this stage.
+ *
+ * The mbuf must be consumed by this routine - either passed up the
+ * net80211 stack, put on the holding queue, or freed.
+ */
+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 mbuf *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++;
+	if (rs->rs_flags & HAL_RX_STBC)
+		sc->sc_stats.ast_rx_stbc++;
+#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)) {
+				/* 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)) {
+				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)) {
+			/* NB: bpf needs the mbuf length setup */
+			len = rs->rs_datalen;
+			m->m_pkthdr.len = m->m_len = len;
+			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);
+		}
+		/* XXX pass MIC errors up for s/w reclaculation */
+		m_freem(m); m = NULL;
+		goto rx_next;
+	}
+rx_accept:
+	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;
+		m = NULL;
+		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 */
+	}
+
+	/*
+	 * If this is an AR9285/AR9485, then the receive and LNA
+	 * configuration is stored in RSSI[2] / EXTRSSI[2].
+	 * We can extract this out to build a much better
+	 * receive antenna profile.
+	 *
+	 * Yes, this just blurts over the above RX antenna field
+	 * for now.  It's fine, the AR9285 doesn't really use
+	 * that.
+	 *
+	 * Later on we should store away the fine grained LNA
+	 * information and keep separate counters just for
+	 * that.  It'll help when debugging the AR9285/AR9485
+	 * combined diversity code.
+	 */
+	if (sc->sc_rx_lnamixer) {
+		rs->rs_antenna = 0;
+
+		/* Bits 0:1 - the LNA configuration used */
+		rs->rs_antenna |=
+		    ((rs->rs_rssi_ctl[2] & HAL_RX_LNA_CFG_USED)
+		      >> HAL_RX_LNA_CFG_USED_S);
+
+		/* Bit 2 - the external RX antenna switch */
+		if (rs->rs_rssi_ctl[2] & HAL_RX_LNA_EXTCFG)
+			rs->rs_antenna |= 0x4;
+	}
+
+	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); m = NULL;
+		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);
+		m = NULL;
+		/*
+		 * 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);
+		m = NULL;
+	}
+
+	/*
+	 * At this point we have passed the frame up the stack; thus
+	 * the mbuf is no longer ours.
+	 */
+
+	/*
+	 * 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;
+	}
+
+	/* Handle slow diversity if enabled */
+	if (sc->sc_dolnadiv) {
+		ath_lna_rx_comb_scan(sc, 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:
+	/*
+	 * Debugging - complain if we didn't NULL the mbuf pointer
+	 * here.
+	 */
+	if (m != NULL) {
+		device_printf(sc->sc_dev,
+		    "%s: mbuf %p should've been freed!\n",
+		    __func__,
+		    m);
+	}
+	return (is_good);
+}
+
+#define	ATH_RX_MAX		128
+
+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;
+	int kickpcu = 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++;
+	kickpcu = sc->sc_kickpcu;
+	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 {
+		/*
+		 * Don't process too many packets at a time; give the
+		 * TX thread time to also run - otherwise the TX
+		 * latency can jump by quite a bit, causing throughput
+		 * degredation.
+		 */
+		if (!kickpcu && npkts >= ATH_RX_MAX)
+			break;
+
+		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
+
+#ifdef	ATH_DEBUG_ALQ
+		if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS))
+		    if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS,
+		    sc->sc_rx_statuslen, (char *) ds);
+#endif	/* ATH_DEBUG_ALQ */
+
+		if (status == HAL_EINPROGRESS)
+			break;
+
+		TAILQ_REMOVE(&sc->sc_rxbuf, bf, bf_list);
+		npkts++;
+
+		/*
+		 * Process a single 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;
+		if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf, m))
+			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;
+
+	ATH_KTR(sc, ATH_KTR_RXPROC, 2, "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.
+	 */
+	if (resched && kickpcu) {
+		ATH_PCU_LOCK(sc);
+		ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_rx_proc: kickpcu");
+		device_printf(sc->sc_dev, "%s: kickpcu; handled %d packets\n",
+		    __func__, npkts);
+
+		/*
+		 * Go through the process of fully tearing down
+		 * the RX buffers and reinitialising them.
+		 *
+		 * There's a hardware bug that causes the RX FIFO
+		 * to get confused under certain conditions and
+		 * constantly write over the same frame, leading
+		 * the RX driver code here to get heavily confused.
+		 */
+#if 1
+		ath_startrecv(sc);
+#else
+		/*
+		 * Disabled for now - it'd be nice to be able to do
+		 * this in order to limit the amount of CPU time spent
+		 * reinitialising the RX side (and thus minimise RX
+		 * drops) however there's a hardware issue that
+		 * causes things to get too far out of whack.
+		 */
+		/*
+		 * 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 */
+#endif
+
+		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
+
+	/*
+	 * If we hit the maximum number of frames in this round,
+	 * reschedule for another immediate pass.  This gives
+	 * the TX and TX completion routines time to run, which
+	 * will reduce latency.
+	 */
+	if (npkts >= ATH_RX_MAX)
+		sc->sc_rx.recv_sched(sc, resched);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_rxproc_cnt--;
+	ATH_PCU_UNLOCK(sc);
+}
+
+#undef	ATH_RX_MAX
+
+/*
+ * 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;
+
+	ATH_KTR(sc, ATH_KTR_RXPROC, 1, "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;
+
+	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)
+{
+
+	if (sc->sc_rxdma.dd_desc_len != 0)
+		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+	return (0);
+}
+
+static void
+ath_legacy_recv_sched(struct ath_softc *sc, int dosched)
+{
+
+	taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
+}
+
+static void
+ath_legacy_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE q,
+    int dosched)
+{
+
+	taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
+}
+
+void
+ath_recv_setup_legacy(struct ath_softc *sc)
+{
+
+	/* Sensible legacy defaults */
+	/*
+	 * XXX this should be changed to properly support the
+	 * exact RX descriptor size for each HAL.
+	 */
+	sc->sc_rx_statuslen = sizeof(struct ath_desc);
+
+	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;
+	sc->sc_rx.recv_sched = ath_legacy_recv_sched;
+	sc->sc_rx.recv_sched_queue = ath_legacy_recv_sched_queue;
+}


Property changes on: trunk/sys/dev/ath/if_ath_rx.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_rx.h
===================================================================
--- trunk/sys/dev/ath/if_ath_rx.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_rx.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,66 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_rx.h 249085 2013-04-04 08:21:56Z 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, struct mbuf *m);
+
+extern	void ath_recv_setup_legacy(struct ath_softc *sc);
+
+#endif


Property changes on: trunk/sys/dev/ath/if_ath_rx.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_rx_edma.c
===================================================================
--- trunk/sys/dev/ath/if_ath_rx_edma.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_rx_edma.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,949 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_rx_edma.c 252385 2013-06-29 19:57:57Z 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>
+
+#ifdef	ATH_DEBUG_ALQ
+#include <dev/ath/if_ath_alq.h>
+#endif
+
+/*
+ * 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:
+ *
+ * + Make sure the FIFO is correctly flushed and reinitialised
+ *   through a reset;
+ * + 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	void ath_edma_recv_proc_queue(struct ath_softc *sc,
+	    HAL_RX_QUEUE qtype, int dosched);
+static	int ath_edma_recv_proc_deferred_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_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype,
+    int dosched)
+{
+
+	ath_edma_recv_proc_queue(sc, qtype, dosched);
+	taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
+}
+
+static void
+ath_edma_recv_sched(struct ath_softc *sc, int dosched)
+{
+
+	ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, dosched);
+	ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, dosched);
+	taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
+}
+
+static void
+ath_edma_recv_flush(struct ath_softc *sc)
+{
+
+	DPRINTF(sc, ATH_DEBUG_RECV, "%s: called\n", __func__);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_rxproc_cnt++;
+	ATH_PCU_UNLOCK(sc);
+
+	/*
+	 * Flush any active frames from FIFO -> deferred list
+	 */
+	ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 0);
+	ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 0);
+
+	/*
+	 * Process what's in the deferred queue
+	 */
+	ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 0);
+	ath_edma_recv_proc_deferred_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 into the deferred queue.
+ */
+static void
+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;
+	uint16_t nf;
+	int npkts = 0;
+
+	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 | BUS_DMASYNC_POSTWRITE);
+		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 /* ATH_DEBUG */
+#ifdef	ATH_DEBUG_ALQ
+		if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS))
+			if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_RXSTATUS,
+			    sc->sc_rx_statuslen, (char *) ds);
+#endif /* ATH_DEBUG */
+		if (bf->bf_rxstatus == HAL_EINPROGRESS)
+			break;
+
+		/*
+		 * Completed descriptor.
+		 */
+		DPRINTF(sc, ATH_DEBUG_EDMA_RX,
+		    "%s: Q%d: completed!\n", __func__, qtype);
+		npkts++;
+
+		/*
+		 * We've been synced already, so unmap.
+		 */
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+
+		/*
+		 * Remove the FIFO entry and place it on the completion
+		 * queue.
+		 */
+		re->m_fifo[re->m_fifo_head] = NULL;
+		TAILQ_INSERT_TAIL(&sc->sc_rx_rxlist[qtype], 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);
+
+	/* rx signal state monitoring */
+	ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan);
+
+	ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1,
+	    "ath edma rx proc: npkts=%d\n",
+	    npkts);
+
+	/* Handle resched and kickpcu appropriately */
+	ATH_PCU_LOCK(sc);
+	if (dosched && sc->sc_kickpcu) {
+		ATH_KTR(sc, ATH_KTR_ERROR, 0,
+		    "ath_edma_recv_proc_queue(): kickpcu");
+		if (npkts > 0)
+			device_printf(sc->sc_dev,
+			    "%s: handled npkts %d\n",
+			    __func__, npkts);
+
+		/*
+		 * XXX TODO: what should occur here? Just re-poke and
+		 * re-enable the RX FIFO?
+		 */
+		sc->sc_kickpcu = 0;
+	}
+	ATH_PCU_UNLOCK(sc);
+
+	return;
+}
+
+/*
+ * Flush the deferred queue.
+ *
+ * This destructively flushes the deferred queue - it doesn't
+ * call the wireless stack on each mbuf.
+ */
+static void
+ath_edma_flush_deferred_queue(struct ath_softc *sc)
+{
+	struct ath_buf *bf, *next;
+
+	ATH_RX_LOCK_ASSERT(sc);
+
+	/* Free in one set, inside the lock */
+	TAILQ_FOREACH_SAFE(bf,
+	    &sc->sc_rx_rxlist[HAL_RX_QUEUE_LP], bf_list, next) {
+		/* Free the buffer/mbuf */
+		ath_edma_rxbuf_free(sc, bf);
+	}
+	TAILQ_FOREACH_SAFE(bf,
+	    &sc->sc_rx_rxlist[HAL_RX_QUEUE_HP], bf_list, next) {
+		/* Free the buffer/mbuf */
+		ath_edma_rxbuf_free(sc, bf);
+	}
+}
+
+static int
+ath_edma_recv_proc_deferred_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype,
+    int dosched)
+{
+	int ngood = 0;
+	uint64_t tsf;
+	struct ath_buf *bf, *next;
+	struct ath_rx_status *rs;
+	int16_t nf;
+	ath_bufhead rxlist;
+	struct mbuf *m;
+
+	TAILQ_INIT(&rxlist);
+
+	nf = ath_hal_getchannoise(sc->sc_ah, sc->sc_curchan);
+	/*
+	 * XXX TODO: the NF/TSF should be stamped on the bufs themselves,
+	 * otherwise we may end up adding in the wrong values if this
+	 * is delayed too far..
+	 */
+	tsf = ath_hal_gettsf64(sc->sc_ah);
+
+	/* Copy the list over */
+	ATH_RX_LOCK(sc);
+	TAILQ_CONCAT(&rxlist, &sc->sc_rx_rxlist[qtype], bf_list);
+	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 */
+
+		rs = &bf->bf_status.ds_rxstat;
+		m = bf->bf_m;
+		bf->bf_m = NULL;
+		if (ath_rx_pkt(sc, rs, bf->bf_rxstatus, tsf, nf, qtype, bf, m))
+			ngood++;
+	}
+
+	if (ngood) {
+		sc->sc_lastrx = tsf;
+	}
+
+	ATH_KTR(sc, ATH_KTR_INTERRUPTS, 1,
+	    "ath edma rx deferred proc: ngood=%d\n",
+	    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);
+
+	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);
+
+	ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 1);
+	ath_edma_recv_proc_deferred_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;
+ * + 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);
+
+	m = m_getm(NULL, sc->sc_edma_bufsize, M_NOWAIT, 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;
+
+	/*
+	 * Populate ath_buf fields.
+	 */
+	bf->bf_desc = mtod(m, struct ath_desc *);
+	bf->bf_lastds = bf->bf_desc;	/* XXX only really for TX? */
+	bf->bf_m = m;
+
+	/*
+	 * Zero the descriptor and ensure it makes it out to the
+	 * bounce buffer if one is required.
+	 *
+	 * XXX PREWRITE will copy the whole buffer; we only needed it
+	 * to sync the first 32 DWORDS.  Oh well.
+	 */
+	memset(bf->bf_desc, '\0', sc->sc_rx_statuslen);
+
+	/*
+	 * 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);
+	}
+
+	/*
+	 * Set daddr to the physical mapping page.
+	 */
+	bf->bf_daddr = bf->bf_segs[0].ds_addr;
+
+	/*
+	 * Prepare for the upcoming read.
+	 *
+	 * We need to both sync some data into the buffer (the zero'ed
+	 * descriptor payload) and also prepare for the read that's going
+	 * to occur.
+	 */
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+	/* Finish! */
+	return (0);
+}
+
+/*
+ * Allocate a RX buffer.
+ */
+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) {
+		device_printf(sc->sc_dev, "%s: nothing on rxbuf?!\n",
+		    __func__);
+		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);
+
+	/*
+	 * Only unload the frame if we haven't consumed
+	 * the mbuf via ath_rx_pkt().
+	 */
+	if (bf->bf_m) {
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		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: i=%d, nbufs=%d?\n",
+			    __func__,
+			    qtype,
+			    i,
+			    nbufs);
+			break;
+		}
+
+		re->m_fifo[re->m_fifo_tail] = bf;
+
+		/* Write to the RX FIFO */
+		DPRINTF(sc, ATH_DEBUG_EDMA_RX,
+		    "%s: Q%d: putrxbuf=%p (0x%jx)\n",
+		    __func__,
+		    qtype,
+		    bf->bf_desc,
+		    (uintmax_t) bf->bf_daddr);
+		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)
+{
+
+	ATH_RX_LOCK(sc);
+	ath_edma_flush_deferred_queue(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)
+{
+
+	/* 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;
+
+	sc->sc_rx.recv_sched = ath_edma_recv_sched;
+	sc->sc_rx.recv_sched_queue = ath_edma_recv_sched_queue;
+}


Property changes on: trunk/sys/dev/ath/if_ath_rx_edma.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_rx_edma.h
===================================================================
--- trunk/sys/dev/ath/if_ath_rx_edma.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_rx_edma.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,37 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/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


Property changes on: trunk/sys/dev/ath/if_ath_rx_edma.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_spectral.c
===================================================================
--- trunk/sys/dev/ath/if_ath_spectral.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_spectral.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,290 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2013 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: stable/10/sys/dev/ath/if_ath_spectral.c 332320 2018-04-09 12:53:15Z emaste $
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_spectral.c 332320 2018-04-09 12:53:15Z emaste $");
+
+/*
+ * Implement some basic spectral scan control logic.
+ */
+#include "opt_ath.h"
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/errno.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <sys/socket.h>
+ 
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>		/* XXX for ether_sprintf */
+
+#include <net80211/ieee80211_var.h>
+
+#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_spectral.h>
+
+#include <dev/ath/ath_hal/ah_desc.h>
+
+struct ath_spectral_state {
+	HAL_SPECTRAL_PARAM	spectral_state;
+
+	/*
+	 * Should we enable spectral scan upon
+	 * each network interface reset/change?
+	 *
+	 * This is intended to allow spectral scan
+	 * frame reporting during channel scans.
+	 *
+	 * Later on it can morph into a larger
+	 * scale config method where it pushes
+	 * a "channel scan" config into the hardware
+	 * rather than just the spectral_state
+	 * config.
+	 */
+	int spectral_enable_after_reset;
+};
+
+/*
+ * Methods which are required
+ */
+
+/*
+ * Attach spectral to the given interface
+ */
+int
+ath_spectral_attach(struct ath_softc *sc)
+{
+	struct ath_spectral_state *ss;
+
+	/*
+	 * If spectral isn't supported, don't error - just
+	 * quietly complete.
+	 */
+	if (! ath_hal_spectral_supported(sc->sc_ah))
+		return (0);
+
+	ss = malloc(sizeof(struct ath_spectral_state),
+	    M_TEMP, M_WAITOK | M_ZERO);
+
+	if (ss == NULL) {
+		device_printf(sc->sc_dev, "%s: failed to alloc memory\n",
+		    __func__);
+		return (-ENOMEM);
+	}
+
+	sc->sc_spectral = ss;
+
+	(void) ath_hal_spectral_get_config(sc->sc_ah, &ss->spectral_state);
+
+	return (0);
+}
+
+/*
+ * Detach spectral from the given interface
+ */
+int
+ath_spectral_detach(struct ath_softc *sc)
+{
+
+	if (! ath_hal_spectral_supported(sc->sc_ah))
+		return (0);
+
+	if (sc->sc_spectral != NULL) {
+		free(sc->sc_spectral, M_TEMP);
+	}
+	return (0);
+}
+
+/*
+ * Check whether spectral needs enabling and if so,
+ * flip it on.
+ */
+int
+ath_spectral_enable(struct ath_softc *sc, struct ieee80211_channel *ch)
+{
+	struct ath_spectral_state *ss = sc->sc_spectral;
+
+	/* Default to disable spectral PHY reporting */
+	sc->sc_dospectral = 0;
+
+	if (ss == NULL)
+		return (0);
+
+	if (ss->spectral_enable_after_reset) {
+		ath_hal_spectral_configure(sc->sc_ah,
+		    &ss->spectral_state);
+		(void) ath_hal_spectral_start(sc->sc_ah);
+		sc->sc_dospectral = 1;
+	}
+	return (0);
+}
+
+/*
+ * Handle ioctl requests from the diagnostic interface.
+ *
+ * The initial part of this code resembles ath_ioctl_diag();
+ * it's likely a good idea to reduce duplication between
+ * these two routines.
+ */
+int
+ath_ioctl_spectral(struct ath_softc *sc, struct ath_diag *ad)
+{
+	unsigned int id = ad->ad_id & ATH_DIAG_ID;
+	void *indata = NULL;
+	void *outdata = NULL;
+	u_int32_t insize = ad->ad_in_size;
+	u_int32_t outsize = ad->ad_out_size;
+	int error = 0;
+	HAL_SPECTRAL_PARAM peout;
+	HAL_SPECTRAL_PARAM *pe;
+	struct ath_spectral_state *ss = sc->sc_spectral;
+	int val;
+
+	if (! ath_hal_spectral_supported(sc->sc_ah))
+		return (EINVAL);
+
+	if (ad->ad_id & ATH_DIAG_IN) {
+		/*
+		 * Copy in data.
+		 */
+		indata = malloc(insize, M_TEMP, M_NOWAIT);
+		if (indata == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
+		error = copyin(ad->ad_in_data, indata, insize);
+		if (error)
+			goto bad;
+	}
+	if (ad->ad_id & ATH_DIAG_DYN) {
+		/*
+		 * Allocate a buffer for the results (otherwise the HAL
+		 * returns a pointer to a buffer where we can read the
+		 * results).  Note that we depend on the HAL leaving this
+		 * pointer for us to use below in reclaiming the buffer;
+		 * may want to be more defensive.
+		 */
+		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
+		if (outdata == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
+	}
+	switch (id) {
+		case SPECTRAL_CONTROL_GET_PARAMS:
+			memset(&peout, 0, sizeof(peout));
+			outsize = sizeof(HAL_SPECTRAL_PARAM);
+			ath_hal_spectral_get_config(sc->sc_ah, &peout);
+			pe = (HAL_SPECTRAL_PARAM *) outdata;
+			memcpy(pe, &peout, sizeof(*pe));
+			break;
+		case SPECTRAL_CONTROL_SET_PARAMS:
+			if (insize < sizeof(HAL_SPECTRAL_PARAM)) {
+				error = EINVAL;
+				break;
+			}
+			pe = (HAL_SPECTRAL_PARAM *) indata;
+			ath_hal_spectral_configure(sc->sc_ah, pe);
+			/* Save a local copy of the updated parameters */
+			ath_hal_spectral_get_config(sc->sc_ah,
+			    &ss->spectral_state);
+			break;
+		case SPECTRAL_CONTROL_START:
+			ath_hal_spectral_configure(sc->sc_ah,
+			    &ss->spectral_state);
+			(void) ath_hal_spectral_start(sc->sc_ah);
+			sc->sc_dospectral = 1;
+			/* XXX need to update the PHY mask in the driver */
+			break;
+		case SPECTRAL_CONTROL_STOP:
+			(void) ath_hal_spectral_stop(sc->sc_ah);
+			sc->sc_dospectral = 0;
+			/* XXX need to update the PHY mask in the driver */
+			break;
+		case SPECTRAL_CONTROL_ENABLE_AT_RESET:
+			if (insize < sizeof(int)) {
+				device_printf(sc->sc_dev, "%d != %d\n",
+				    insize,
+				    (int) sizeof(int));
+				error = EINVAL;
+				break;
+			}
+			if (indata == NULL) {
+				device_printf(sc->sc_dev, "indata=NULL\n");
+				error = EINVAL;
+				break;
+			}
+			val = * ((int *) indata);
+			if (val == 0)
+				ss->spectral_enable_after_reset = 0;
+			else
+				ss->spectral_enable_after_reset = 1;
+			break;
+		case SPECTRAL_CONTROL_ENABLE:
+			/* XXX TODO */
+		case SPECTRAL_CONTROL_DISABLE:
+			/* XXX TODO */
+		break;
+		default:
+			error = EINVAL;
+			goto bad;
+	}
+	if (outsize < ad->ad_out_size)
+		ad->ad_out_size = outsize;
+	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
+		error = EFAULT;
+bad:
+	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
+		free(indata, M_TEMP);
+	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
+		free(outdata, M_TEMP);
+	return (error);
+}
+


Property changes on: trunk/sys/dev/ath/if_ath_spectral.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_spectral.h
===================================================================
--- trunk/sys/dev/ath/if_ath_spectral.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_spectral.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,41 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2013 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: stable/10/sys/dev/ath/if_ath_spectral.h 244951 2013-01-02 03:59:02Z adrian $
+ */
+#ifndef	__IF_ATH_SPECTRAL_H__
+#define	__IF_ATH_SPECTRAL_H__
+
+extern	int ath_spectral_attach(struct ath_softc *sc);
+extern	int ath_spectral_detach(struct ath_softc *sc);
+extern	int ath_ioctl_spectral(struct ath_softc *sc, struct ath_diag *ad);
+extern	int ath_spectral_enable(struct ath_softc *sc,
+	    struct ieee80211_channel *ch);
+
+#endif	/* __IF_ATH_SPECTRAL_H__ */


Property changes on: trunk/sys/dev/ath/if_ath_spectral.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/if_ath_sysctl.c
===================================================================
--- trunk/sys/dev/ath/if_ath_sysctl.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_sysctl.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -28,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_sysctl.c 250866 2013-05-21 18:13:57Z adrian $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -90,6 +91,7 @@
 #include <dev/ath/ath_hal/ah_diagcodes.h>
 
 #include <dev/ath/if_ath_debug.h>
+#include <dev/ath/if_ath_led.h>
 #include <dev/ath/if_ath_misc.h>
 #include <dev/ath/if_ath_tx.h>
 #include <dev/ath/if_ath_sysctl.h>
@@ -98,6 +100,10 @@
 #include <dev/ath/ath_tx99/ath_tx99.h>
 #endif
 
+#ifdef	ATH_DEBUG_ALQ
+#include <dev/ath/if_ath_alq.h>
+#endif
+
 static int
 ath_sysctl_slottime(SYSCTL_HANDLER_ARGS)
 {
@@ -151,10 +157,7 @@
 	if (softled != sc->sc_softled) {
 		if (softled) {
 			/* NB: handle any sc_ledpin change */
-			ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin,
-			    HAL_GPIO_MUX_MAC_NETWORK_LED);
-			ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin,
-				!sc->sc_ledon);
+			ath_led_config(sc);
 		}
 		sc->sc_softled = softled;
 	}
@@ -174,10 +177,7 @@
 	if (ledpin != sc->sc_ledpin) {
 		sc->sc_ledpin = ledpin;
 		if (sc->sc_softled) {
-			ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin,
-			    HAL_GPIO_MUX_MAC_NETWORK_LED);
-			ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin,
-				!sc->sc_ledon);
+			ath_led_config(sc);
 		}
 	}
 	return 0;
@@ -184,6 +184,27 @@
 }
 
 static int
+ath_sysctl_hardled(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	int hardled = sc->sc_hardled;
+	int error;
+
+	error = sysctl_handle_int(oidp, &hardled, 0, req);
+	if (error || !req->newptr)
+		return error;
+	hardled = (hardled != 0);
+	if (hardled != sc->sc_hardled) {
+		if (hardled) {
+			/* NB: handle any sc_ledpin change */
+			ath_led_config(sc);
+		}
+		sc->sc_hardled = hardled;
+	}
+	return 0;
+}
+
+static int
 ath_sysctl_txantenna(SYSCTL_HANDLER_ARGS)
 {
 	struct ath_softc *sc = arg1;
@@ -263,7 +284,8 @@
 	if (error || !req->newptr)
 		return error;
 	return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL :
-	    (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0;
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) ?
+	      ath_reset(ifp, ATH_RESET_NOLOSS) : 0;
 }
 
 static int
@@ -295,10 +317,108 @@
 		return 0;
 	if (!ath_hal_setrfkill(ah, rfkill))
 		return EINVAL;
-	return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0;
+	return (ifp->if_drv_flags & IFF_DRV_RUNNING) ?
+	    ath_reset(ifp, ATH_RESET_FULL) : 0;
 }
 
 static int
+ath_sysctl_txagg(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	int i, t, param = 0;
+	int error;
+	struct ath_buf *bf;
+
+	error = sysctl_handle_int(oidp, &param, 0, req);
+	if (error || !req->newptr)
+		return error;
+
+	if (param != 1)
+		return 0;
+
+	printf("no tx bufs (empty list): %d\n", sc->sc_stats.ast_tx_getnobuf);
+	printf("no tx bufs (was busy): %d\n", sc->sc_stats.ast_tx_getbusybuf);
+
+	printf("aggr single packet: %d\n",
+	    sc->sc_aggr_stats.aggr_single_pkt);
+	printf("aggr single packet w/ BAW closed: %d\n",
+	    sc->sc_aggr_stats.aggr_baw_closed_single_pkt);
+	printf("aggr non-baw packet: %d\n",
+	    sc->sc_aggr_stats.aggr_nonbaw_pkt);
+	printf("aggr aggregate packet: %d\n",
+	    sc->sc_aggr_stats.aggr_aggr_pkt);
+	printf("aggr single packet low hwq: %d\n",
+	    sc->sc_aggr_stats.aggr_low_hwq_single_pkt);
+	printf("aggr single packet RTS aggr limited: %d\n",
+	    sc->sc_aggr_stats.aggr_rts_aggr_limited);
+	printf("aggr sched, no work: %d\n",
+	    sc->sc_aggr_stats.aggr_sched_nopkt);
+	for (i = 0; i < 64; i++) {
+		printf("%2d: %10d ", i, sc->sc_aggr_stats.aggr_pkts[i]);
+		if (i % 4 == 3)
+			printf("\n");
+	}
+	printf("\n");
+
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+		if (ATH_TXQ_SETUP(sc, i)) {
+			printf("HW TXQ %d: axq_depth=%d, axq_aggr_depth=%d, "
+			    "axq_fifo_depth=%d, holdingbf=%p\n",
+			    i,
+			    sc->sc_txq[i].axq_depth,
+			    sc->sc_txq[i].axq_aggr_depth,
+			    sc->sc_txq[i].axq_fifo_depth,
+			    sc->sc_txq[i].axq_holdingbf);
+		}
+	}
+
+	i = t = 0;
+	ATH_TXBUF_LOCK(sc);
+	TAILQ_FOREACH(bf, &sc->sc_txbuf, bf_list) {
+		if (bf->bf_flags & ATH_BUF_BUSY) {
+			printf("Busy: %d\n", t);
+			i++;
+		}
+		t++;
+	}
+	ATH_TXBUF_UNLOCK(sc);
+	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);
+
+	ATH_RX_LOCK(sc);
+	for (i = 0; i < 2; i++) {
+		printf("%d: fifolen: %d/%d; head=%d; tail=%d\n",
+		    i,
+		    sc->sc_rxedma[i].m_fifo_depth,
+		    sc->sc_rxedma[i].m_fifolen,
+		    sc->sc_rxedma[i].m_fifo_head,
+		    sc->sc_rxedma[i].m_fifo_tail);
+	}
+	i = 0;
+	TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+		i++;
+	}
+	printf("Total RX buffers in free list: %d buffers\n",
+	    i);
+	ATH_RX_UNLOCK(sc);
+
+	return 0;
+}
+
+static int
 ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS)
 {
 	struct ath_softc *sc = arg1;
@@ -366,7 +486,7 @@
 	 * things in an inconsistent state.
 	 */
 	if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)
-		ath_reset(sc->sc_ifp);
+		ath_reset(sc->sc_ifp, ATH_RESET_NOLOSS);
 
 	return 0;
 }
@@ -387,6 +507,102 @@
 }
 #endif /* IEEE80211_SUPPORT_TDMA */
 
+static int
+ath_sysctl_forcebstuck(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	int val = 0;
+	int error;
+
+	error = sysctl_handle_int(oidp, &val, 0, req);
+	if (error || !req->newptr)
+		return error;
+	if (val == 0)
+		return 0;
+
+	taskqueue_enqueue_fast(sc->sc_tq, &sc->sc_bstucktask);
+	val = 0;
+	return 0;
+}
+
+static int
+ath_sysctl_hangcheck(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	int val = 0;
+	int error;
+	uint32_t mask = 0xffffffff;
+	uint32_t *sp;
+	uint32_t rsize;
+	struct ath_hal *ah = sc->sc_ah;
+
+	error = sysctl_handle_int(oidp, &val, 0, req);
+	if (error || !req->newptr)
+		return error;
+	if (val == 0)
+		return 0;
+
+	/* Do a hang check */
+	if (!ath_hal_getdiagstate(ah, HAL_DIAG_CHECK_HANGS,
+	    &mask, sizeof(mask),
+	    (void *) &sp, &rsize))
+		return (0);
+	device_printf(sc->sc_dev, "%s: sp=0x%08x\n", __func__, *sp);
+
+	val = 0;
+	return 0;
+}
+
+#ifdef ATH_DEBUG_ALQ
+static int
+ath_sysctl_alq_log(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	int error, enable;
+
+	enable = (sc->sc_alq.sc_alq_isactive);
+
+	error = sysctl_handle_int(oidp, &enable, 0, req);
+	if (error || !req->newptr)
+		return (error);
+	else if (enable)
+		error = if_ath_alq_start(&sc->sc_alq);
+	else
+		error = if_ath_alq_stop(&sc->sc_alq);
+	return (error);
+}
+
+/*
+ * Attach the ALQ debugging if required.
+ */
+static void
+ath_sysctl_alq_attach(struct ath_softc *sc)
+{
+	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+	struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
+
+	tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "alq", CTLFLAG_RD,
+	    NULL, "Atheros ALQ logging parameters");
+	child = SYSCTL_CHILDREN(tree);
+
+	SYSCTL_ADD_STRING(ctx, child, OID_AUTO, "filename",
+	    CTLFLAG_RW, sc->sc_alq.sc_alq_filename, 0, "ALQ filename");
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"enable", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_alq_log, "I", "");
+
+	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"debugmask", CTLFLAG_RW, &sc->sc_alq.sc_alq_debug, 0,
+		"ALQ debug mask");
+
+	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"numlost", CTLFLAG_RW, &sc->sc_alq.sc_alq_numlost, 0,
+		"number lost");
+}
+#endif /* ATH_DEBUG_ALQ */
+
 void
 ath_sysctlattach(struct ath_softc *sc)
 {
@@ -401,10 +617,15 @@
 		"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
+#ifdef	ATH_DEBUG_ALQ
+	SYSCTL_ADD_QUAD(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ktrdebug", CTLFLAG_RW, &sc->sc_ktrdebug,
+		"control debugging KTR");
+#endif /* ATH_DEBUG_ALQ */
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"slottime", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
 		ath_sysctl_slottime, "I", "802.11 slot time (us)");
@@ -414,6 +635,7 @@
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"ctstimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
 		ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)");
+
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
 		ath_sysctl_softled, "I", "enable/disable software LED support");
@@ -426,7 +648,19 @@
 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
 		"idle time for inactivity LED (ticks)");
+
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"hardled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_hardled, "I", "enable/disable hardware LED support");
+	/* XXX Laziness - configure pins, then flip hardled off/on */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"led_net_pin", CTLFLAG_RW, &sc->sc_led_net_pin, 0,
+		"MAC Network LED pin, or -1 to disable");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"led_pwr_pin", CTLFLAG_RW, &sc->sc_led_pwr_pin, 0,
+		"MAC Power LED pin, or -1 to disable");
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"txantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
 		ath_sysctl_txantenna, "I", "antenna switch");
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
@@ -465,6 +699,19 @@
 			"rfkill", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
 			ath_sysctl_rfkill, "I", "enable/disable RF kill switch");
 	}
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"txagg", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_txagg, "I", "");
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"forcebstuck", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_forcebstuck, "I", "");
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"hangcheck", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_hangcheck, "I", "");
+
 	if (ath_hal_hasintmit(ah)) {
 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 			"intmit", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
@@ -474,6 +721,53 @@
 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"monpass", CTLFLAG_RW, &sc->sc_monpass, 0,
 		"mask of error frames to pass when monitoring");
+
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"hwq_limit_nonaggr", CTLFLAG_RW, &sc->sc_hwq_limit_nonaggr, 0,
+		"Hardware non-AMPDU queue depth before software-queuing TX frames");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"hwq_limit_aggr", CTLFLAG_RW, &sc->sc_hwq_limit_aggr, 0,
+		"Hardware AMPDU queue depth before software-queuing TX frames");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"tid_hwq_lo", CTLFLAG_RW, &sc->sc_tid_hwq_lo, 0,
+		"");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"tid_hwq_hi", CTLFLAG_RW, &sc->sc_tid_hwq_hi, 0,
+		"");
+
+	/* Aggregate length twiddles */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"aggr_limit", CTLFLAG_RW, &sc->sc_aggr_limit, 0,
+		"Maximum A-MPDU size, or 0 for 'default'");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"rts_aggr_limit", CTLFLAG_RW, &sc->sc_rts_aggr_limit, 0,
+		"Maximum A-MPDU size for RTS-protected frames, or '0' "
+		"for default");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"delim_min_pad", CTLFLAG_RW, &sc->sc_delim_min_pad, 0,
+		"Enforce a minimum number of delimiters per A-MPDU "
+		" sub-frame");
+
+	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");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"txq_mcastq_maxdepth", CTLFLAG_RW,
+		&sc->sc_txq_mcastq_maxdepth, 0,
+		"Maximum buffer depth for multicast/broadcast frames");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"txq_node_maxdepth", CTLFLAG_RW,
+		&sc->sc_txq_node_maxdepth, 0,
+		"Maximum buffer depth for a single node");
+
+#if 0
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"cabq_enable", CTLFLAG_RW,
+		&sc->sc_cabq_enable, 0,
+		"Whether to transmit on the CABQ or not");
+#endif
+
 #ifdef IEEE80211_SUPPORT_TDMA
 	if (ath_hal_macversion(ah) > 0x78) {
 		sc->sc_tdmadbaprep = 2;
@@ -495,6 +789,10 @@
 			ath_sysctl_setcca, "I", "enable CCA control");
 	}
 #endif
+
+#ifdef	ATH_DEBUG_ALQ
+	ath_sysctl_alq_attach(sc);
+#endif
 }
 
 static int
@@ -510,6 +808,9 @@
 	if (val == 0)
 		return 0;       /* Not clearing the stats is still valid */
 	memset(&sc->sc_stats, 0, sizeof(sc->sc_stats));
+	memset(&sc->sc_aggr_stats, 0, sizeof(sc->sc_aggr_stats));
+	memset(&sc->sc_intr_stats, 0, sizeof(sc->sc_intr_stats));
+
 	val = 0;
 	return 0;
 }
@@ -531,6 +832,26 @@
 	}
 }
 
+static void
+ath_sysctl_stats_attach_intr(struct ath_softc *sc,
+    struct sysctl_oid_list *parent)
+{
+	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+	struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
+	int i;
+	char sn[8];
+
+	tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "sync_intr",
+	    CTLFLAG_RD, NULL, "Sync interrupt statistics");
+	child = SYSCTL_CHILDREN(tree);
+	for (i = 0; i < 32; i++) {
+		snprintf(sn, sizeof(sn), "%d", i);
+		SYSCTL_ADD_UINT(ctx, child, OID_AUTO, sn, CTLFLAG_RD,
+		    &sc->sc_intr_stats.sync_intr[i], 0, "");
+	}
+}
+
 void
 ath_sysctl_stats_attach(struct ath_softc *sc)
 {
@@ -729,9 +1050,45 @@
 	    &sc->sc_stats.ast_tx_timerexpired, 0, "TX exceeded TX_TIMER register");
 	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_desccfgerr", CTLFLAG_RD,
 	    &sc->sc_stats.ast_tx_desccfgerr, 0, "TX Descriptor Cfg Error");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swretries", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_swretries, 0, "TX software retry count");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swretrymax", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_swretrymax, 0, "TX software retry max reached");
 
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_data_underrun", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_data_underrun, 0, "");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_delim_underrun", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_delim_underrun, 0, "");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_failall", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_aggr_failall, 0,
+	    "Number of aggregate TX failures (whole frame)");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_ok", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_aggr_ok, 0,
+	    "Number of aggregate TX OK completions (subframe)");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_aggr_fail", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_aggr_fail, 0,
+	    "Number of aggregate TX failures (subframe)");
+
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_intr", CTLFLAG_RD,
+	    &sc->sc_stats.ast_rx_intr, 0, "RX interrupts");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_intr", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_intr, 0, "TX interrupts");
+	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, "");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_tx_swfiltered", CTLFLAG_RD,
+	    &sc->sc_stats.ast_tx_swfiltered, 0, "");
+	SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "ast_rx_stbc",
+	    CTLFLAG_RD, &sc->sc_stats.ast_rx_stbc, 0,
+	    "Number of STBC frames received");
+	
 	/* Attach the RX phy error array */
 	ath_sysctl_stats_attach_rxphyerr(sc, child);
+
+	/* Attach the interrupt statistics array */
+	ath_sysctl_stats_attach_intr(sc, child);
 }
 
 /*
@@ -772,4 +1129,16 @@
 	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "swba_backoff", CTLFLAG_RW,
 	    &sc->sc_ah->ah_config.ah_additional_swba_backoff, 0,
 	    "Atheros HAL additional SWBA backoff time");
+
+	sc->sc_ah->ah_config.ah_force_full_reset = 0;
+	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "force_full_reset", CTLFLAG_RW,
+	    &sc->sc_ah->ah_config.ah_force_full_reset, 0,
+	    "Force full chip reset rather than a warm reset");
+
+	/*
+	 * This is initialised by the driver.
+	 */
+	SYSCTL_ADD_INT(ctx, child, OID_AUTO, "serialise_reg_war", CTLFLAG_RW,
+	    &sc->sc_ah->ah_config.ah_serialise_reg_war, 0,
+	    "Force register access serialisation");
 }

Modified: trunk/sys/dev/ath/if_ath_sysctl.h
===================================================================
--- trunk/sys/dev/ath/if_ath_sysctl.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_sysctl.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_ath_sysctl.h 223459 2011-06-23 02:38:36Z adrian $
  */
 
 #ifndef	__IF_ATH_SYSCTL_H__

Added: trunk/sys/dev/ath/if_ath_tdma.c
===================================================================
--- trunk/sys/dev/ath/if_ath_tdma.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_tdma.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,684 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_tdma.c 250865 2013-05-21 18:02:54Z 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	ATH_DEBUG_ALQ
+#include <dev/ath/if_ath_alq.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;
+#if 0
+	DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
+	    "%s: intval=%d (0x%08x) nexttbtt=%u (0x%08x), nextdba=%u (0x%08x), nextswba=%u (0x%08x),nextatim=%u (0x%08x)\n",
+	    __func__,
+	    bt.bt_intval,
+	    bt.bt_intval,
+	    bt.bt_nexttbtt,
+	    bt.bt_nexttbtt,
+	    bt.bt_nextdba,
+	    bt.bt_nextdba,
+	    bt.bt_nextswba,
+	    bt.bt_nextswba,
+	    bt.bt_nextatim,
+	    bt.bt_nextatim);
+#endif
+
+#ifdef	ATH_DEBUG_ALQ
+	if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_SET)) {
+		struct if_ath_alq_tdma_timer_set t;
+		t.bt_intval = htobe32(bt.bt_intval);
+		t.bt_nexttbtt = htobe32(bt.bt_nexttbtt);
+		t.bt_nextdba = htobe32(bt.bt_nextdba);
+		t.bt_nextswba = htobe32(bt.bt_nextswba);
+		t.bt_nextatim = htobe32(bt.bt_nextatim);
+		t.bt_flags = htobe32(bt.bt_flags);
+		t.sc_tdmadbaprep = htobe32(sc->sc_tdmadbaprep);
+		t.sc_tdmaswbaprep = htobe32(sc->sc_tdmaswbaprep);
+		if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_SET,
+		    sizeof(t), (char *) &t);
+	}
+#endif
+
+	DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
+	    "%s: nexttbtt=%u (0x%08x), nexttbtt tsf=%lld (0x%08llx)\n",
+	    __func__,
+	    bt.bt_nexttbtt,
+	    bt.bt_nexttbtt,
+	    (long long) ( ((u_int64_t) (bt.bt_nexttbtt)) << 10),
+	    (long long) ( ((u_int64_t) (bt.bt_nexttbtt)) << 10));
+	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);
+
+	/*
+	 * If the chip supports enforcing TxOP on transmission,
+	 * we can just delete the guard window.  It isn't at all required.
+	 */
+	if (sc->sc_hasenforcetxop) {
+		sc->sc_tdmaguard = 0;
+	} else {
+		/* XXX short preamble assumed */
+		/* XXX non-11n rate 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);
+
+#ifdef	ATH_DEBUG_ALQ
+	if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_CONFIG)) {
+		struct if_ath_alq_tdma_timer_config t;
+
+		t.tdma_slot = htobe32(tdma->tdma_slot);
+		t.tdma_slotlen = htobe32(tdma->tdma_slotlen);
+		t.tdma_slotcnt = htobe32(tdma->tdma_slotcnt);
+		t.tdma_bintval = htobe32(tdma->tdma_bintval);
+		t.tdma_guard = htobe32(sc->sc_tdmaguard);
+		t.tdma_scbintval = htobe32(sc->sc_tdmabintval);
+		t.tdma_dbaprep = htobe32(sc->sc_tdmadbaprep);
+
+		if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TIMER_CONFIG,
+		    sizeof(t), (char *) &t);
+	}
+#endif	/* ATH_DEBUG_ALQ */
+}
+
+/*
+ * 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.
+ *
+ * XXX TODO: since the changes for the AR5416 and later chips
+ * involved changing the TSF/TU calculations, we need to make
+ * sure that various calculations wrap consistently.
+ *
+ * A lot of the problems stemmed from the calculations wrapping
+ * at 65,535 TU.  Since a lot of the math is still being done in
+ * TU, please audit it to ensure that when the TU values programmed
+ * into the timers wrap at (2^31)-1 TSF, all the various terms
+ * wrap consistently.
+ */
+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, nexttbtt_full;
+	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];
+
+	/*
+	 * To calculate the packet duration for legacy rates, we
+	 * only need the rix and preamble.
+	 *
+	 * For 11n non-aggregate frames, we also need the channel
+	 * width and short/long guard interval.
+	 *
+	 * For 11n aggregate frames, the required hacks are a little
+	 * more subtle.  You need to figure out the frame duration
+	 * for each frame, including the delimiters.  However, when
+	 * a frame isn't received successfully, we won't hear it
+	 * (unless you enable reception of CRC errored frames), so
+	 * your duration calculation is going to be off.
+	 *
+	 * However, we can assume that the beacon frames won't be
+	 * transmitted as aggregate frames, so we should be okay.
+	 * Just add a check to ensure that we aren't handed something
+	 * bad.
+	 *
+	 * For ath_hal_pkt_txtime() - for 11n rates, shortPreamble is
+	 * actually short guard interval. For legacy rates,
+	 * it's short preamble.
+	 */
+	txtime = ath_hal_pkt_txtime(ah, rt, rs->rs_datalen,
+	    rix,
+	    !! (rs->rs_flags & HAL_RX_2040),
+	    (rix & 0x80) ?
+	      (! (rs->rs_flags & HAL_RX_GI)) : rt->info[rix].shortPreamble);
+	/* NB: << 9 is to cvt to TU and /2 */
+	nextslot = (rstamp - txtime) + (sc->sc_tdmabintval << 9);
+
+	/*
+	 * For 802.11n chips: nextslottu needs to be the full TSF space,
+	 * not just 0..65535 TU.
+	 */
+	nextslottu = TSF_TO_TU(nextslot>>32, nextslot);
+	/*
+	 * 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.
+	 */
+	/*
+	 * The logic here assumes the nexttbtt counter is in TSF
+	 * but the prr-11n NICs are in TU.  The HAL shifts them
+	 * to TSF but there's two important differences:
+	 *
+	 * + The TU->TSF values have 0's for the low 9 bits, and
+	 * + The counter wraps at TU_TO_TSF(HAL_BEACON_PERIOD + 1) for
+	 *   the pre-11n NICs, but not for the 11n NICs.
+	 *
+	 * So for now, just make sure the nexttbtt value we get
+	 * matches the second issue or once nexttbtt exceeds this
+	 * value, tsfdelta ends up becoming very negative and all
+	 * of the adjustments get very messed up.
+	 */
+
+	/*
+	 * We need to track the full nexttbtt rather than having it
+	 * truncated at HAL_BEACON_PERIOD, as programming the
+	 * nexttbtt (and related) registers for the 11n chips is
+	 * actually going to take the full 32 bit space, rather than
+	 * just 0..65535 TU.
+	 */
+	nexttbtt_full = ath_hal_getnexttbtt(ah);
+	nexttbtt = nexttbtt_full % (TU_TO_TSF(HAL_BEACON_PERIOD + 1));
+	tsfdelta = (int32_t)((nextslot % TU_TO_TSF(HAL_BEACON_PERIOD + 1)) - nexttbtt);
+
+	DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
+	    "rs->rstamp %llu rstamp %llu tsf %llu txtime %d, nextslot %llu, "
+	    "nextslottu %d, nextslottume %d\n",
+	    (unsigned long long) rs->rs_tstamp, rstamp, tsf, txtime,
+	    nextslot, nextslottu, TSF_TO_TU(nextslot >> 32, nextslot));
+	DPRINTF(sc, ATH_DEBUG_TDMA,
+	    "  beacon tstamp: %llu (0x%016llx)\n",
+	    le64toh(ni->ni_tstamp.tsf),
+	    le64toh(ni->ni_tstamp.tsf));
+
+	DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
+	    "nexttbtt %llu (0x%08llx) tsfdelta %d avg +%d/-%d\n",
+	    nexttbtt,
+	    (long long) nexttbtt,
+	    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_full >> 32, nexttbtt_full);
+
+#ifdef	ATH_DEBUG_ALQ
+	if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_BEACON_STATE)) {
+		struct if_ath_alq_tdma_beacon_state t;
+		t.rx_tsf = htobe64(rstamp);
+		t.beacon_tsf = htobe64(le64toh(ni->ni_tstamp.tsf));
+		t.tsf64 = htobe64(tsf);
+		t.nextslot_tsf = htobe64(nextslot);
+		t.nextslot_tu = htobe32(nextslottu);
+		t.txtime = htobe32(txtime);
+		if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_BEACON_STATE,
+		    sizeof(t), (char *) &t);
+	}
+
+	if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_TDMA_SLOT_CALC)) {
+		struct if_ath_alq_tdma_slot_calc t;
+
+		t.nexttbtt = htobe64(nexttbtt_full);
+		t.next_slot = htobe64(nextslot);
+		t.tsfdelta = htobe32(tsfdelta);
+		t.avg_plus = htobe32(TDMA_AVG(sc->sc_avgtsfdeltap));
+		t.avg_minus = htobe32(TDMA_AVG(sc->sc_avgtsfdeltam));
+
+		if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_SLOT_CALC,
+		    sizeof(t), (char *) &t);
+	}
+#endif
+
+	/*
+	 * 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)) {
+		DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
+		    "%s: calling ath_tdma_settimers; nextslottu=%d, bintval=%d\n",
+		    __func__,
+		    nextslottu,
+		    sc->sc_tdmabintval);
+		ath_tdma_settimers(sc, nextslottu, sc->sc_tdmabintval);
+		sc->sc_stats.ast_tdma_timers++;
+	}
+	if (tsfdelta > 0) {
+		uint64_t tsf;
+
+		/* XXX should just teach ath_hal_adjusttsf() to do this */
+		tsf = ath_hal_gettsf64(ah);
+		ath_hal_settsf64(ah, tsf + tsfdelta);
+		DPRINTF(sc, ATH_DEBUG_TDMA_TIMER,
+		    "%s: calling ath_hal_adjusttsf: TSF=%llu, tsfdelta=%d\n",
+		    __func__,
+		    tsf,
+		    tsfdelta);
+
+#ifdef	ATH_DEBUG_ALQ
+		if (if_ath_alq_checkdebug(&sc->sc_alq,
+		    ATH_ALQ_TDMA_TSF_ADJUST)) {
+			struct if_ath_alq_tdma_tsf_adjust t;
+
+			t.tsfdelta = htobe32(tsfdelta);
+			t.tsf64_old = htobe64(tsf);
+			t.tsf64_new = htobe64(tsf + tsfdelta);
+			if_ath_alq_post(&sc->sc_alq, ATH_ALQ_TDMA_TSF_ADJUST,
+			    sizeof(t), (char *) &t);
+		}
+#endif	/* ATH_DEBUG_ALQ */
+		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);
+	/* XXX We don't do cabq traffic, but just for completeness .. */
+	ATH_TXQ_LOCK(sc->sc_cabq);
+	ath_beacon_cabq_start(sc);
+	ATH_TXQ_UNLOCK(sc->sc_cabq);
+
+	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 ((! sc->sc_isedma) &&
+		    (! 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 */


Property changes on: trunk/sys/dev/ath/if_ath_tdma.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_tdma.h
===================================================================
--- trunk/sys/dev/ath/if_ath_tdma.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_tdma.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,56 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/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


Property changes on: trunk/sys/dev/ath/if_ath_tdma.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_tsf.h
===================================================================
--- trunk/sys/dev/ath/if_ath_tsf.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_tsf.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,82 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/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


Property changes on: trunk/sys/dev/ath/if_ath_tsf.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/if_ath_tx.c
===================================================================
--- trunk/sys/dev/ath/if_ath_tx.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_tx.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,5 +1,7 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * Copyright (c) 2010-2012 Adrian Chadd, Xenion Pty Ltd
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_tx.c 262998 2014-03-11 05:58:52Z rpaulo $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -77,6 +79,7 @@
 #ifdef IEEE80211_SUPPORT_TDMA
 #include <net80211/ieee80211_tdma.h>
 #endif
+#include <net80211/ieee80211_ht.h>
 
 #include <net/bpf.h>
 
@@ -99,15 +102,140 @@
 #include <dev/ath/if_ath_tx.h>
 #include <dev/ath/if_ath_tx_ht.h>
 
+#ifdef	ATH_DEBUG_ALQ
+#include <dev/ath/if_ath_alq.h>
+#endif
+
 /*
+ * How many retries to perform in software
+ */
+#define	SWMAX_RETRIES		10
+
+/*
+ * What queue to throw the non-QoS TID traffic into
+ */
+#define	ATH_NONQOS_TID_AC	WME_AC_VO
+
+#if 0
+static int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an);
+#endif
+static int ath_tx_ampdu_pending(struct ath_softc *sc, struct ath_node *an,
+    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 struct ath_buf *
+ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid, struct ath_buf *bf);
+
+#ifdef	ATH_DEBUG_ALQ
+void
+ath_tx_alq_post(struct ath_softc *sc, struct ath_buf *bf_first)
+{
+	struct ath_buf *bf;
+	int i, n;
+	const char *ds;
+
+	/* XXX we should skip out early if debugging isn't enabled! */
+	bf = bf_first;
+
+	while (bf != NULL) {
+		/* XXX should ensure bf_nseg > 0! */
+		if (bf->bf_nseg == 0)
+			break;
+		n = ((bf->bf_nseg - 1) / sc->sc_tx_nmaps) + 1;
+		for (i = 0, ds = (const char *) bf->bf_desc;
+		    i < n;
+		    i++, ds += sc->sc_tx_desclen) {
+			if_ath_alq_post(&sc->sc_alq,
+			    ATH_ALQ_EDMA_TXDESC,
+			    sc->sc_tx_desclen,
+			    ds);
+		}
+		bf = bf->bf_next;
+	}
+}
+#endif /* ATH_DEBUG_ALQ */
+
+/*
  * Whether to use the 11n rate scenario functions or not
  */
 static inline int
 ath_tx_is_11n(struct ath_softc *sc)
 {
-	return (sc->sc_ah->ah_magic == 0x20065416);
+	return ((sc->sc_ah->ah_magic == 0x20065416) ||
+		    (sc->sc_ah->ah_magic == 0x19741014));
 }
 
+/*
+ * Obtain the current TID from the given frame.
+ *
+ * Non-QoS frames need to go into TID 16 (IEEE80211_NONQOS_TID.)
+ * This has implications for which AC/priority the packet is placed
+ * in.
+ */
+static int
+ath_tx_gettid(struct ath_softc *sc, const struct mbuf *m0)
+{
+	const struct ieee80211_frame *wh;
+	int pri = M_WME_GETAC(m0);
+
+	wh = mtod(m0, const struct ieee80211_frame *);
+	if (! IEEE80211_QOS_HAS_SEQ(wh))
+		return IEEE80211_NONQOS_TID;
+	else
+		return WME_AC_TO_TID(pri);
+}
+
+static void
+ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ieee80211_frame *wh;
+
+	wh = mtod(bf->bf_m, struct ieee80211_frame *);
+	/* Only update/resync if needed */
+	if (bf->bf_state.bfs_isretried == 0) {
+		wh->i_fc[1] |= IEEE80211_FC1_RETRY;
+		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+		    BUS_DMASYNC_PREWRITE);
+	}
+	bf->bf_state.bfs_isretried = 1;
+	bf->bf_state.bfs_retries ++;
+}
+
+/*
+ * Determine what the correct AC queue for the given frame
+ * should be.
+ *
+ * This code assumes that the TIDs map consistently to
+ * the underlying hardware (or software) ath_txq.
+ * Since the sender may try to set an AC which is
+ * arbitrary, non-QoS TIDs may end up being put on
+ * completely different ACs. There's no way to put a
+ * TID into multiple ath_txq's for scheduling, so
+ * for now we override the AC/TXQ selection and set
+ * non-QOS TID frames into the BE queue.
+ *
+ * This may be completely incorrect - specifically,
+ * some management frames may end up out of order
+ * compared to the QoS traffic they're controlling.
+ * I'll look into this later.
+ */
+static int
+ath_tx_getac(struct ath_softc *sc, const struct mbuf *m0)
+{
+	const struct ieee80211_frame *wh;
+	int pri = M_WME_GETAC(m0);
+	wh = mtod(m0, const struct ieee80211_frame *);
+	if (IEEE80211_QOS_HAS_SEQ(wh))
+		return pri;
+
+	return ATH_NONQOS_TID_AC;
+}
+
 void
 ath_txfrag_cleanup(struct ath_softc *sc,
 	ath_bufhead *frags, struct ieee80211_node *ni)
@@ -116,10 +244,10 @@
 
 	ATH_TXBUF_LOCK_ASSERT(sc);
 
-	STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) {
+	TAILQ_FOREACH_SAFE(bf, frags, bf_list, next) {
 		/* NB: bf assumed clean */
-		STAILQ_REMOVE_HEAD(frags, bf_list);
-		STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+		TAILQ_REMOVE(frags, bf, bf_list);
+		ath_returnbuf_head(sc, bf);
 		ieee80211_node_decref(ni);
 	}
 }
@@ -138,17 +266,20 @@
 
 	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 */
+			DPRINTF(sc, ATH_DEBUG_XMIT, "%s: no buffer?\n",
+			    __func__);
 			ath_txfrag_cleanup(sc, frags, ni);
 			break;
 		}
 		ieee80211_node_incref(ni);
-		STAILQ_INSERT_TAIL(frags, bf, bf_list);
+		TAILQ_INSERT_TAIL(frags, bf, bf_list);
 	}
 	ATH_TXBUF_UNLOCK(sc);
 
-	return !STAILQ_EMPTY(frags);
+	return !TAILQ_EMPTY(frags);
 }
 
 /*
@@ -182,7 +313,7 @@
 				     BUS_DMA_NOWAIT);
 	if (error == EFBIG) {
 		/* XXX packet requires too many descriptors */
-		bf->bf_nseg = ATH_TXDESC+1;
+		bf->bf_nseg = ATH_MAX_SCATTER + 1;
 	} else if (error != 0) {
 		sc->sc_stats.ast_tx_busdma++;
 		ath_freetx(m0);
@@ -193,9 +324,9 @@
 	 * require too many TX descriptors.  We try to convert
 	 * the latter to a cluster.
 	 */
-	if (bf->bf_nseg > ATH_TXDESC) {		/* too many desc's, linearize */
+	if (bf->bf_nseg > ATH_MAX_SCATTER) {		/* too many desc's, linearize */
 		sc->sc_stats.ast_tx_linear++;
-		m = m_collapse(m0, M_NOWAIT, ATH_TXDESC);
+		m = m_collapse(m0, M_NOWAIT, ATH_MAX_SCATTER);
 		if (m == NULL) {
 			ath_freetx(m0);
 			sc->sc_stats.ast_tx_nombuf++;
@@ -210,7 +341,7 @@
 			ath_freetx(m0);
 			return error;
 		}
-		KASSERT(bf->bf_nseg <= ATH_TXDESC,
+		KASSERT(bf->bf_nseg <= ATH_MAX_SCATTER,
 		    ("too many segments after defrag; nseg %u", bf->bf_nseg));
 	} else if (bf->bf_nseg == 0) {		/* null packet, discard */
 		sc->sc_stats.ast_tx_nodata++;
@@ -225,45 +356,395 @@
 	return 0;
 }
 
+/*
+ * Chain together segments+descriptors for a frame - 11n or otherwise.
+ *
+ * For aggregates, this is called on each frame in the aggregate.
+ */
 static void
-ath_tx_chaindesclist(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
+ath_tx_chaindesclist(struct ath_softc *sc, struct ath_desc *ds0,
+    struct ath_buf *bf, int is_aggr, int is_first_subframe,
+    int is_last_subframe)
 {
 	struct ath_hal *ah = sc->sc_ah;
-	struct ath_desc *ds, *ds0;
-	int i;
+	char *ds;
+	int i, bp, dsp;
+	HAL_DMA_ADDR bufAddrList[4];
+	uint32_t segLenList[4];
+	int numTxMaps = 1;
+	int isFirstDesc = 1;
 
 	/*
+	 * 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.
 	 */
-	ds0 = ds = bf->bf_desc;
-	for (i = 0; i < bf->bf_nseg; i++, ds++) {
-		ds->ds_data = bf->bf_segs[i].ds_addr;
+
+	/*
+	 * We need the number of TX data pointers in each descriptor.
+	 * EDMA and later chips support 4 TX buffers per descriptor;
+	 * previous chips just support one.
+	 */
+	numTxMaps = sc->sc_tx_nmaps;
+
+	/*
+	 * For EDMA and later chips ensure the TX map is fully populated
+	 * before advancing to the next descriptor.
+	 */
+	ds = (char *) bf->bf_desc;
+	bp = dsp = 0;
+	bzero(bufAddrList, sizeof(bufAddrList));
+	bzero(segLenList, sizeof(segLenList));
+	for (i = 0; i < bf->bf_nseg; i++) {
+		bufAddrList[bp] = bf->bf_segs[i].ds_addr;
+		segLenList[bp] = bf->bf_segs[i].ds_len;
+		bp++;
+
+		/*
+		 * Go to the next segment if this isn't the last segment
+		 * and there's space in the current TX map.
+		 */
+		if ((i != bf->bf_nseg - 1) && (bp < numTxMaps))
+			continue;
+
+		/*
+		 * Last segment or we're out of buffer pointers.
+		 */
+		bp = 0;
+
 		if (i == bf->bf_nseg - 1)
-			ds->ds_link = 0;
+			ath_hal_settxdesclink(ah, (struct ath_desc *) ds, 0);
 		else
-			ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
-		ath_hal_filltxdesc(ah, ds
-			, bf->bf_segs[i].ds_len	/* segment length */
-			, i == 0		/* first segment */
+			ath_hal_settxdesclink(ah, (struct ath_desc *) ds,
+			    bf->bf_daddr + dd->dd_descsize * (dsp + 1));
+
+		/*
+		 * XXX This assumes that bfs_txq is the actual destination
+		 * hardware queue at this point.  It may not have been
+		 * assigned, it may actually be pointing to the multicast
+		 * software TXQ id.  These must be fixed!
+		 */
+		ath_hal_filltxdesc(ah, (struct ath_desc *) ds
+			, bufAddrList
+			, segLenList
+			, bf->bf_descid		/* XXX desc id */
+			, bf->bf_state.bfs_tx_queue
+			, isFirstDesc		/* first segment */
 			, i == bf->bf_nseg - 1	/* last segment */
-			, ds0			/* first descriptor */
+			, (struct ath_desc *) ds0	/* first descriptor */
 		);
+
+		/*
+		 * Make sure the 11n aggregate fields are cleared.
+		 *
+		 * XXX TODO: this doesn't need to be called for
+		 * aggregate frames; as it'll be called on all
+		 * sub-frames.  Since the descriptors are in
+		 * non-cacheable memory, this leads to some
+		 * rather slow writes on MIPS/ARM platforms.
+		 */
+		if (ath_tx_is_11n(sc))
+			ath_hal_clr11n_aggr(sc->sc_ah, (struct ath_desc *) ds);
+
+		/*
+		 * If 11n is enabled, set it up as if it's an aggregate
+		 * frame.
+		 */
+		if (is_last_subframe) {
+			ath_hal_set11n_aggr_last(sc->sc_ah,
+			    (struct ath_desc *) ds);
+		} else if (is_aggr) {
+			/*
+			 * This clears the aggrlen field; so
+			 * the caller needs to call set_aggr_first()!
+			 *
+			 * XXX TODO: don't call this for the first
+			 * descriptor in the first frame in an
+			 * aggregate!
+			 */
+			ath_hal_set11n_aggr_middle(sc->sc_ah,
+			    (struct ath_desc *) ds,
+			    bf->bf_state.bfs_ndelim);
+		}
+		isFirstDesc = 0;
+		bf->bf_lastds = (struct ath_desc *) ds;
+
+		/*
+		 * Don't forget to skip to the next descriptor.
+		 */
+		ds += sc->sc_tx_desclen;
+		dsp++;
+
+		/*
+		 * .. and don't forget to blank these out!
+		 */
+		bzero(bufAddrList, sizeof(bufAddrList));
+		bzero(segLenList, sizeof(segLenList));
+	}
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+}
+
+/*
+ * Set the rate control fields in the given descriptor based on
+ * the bf_state fields and node state.
+ *
+ * The bfs fields should already be set with the relevant rate
+ * control information, including whether MRR is to be enabled.
+ *
+ * Since the FreeBSD HAL currently sets up the first TX rate
+ * in ath_hal_setuptxdesc(), this will setup the MRR
+ * conditionally for the pre-11n chips, and call ath_buf_set_rate
+ * unconditionally for 11n chips. These require the 11n rate
+ * scenario to be set if MCS rates are enabled, so it's easier
+ * to just always call it. The caller can then only set rates 2, 3
+ * and 4 if multi-rate retry is needed.
+ */
+static void
+ath_tx_set_ratectrl(struct ath_softc *sc, struct ieee80211_node *ni,
+    struct ath_buf *bf)
+{
+	struct ath_rc_series *rc = bf->bf_state.bfs_rc;
+
+	/* If mrr is disabled, blank tries 1, 2, 3 */
+	if (! bf->bf_state.bfs_ismrr)
+		rc[1].tries = rc[2].tries = rc[3].tries = 0;
+
+#if 0
+	/*
+	 * If NOACK is set, just set ntries=1.
+	 */
+	else if (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) {
+		rc[1].tries = rc[2].tries = rc[3].tries = 0;
+		rc[0].tries = 1;
+	}
+#endif
+
+	/*
+	 * Always call - that way a retried descriptor will
+	 * have the MRR fields overwritten.
+	 *
+	 * XXX TODO: see if this is really needed - setting up
+	 * the first descriptor should set the MRR fields to 0
+	 * for us anyway.
+	 */
+	if (ath_tx_is_11n(sc)) {
+		ath_buf_set_rate(sc, ni, bf);
+	} else {
+		ath_hal_setupxtxdesc(sc->sc_ah, bf->bf_desc
+			, rc[1].ratecode, rc[1].tries
+			, rc[2].ratecode, rc[2].tries
+			, rc[3].ratecode, rc[3].tries
+		);
+	}
+}
+
+/*
+ * Setup segments+descriptors for an 11n aggregate.
+ * bf_first is the first buffer in the aggregate.
+ * The descriptor list must already been linked together using
+ * bf->bf_next.
+ */
+static void
+ath_tx_setds_11n(struct ath_softc *sc, struct ath_buf *bf_first)
+{
+	struct ath_buf *bf, *bf_prev = NULL;
+	struct ath_desc *ds0 = bf_first->bf_desc;
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: nframes=%d, al=%d\n",
+	    __func__, bf_first->bf_state.bfs_nframes,
+	    bf_first->bf_state.bfs_al);
+
+	bf = bf_first;
+
+	if (bf->bf_state.bfs_txrate0 == 0)
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, txrate0=%d\n",
+		    __func__, bf, 0);
+	if (bf->bf_state.bfs_rc[0].ratecode == 0)
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: bf=%p, rix0=%d\n",
+		    __func__, bf, 0);
+
+	/*
+	 * Setup all descriptors of all subframes - this will
+	 * call ath_hal_set11naggrmiddle() on every frame.
+	 */
+	while (bf != NULL) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+		    "%s: bf=%p, nseg=%d, pktlen=%d, seqno=%d\n",
+		    __func__, bf, bf->bf_nseg, bf->bf_state.bfs_pktlen,
+		    SEQNO(bf->bf_state.bfs_seqno));
+
+		/*
+		 * Setup the initial fields for the first descriptor - all
+		 * the non-11n specific stuff.
+		 */
+		ath_hal_setuptxdesc(sc->sc_ah, bf->bf_desc
+			, bf->bf_state.bfs_pktlen	/* packet length */
+			, bf->bf_state.bfs_hdrlen	/* header length */
+			, bf->bf_state.bfs_atype	/* Atheros packet type */
+			, bf->bf_state.bfs_txpower	/* txpower */
+			, bf->bf_state.bfs_txrate0
+			, bf->bf_state.bfs_try0		/* series 0 rate/tries */
+			, bf->bf_state.bfs_keyix	/* key cache index */
+			, bf->bf_state.bfs_txantenna	/* antenna mode */
+			, bf->bf_state.bfs_txflags | HAL_TXDESC_INTREQ	/* flags */
+			, bf->bf_state.bfs_ctsrate	/* rts/cts rate */
+			, bf->bf_state.bfs_ctsduration	/* rts/cts duration */
+		);
+
+		/*
+		 * First descriptor? Setup the rate control and initial
+		 * aggregate header information.
+		 */
+		if (bf == bf_first) {
+			/*
+			 * setup first desc with rate and aggr info
+			 */
+			ath_tx_set_ratectrl(sc, bf->bf_node, bf);
+		}
+
+		/*
+		 * Setup the descriptors for a multi-descriptor frame.
+		 * This is both aggregate and non-aggregate aware.
+		 */
+		ath_tx_chaindesclist(sc, ds0, bf,
+		    1, /* is_aggr */
+		    !! (bf == bf_first), /* is_first_subframe */
+		    !! (bf->bf_next == NULL) /* is_last_subframe */
+		    );
+
+		if (bf == bf_first) {
+			/*
+			 * Initialise the first 11n aggregate with the
+			 * aggregate length and aggregate enable bits.
+			 */
+			ath_hal_set11n_aggr_first(sc->sc_ah,
+			    ds0,
+			    bf->bf_state.bfs_al,
+			    bf->bf_state.bfs_ndelim);
+		}
+
+		/*
+		 * Link the last descriptor of the previous frame
+		 * to the beginning descriptor of this frame.
+		 */
+		if (bf_prev != NULL)
+			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;
+		bf = bf->bf_next;
+	}
+
+	/*
+	 * Set the first descriptor bf_lastds field to point to
+	 * the last descriptor in the last subframe, that's where
+	 * the status update will occur.
+	 */
+	bf_first->bf_lastds = bf_prev->bf_lastds;
+
+	/*
+	 * And bf_last in the first descriptor points to the end of
+	 * the aggregate list.
+	 */
+	bf_first->bf_last = bf_prev;
+
+	/*
+	 * For non-AR9300 NICs, which require the rate control
+	 * in the final descriptor - let's set that up now.
+	 *
+	 * This is because the filltxdesc() HAL call doesn't
+	 * populate the last segment with rate control information
+	 * if firstSeg is also true.  For non-aggregate frames
+	 * that is fine, as the first frame already has rate control
+	 * info.  But if the last frame in an aggregate has one
+	 * descriptor, both firstseg and lastseg will be true and
+	 * the rate info isn't copied.
+	 *
+	 * This is inefficient on MIPS/ARM platforms that have
+	 * non-cachable memory for TX descriptors, but we'll just
+	 * make do for now.
+	 *
+	 * As to why the rate table is stashed in the last descriptor
+	 * rather than the first descriptor?  Because proctxdesc()
+	 * is called on the final descriptor in an MPDU or A-MPDU -
+	 * ie, the one that gets updated by the hardware upon
+	 * completion.  That way proctxdesc() doesn't need to know
+	 * about the first _and_ last TX descriptor.
+	 */
+	ath_hal_setuplasttxdesc(sc->sc_ah, bf_prev->bf_lastds, ds0);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: end\n", __func__);
+}
+
+/*
+ * Hand-off a frame to the multicast TX queue.
+ *
+ * This is a software TXQ which will be appended to the CAB queue
+ * during the beacon setup code.
+ *
+ * XXX TODO: since the AR9300 EDMA TX queue support wants the QCU ID
+ * as part of the TX descriptor, bf_state.bfs_tx_queue must be updated
+ * with the actual hardware txq, or all of this will fall apart.
+ *
+ * XXX It may not be a bad idea to just stuff the QCU ID into bf_state
+ * and retire bfs_tx_queue; then make sure the CABQ QCU ID is populated
+ * correctly.
+ */
+static void
+ath_tx_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq,
+    struct ath_buf *bf)
+{
+	ATH_TX_LOCK_ASSERT(sc);
+
+	KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
+	     ("%s: busy status 0x%x", __func__, bf->bf_flags));
+
+	/*
+	 * Ensure that the tx queue is the cabq, so things get
+	 * mapped correctly.
+	 */
+	if (bf->bf_state.bfs_tx_queue != sc->sc_cabq->axq_qnum) {
 		DPRINTF(sc, ATH_DEBUG_XMIT,
-			"%s: %d: %08x %08x %08x %08x %08x %08x\n",
-			__func__, i, ds->ds_link, ds->ds_data,
-			ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
+		    "%s: bf=%p, bfs_tx_queue=%d, axq_qnum=%d\n",
+		    __func__, bf, bf->bf_state.bfs_tx_queue,
+		    txq->axq_qnum);
 	}
 
+	ATH_TXQ_LOCK(txq);
+	if (ATH_TXQ_LAST(txq, axq_q_s) != NULL) {
+		struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s);
+		struct ieee80211_frame *wh;
+
+		/* mark previous frame */
+		wh = mtod(bf_last->bf_m, struct ieee80211_frame *);
+		wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+		bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap,
+		    BUS_DMASYNC_PREWRITE);
+
+		/* link descriptor */
+		ath_hal_settxdesclink(sc->sc_ah,
+		    bf_last->bf_lastds,
+		    bf->bf_daddr);
+	}
+	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+	ATH_TXQ_UNLOCK(txq);
 }
 
+/*
+ * Hand-off packet to a hardware queue.
+ */
 static void
-ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
+ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq,
+    struct ath_buf *bf)
 {
 	struct ath_hal *ah = sc->sc_ah;
+	struct ath_buf *bf_first;
 
-	/* Fill in the details in the descriptor list */
-	ath_tx_chaindesclist(sc, txq, bf);
-
 	/*
 	 * Insert the frame on the outbound list and pass it on
 	 * to the hardware.  Multicast frames buffered for power
@@ -272,107 +753,256 @@
 	 * the SWBA handler since frames only go out on DTIM and
 	 * to avoid possible races.
 	 */
-	ATH_TXQ_LOCK(txq);
+	ATH_TX_LOCK_ASSERT(sc);
 	KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
-	     ("busy status 0x%x", bf->bf_flags));
-	if (txq->axq_qnum != ATH_TXQ_SWQ) {
-#ifdef IEEE80211_SUPPORT_TDMA
-		int qbusy;
+	     ("%s: busy status 0x%x", __func__, bf->bf_flags));
+	KASSERT(txq->axq_qnum != ATH_TXQ_SWQ,
+	     ("ath_tx_handoff_hw called for mcast queue"));
 
+	/*
+	 * XXX racy, should hold the PCU lock when checking this,
+	 * and also should ensure that the TX counter is >0!
+	 */
+	KASSERT((sc->sc_inreset_cnt == 0),
+	    ("%s: TX during reset?\n", __func__));
+
+#if 0
+	/*
+	 * This causes a LOR. Find out where the PCU lock is being
+	 * held whilst the TXQ lock is grabbed - that shouldn't
+	 * be occuring.
+	 */
+	ATH_PCU_LOCK(sc);
+	if (sc->sc_inreset_cnt) {
+		ATH_PCU_UNLOCK(sc);
+		DPRINTF(sc, ATH_DEBUG_RESET,
+		    "%s: called with sc_in_reset != 0\n",
+		    __func__);
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: queued: TXDP[%u] = %p (%p) depth %d\n",
+		    __func__, txq->axq_qnum,
+		    (caddr_t)bf->bf_daddr, bf->bf_desc,
+		    txq->axq_depth);
+		/* XXX axq_link needs to be set and updated! */
 		ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
-		qbusy = ath_hal_txqenabled(ah, txq->axq_qnum);
-		if (txq->axq_link == NULL) {
-			/*
-			 * Be careful writing the address to TXDP.  If
-			 * the tx q is enabled then this write will be
-			 * ignored.  Normally this is not an issue but
-			 * when tdma is in use and the q is beacon gated
-			 * this race can occur.  If the q is busy then
-			 * defer the work to later--either when another
-			 * packet comes along or when we prepare a beacon
-			 * frame at SWBA.
-			 */
-			if (!qbusy) {
-				ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
-				txq->axq_flags &= ~ATH_TXQ_PUTPENDING;
-				DPRINTF(sc, ATH_DEBUG_XMIT,
-				    "%s: TXDP[%u] = %p (%p) depth %d\n",
-				    __func__, txq->axq_qnum,
-				    (caddr_t)bf->bf_daddr, bf->bf_desc,
-				    txq->axq_depth);
-			} else {
-				txq->axq_flags |= ATH_TXQ_PUTPENDING;
-				DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT,
-				    "%s: Q%u busy, defer enable\n", __func__,
-				    txq->axq_qnum);
-			}
-		} else {
-			*txq->axq_link = bf->bf_daddr;
-			DPRINTF(sc, ATH_DEBUG_XMIT,
-			    "%s: link[%u](%p)=%p (%p) depth %d\n", __func__,
-			    txq->axq_qnum, txq->axq_link,
-			    (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth);
-			if ((txq->axq_flags & ATH_TXQ_PUTPENDING) && !qbusy) {
-				/*
-				 * The q was busy when we previously tried
-				 * to write the address of the first buffer
-				 * in the chain.  Since it's not busy now
-				 * handle this chore.  We are certain the
-				 * buffer at the front is the right one since
-				 * axq_link is NULL only when the buffer list
-				 * is/was empty.
-				 */
-				ath_hal_puttxbuf(ah, txq->axq_qnum,
-					STAILQ_FIRST(&txq->axq_q)->bf_daddr);
-				txq->axq_flags &= ~ATH_TXQ_PUTPENDING;
-				DPRINTF(sc, ATH_DEBUG_TDMA | ATH_DEBUG_XMIT,
-				    "%s: Q%u restarted\n", __func__,
-				    txq->axq_qnum);
-			}
+		if (bf->bf_state.bfs_aggr)
+			txq->axq_aggr_depth++;
+		return;
 		}
-#else
-		ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
-		if (txq->axq_link == NULL) {
-			ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
-			DPRINTF(sc, ATH_DEBUG_XMIT,
-			    "%s: TXDP[%u] = %p (%p) depth %d\n",
-			    __func__, txq->axq_qnum,
-			    (caddr_t)bf->bf_daddr, bf->bf_desc,
-			    txq->axq_depth);
-		} else {
-			*txq->axq_link = bf->bf_daddr;
-			DPRINTF(sc, ATH_DEBUG_XMIT,
-			    "%s: link[%u](%p)=%p (%p) depth %d\n", __func__,
-			    txq->axq_qnum, txq->axq_link,
-			    (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth);
-		}
-#endif /* IEEE80211_SUPPORT_TDMA */
-		txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
-		ath_hal_txstart(ah, txq->axq_qnum);
-	} else {
-		if (txq->axq_link != NULL) {
-			struct ath_buf *last = ATH_TXQ_LAST(txq);
-			struct ieee80211_frame *wh;
+	ATH_PCU_UNLOCK(sc);
+#endif
 
-			/* mark previous frame */
-			wh = mtod(last->bf_m, struct ieee80211_frame *);
-			wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
-			bus_dmamap_sync(sc->sc_dmat, last->bf_dmamap,
-			    BUS_DMASYNC_PREWRITE);
+	ATH_TXQ_LOCK(txq);
 
-			/* link descriptor */
-			*txq->axq_link = bf->bf_daddr;
-		}
-		ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
-		txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
+	/*
+	 * XXX TODO: if there's a holdingbf, then
+	 * ATH_TXQ_PUTRUNNING should be clear.
+	 *
+	 * If there is a holdingbf and the list is empty,
+	 * then axq_link should be pointing to the holdingbf.
+	 *
+	 * Otherwise it should point to the last descriptor
+	 * in the last ath_buf.
+	 *
+	 * In any case, we should really ensure that we
+	 * update the previous descriptor link pointer to
+	 * this descriptor, regardless of all of the above state.
+	 *
+	 * For now this is captured by having axq_link point
+	 * to either the holdingbf (if the TXQ list is empty)
+	 * or the end of the list (if the TXQ list isn't empty.)
+	 * I'd rather just kill axq_link here and do it as above.
+	 */
+
+	/*
+	 * Append the frame to the TX queue.
+	 */
+	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+	ATH_KTR(sc, ATH_KTR_TX, 3,
+	    "ath_tx_handoff: non-tdma: txq=%u, add bf=%p "
+	    "depth=%d",
+	    txq->axq_qnum,
+	    bf,
+	    txq->axq_depth);
+
+	/*
+	 * If there's a link pointer, update it.
+	 *
+	 * XXX we should replace this with the above logic, just
+	 * to kill axq_link with fire.
+	 */
+	if (txq->axq_link != NULL) {
+		*txq->axq_link = bf->bf_daddr;
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: link[%u](%p)=%p (%p) depth %d\n", __func__,
+		    txq->axq_qnum, txq->axq_link,
+		    (caddr_t)bf->bf_daddr, bf->bf_desc,
+		    txq->axq_depth);
+		ATH_KTR(sc, ATH_KTR_TX, 5,
+		    "ath_tx_handoff: non-tdma: link[%u](%p)=%p (%p) "
+		    "lastds=%d",
+		    txq->axq_qnum, txq->axq_link,
+		    (caddr_t)bf->bf_daddr, bf->bf_desc,
+		    bf->bf_lastds);
 	}
+
+	/*
+	 * If we've not pushed anything into the hardware yet,
+	 * push the head of the queue into the TxDP.
+	 *
+	 * Once we've started DMA, there's no guarantee that
+	 * updating the TxDP with a new value will actually work.
+	 * So we just don't do that - if we hit the end of the list,
+	 * we keep that buffer around (the "holding buffer") and
+	 * re-start DMA by updating the link pointer of _that_
+	 * descriptor and then restart DMA.
+	 */
+	if (! (txq->axq_flags & ATH_TXQ_PUTRUNNING)) {
+		bf_first = TAILQ_FIRST(&txq->axq_q);
+		txq->axq_flags |= ATH_TXQ_PUTRUNNING;
+		ath_hal_puttxbuf(ah, txq->axq_qnum, bf_first->bf_daddr);
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: TXDP[%u] = %p (%p) depth %d\n",
+		    __func__, txq->axq_qnum,
+		    (caddr_t)bf_first->bf_daddr, bf_first->bf_desc,
+		    txq->axq_depth);
+		ATH_KTR(sc, ATH_KTR_TX, 5,
+		    "ath_tx_handoff: TXDP[%u] = %p (%p) "
+		    "lastds=%p depth %d",
+		    txq->axq_qnum,
+		    (caddr_t)bf_first->bf_daddr, bf_first->bf_desc,
+		    bf_first->bf_lastds,
+		    txq->axq_depth);
+	}
+
+	/*
+	 * Ensure that the bf TXQ matches this TXQ, so later
+	 * checking and holding buffer manipulation is sane.
+	 */
+	if (bf->bf_state.bfs_tx_queue != txq->axq_qnum) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: bf=%p, bfs_tx_queue=%d, axq_qnum=%d\n",
+		    __func__, bf, bf->bf_state.bfs_tx_queue,
+		    txq->axq_qnum);
+	}
+
+	/*
+	 * Track aggregate queue depth.
+	 */
+	if (bf->bf_state.bfs_aggr)
+		txq->axq_aggr_depth++;
+
+	/*
+	 * Update the link pointer.
+	 */
+	ath_hal_gettxdesclinkptr(ah, bf->bf_lastds, &txq->axq_link);
+
+	/*
+	 * Start DMA.
+	 *
+	 * If we wrote a TxDP above, DMA will start from here.
+	 *
+	 * If DMA is running, it'll do nothing.
+	 *
+	 * If the DMA engine hit the end of the QCU list (ie LINK=NULL,
+	 * or VEOL) then it stops at the last transmitted write.
+	 * We then append a new frame by updating the link pointer
+	 * in that descriptor and then kick TxE here; it will re-read
+	 * that last descriptor and find the new descriptor to transmit.
+	 *
+	 * This is why we keep the holding descriptor around.
+	 */
+	ath_hal_txstart(ah, txq->axq_qnum);
 	ATH_TXQ_UNLOCK(txq);
+	ATH_KTR(sc, ATH_KTR_TX, 1,
+	    "ath_tx_handoff: txq=%u, txstart", txq->axq_qnum);
 }
 
+/*
+ * Restart TX DMA for the given TXQ.
+ *
+ * This must be called whether the queue is empty or not.
+ */
+static void
+ath_legacy_tx_dma_restart(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_buf *bf, *bf_last;
+
+	ATH_TXQ_LOCK_ASSERT(txq);
+
+	/* XXX make this ATH_TXQ_FIRST */
+	bf = TAILQ_FIRST(&txq->axq_q);
+	bf_last = ATH_TXQ_LAST(txq, axq_q_s);
+
+	if (bf == NULL)
+		return;
+
+	DPRINTF(sc, ATH_DEBUG_RESET,
+	    "%s: Q%d: bf=%p, bf_last=%p, daddr=0x%08x\n",
+	    __func__,
+	    txq->axq_qnum,
+	    bf,
+	    bf_last,
+	    (uint32_t) bf->bf_daddr);
+
+#ifdef	ATH_DEBUG
+	if (sc->sc_debug & ATH_DEBUG_RESET)
+		ath_tx_dump(sc, txq);
+#endif
+
+	/*
+	 * This is called from a restart, so DMA is known to be
+	 * completely stopped.
+	 */
+	KASSERT((!(txq->axq_flags & ATH_TXQ_PUTRUNNING)),
+	    ("%s: Q%d: called with PUTRUNNING=1\n",
+	    __func__,
+	    txq->axq_qnum));
+
+	ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr);
+	txq->axq_flags |= ATH_TXQ_PUTRUNNING;
+
+	ath_hal_gettxdesclinkptr(sc->sc_ah, bf_last->bf_lastds,
+	    &txq->axq_link);
+	ath_hal_txstart(sc->sc_ah, txq->axq_qnum);
+}
+
+/*
+ * Hand off a packet to the hardware (or mcast queue.)
+ *
+ * The relevant hardware txq should be locked.
+ */
+static void
+ath_legacy_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq,
+    struct ath_buf *bf)
+{
+	ATH_TX_LOCK_ASSERT(sc);
+
+#ifdef	ATH_DEBUG_ALQ
+	if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC))
+		ath_tx_alq_post(sc, bf);
+#endif
+
+	if (txq->axq_qnum == ATH_TXQ_SWQ)
+		ath_tx_handoff_mcast(sc, txq, bf);
+	else
+		ath_tx_handoff_hw(sc, txq, bf);
+}
+
 static int
 ath_tx_tag_crypto(struct ath_softc *sc, struct ieee80211_node *ni,
-    struct mbuf *m0, int iswep, int isfrag, int *hdrlen, int *pktlen, int *keyix)
+    struct mbuf *m0, int iswep, int isfrag, int *hdrlen, int *pktlen,
+    int *keyix)
 {
+	DPRINTF(sc, ATH_DEBUG_XMIT,
+	    "%s: hdrlen=%d, pktlen=%d, isfrag=%d, iswep=%d, m0=%p\n",
+	    __func__,
+	    *hdrlen,
+	    *pktlen,
+	    isfrag,
+	    iswep,
+	    m0);
+
 	if (iswep) {
 		const struct ieee80211_cipher *cip;
 		struct ieee80211_key *k;
@@ -390,7 +1020,7 @@
 			 * 802.11 layer counts failures and provides
 			 * debugging/diagnostics.
 			 */
-			return 0;
+			return (0);
 		}
 		/*
 		 * Adjust the packet + header lengths for the crypto
@@ -417,12 +1047,143 @@
 	} else
 		(*keyix) = HAL_TXKEYIX_INVALID;
 
-	return 1;
+	return (1);
 }
 
+/*
+ * Calculate whether interoperability protection is required for
+ * this frame.
+ *
+ * This requires the rate control information be filled in,
+ * as the protection requirement depends upon the current
+ * operating mode / PHY.
+ */
+static void
+ath_tx_calc_protection(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ieee80211_frame *wh;
+	uint8_t rix;
+	uint16_t flags;
+	int shortPreamble;
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ieee80211com *ic = ifp->if_l2com;
+
+	flags = bf->bf_state.bfs_txflags;
+	rix = bf->bf_state.bfs_rc[0].rix;
+	shortPreamble = bf->bf_state.bfs_shpream;
+	wh = mtod(bf->bf_m, struct ieee80211_frame *);
+
+	/*
+	 * If 802.11g protection is enabled, determine whether
+	 * to use RTS/CTS or just CTS.  Note that this is only
+	 * done for OFDM unicast frames.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+	    rt->info[rix].phy == IEEE80211_T_OFDM &&
+	    (flags & HAL_TXDESC_NOACK) == 0) {
+		bf->bf_state.bfs_doprot = 1;
+		/* XXX fragments must use CCK rates w/ protection */
+		if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) {
+			flags |= HAL_TXDESC_RTSENA;
+		} else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) {
+			flags |= HAL_TXDESC_CTSENA;
+		}
+		/*
+		 * For frags it would be desirable to use the
+		 * highest CCK rate for RTS/CTS.  But stations
+		 * farther away may detect it at a lower CCK rate
+		 * so use the configured protection rate instead
+		 * (for now).
+		 */
+		sc->sc_stats.ast_tx_protect++;
+	}
+
+	/*
+	 * If 11n protection is enabled and it's a HT frame,
+	 * enable RTS.
+	 *
+	 * XXX ic_htprotmode or ic_curhtprotmode?
+	 * XXX should it_htprotmode only matter if ic_curhtprotmode 
+	 * XXX indicates it's not a HT pure environment?
+	 */
+	if ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) &&
+	    rt->info[rix].phy == IEEE80211_T_HT &&
+	    (flags & HAL_TXDESC_NOACK) == 0) {
+		flags |= HAL_TXDESC_RTSENA;
+		sc->sc_stats.ast_tx_htprotect++;
+	}
+	bf->bf_state.bfs_txflags = flags;
+}
+
+/*
+ * Update the frame duration given the currently selected rate.
+ *
+ * This also updates the frame duration value, so it will require
+ * a DMA flush.
+ */
+static void
+ath_tx_calc_duration(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ieee80211_frame *wh;
+	uint8_t rix;
+	uint16_t flags;
+	int shortPreamble;
+	struct ath_hal *ah = sc->sc_ah;
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	int isfrag = bf->bf_m->m_flags & M_FRAG;
+
+	flags = bf->bf_state.bfs_txflags;
+	rix = bf->bf_state.bfs_rc[0].rix;
+	shortPreamble = bf->bf_state.bfs_shpream;
+	wh = mtod(bf->bf_m, struct ieee80211_frame *);
+
+	/*
+	 * Calculate duration.  This logically belongs in the 802.11
+	 * layer but it lacks sufficient information to calculate it.
+	 */
+	if ((flags & HAL_TXDESC_NOACK) == 0 &&
+	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
+		u_int16_t dur;
+		if (shortPreamble)
+			dur = rt->info[rix].spAckDuration;
+		else
+			dur = rt->info[rix].lpAckDuration;
+		if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) {
+			dur += dur;		/* additional SIFS+ACK */
+			/*
+			 * Include the size of next fragment so NAV is
+			 * updated properly.  The last fragment uses only
+			 * the ACK duration
+			 *
+			 * XXX TODO: ensure that the rate lookup for each
+			 * fragment is the same as the rate used by the
+			 * first fragment!
+			 */
+			dur += ath_hal_computetxtime(ah,
+			    rt,
+			    bf->bf_nextfraglen,
+			    rix, shortPreamble);
+		}
+		if (isfrag) {
+			/*
+			 * Force hardware to use computed duration for next
+			 * fragment by disabling multi-rate retry which updates
+			 * duration based on the multi-rate duration table.
+			 */
+			bf->bf_state.bfs_ismrr = 0;
+			bf->bf_state.bfs_try0 = ATH_TXMGTTRY;
+			/* XXX update bfs_rc[0].try? */
+		}
+
+		/* Update the duration field itself */
+		*(u_int16_t *)wh->i_dur = htole16(dur);
+	}
+}
+
 static uint8_t
 ath_tx_get_rtscts_rate(struct ath_hal *ah, const HAL_RATE_TABLE *rt,
-    int rix, int cix, int shortPreamble)
+    int cix, int shortPreamble)
 {
 	uint8_t ctsrate;
 
@@ -439,10 +1200,9 @@
 	if (shortPreamble)
 		ctsrate |= rt->info[cix].shortPreamble;
 
-	return ctsrate;
+	return (ctsrate);
 }
 
-
 /*
  * Calculate the RTS/CTS duration for legacy frames.
  */
@@ -457,7 +1217,7 @@
 	if (rt->info[cix].phy == IEEE80211_T_HT) {
 		printf("%s: HT rate where it shouldn't be (0x%x)\n",
 		    __func__, rt->info[cix].rateCode);
-		return -1;
+		return (-1);
 	}
 
 	/*
@@ -485,39 +1245,347 @@
 			ctsduration += rt->info[rix].lpAckDuration;
 	}
 
-	return ctsduration;
+	return (ctsduration);
 }
 
-int
-ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf,
-    struct mbuf *m0)
+/*
+ * Update the given ath_buf with updated rts/cts setup and duration
+ * values.
+ *
+ * To support rate lookups for each software retry, the rts/cts rate
+ * and cts duration must be re-calculated.
+ *
+ * This function assumes the RTS/CTS flags have been set as needed;
+ * mrr has been disabled; and the rate control lookup has been done.
+ *
+ * XXX TODO: MRR need only be disabled for the pre-11n NICs.
+ * XXX The 11n NICs support per-rate RTS/CTS configuration.
+ */
+static void
+ath_tx_set_rtscts(struct ath_softc *sc, struct ath_buf *bf)
 {
+	uint16_t ctsduration = 0;
+	uint8_t ctsrate = 0;
+	uint8_t rix = bf->bf_state.bfs_rc[0].rix;
+	uint8_t cix = 0;
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+
+	/*
+	 * No RTS/CTS enabled? Don't bother.
+	 */
+	if ((bf->bf_state.bfs_txflags &
+	    (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) == 0) {
+		/* XXX is this really needed? */
+		bf->bf_state.bfs_ctsrate = 0;
+		bf->bf_state.bfs_ctsduration = 0;
+		return;
+	}
+
+	/*
+	 * If protection is enabled, use the protection rix control
+	 * rate. Otherwise use the rate0 control rate.
+	 */
+	if (bf->bf_state.bfs_doprot)
+		rix = sc->sc_protrix;
+	else
+		rix = bf->bf_state.bfs_rc[0].rix;
+
+	/*
+	 * If the raw path has hard-coded ctsrate0 to something,
+	 * use it.
+	 */
+	if (bf->bf_state.bfs_ctsrate0 != 0)
+		cix = ath_tx_findrix(sc, bf->bf_state.bfs_ctsrate0);
+	else
+		/* Control rate from above */
+		cix = rt->info[rix].controlRate;
+
+	/* Calculate the rtscts rate for the given cix */
+	ctsrate = ath_tx_get_rtscts_rate(sc->sc_ah, rt, cix,
+	    bf->bf_state.bfs_shpream);
+
+	/* The 11n chipsets do ctsduration calculations for you */
+	if (! ath_tx_is_11n(sc))
+		ctsduration = ath_tx_calc_ctsduration(sc->sc_ah, rix, cix,
+		    bf->bf_state.bfs_shpream, bf->bf_state.bfs_pktlen,
+		    rt, bf->bf_state.bfs_txflags);
+
+	/* Squirrel away in ath_buf */
+	bf->bf_state.bfs_ctsrate = ctsrate;
+	bf->bf_state.bfs_ctsduration = ctsduration;
+	
+	/*
+	 * Must disable multi-rate retry when using RTS/CTS.
+	 */
+	if (!sc->sc_mrrprot) {
+		bf->bf_state.bfs_ismrr = 0;
+		bf->bf_state.bfs_try0 =
+		    bf->bf_state.bfs_rc[0].tries = ATH_TXMGTTRY; /* XXX ew */
+	}
+}
+
+/*
+ * Setup the descriptor chain for a normal or fast-frame
+ * frame.
+ *
+ * XXX TODO: extend to include the destination hardware QCU ID.
+ * Make sure that is correct.  Make sure that when being added
+ * to the mcastq, the CABQ QCUID is set or things will get a bit
+ * odd.
+ */
+static void
+ath_tx_setds(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ath_desc *ds = bf->bf_desc;
+	struct ath_hal *ah = sc->sc_ah;
+
+	if (bf->bf_state.bfs_txrate0 == 0)
+		DPRINTF(sc, ATH_DEBUG_XMIT, 
+		    "%s: bf=%p, txrate0=%d\n", __func__, bf, 0);
+
+	ath_hal_setuptxdesc(ah, ds
+		, bf->bf_state.bfs_pktlen	/* packet length */
+		, bf->bf_state.bfs_hdrlen	/* header length */
+		, bf->bf_state.bfs_atype	/* Atheros packet type */
+		, bf->bf_state.bfs_txpower	/* txpower */
+		, bf->bf_state.bfs_txrate0
+		, bf->bf_state.bfs_try0		/* series 0 rate/tries */
+		, bf->bf_state.bfs_keyix	/* key cache index */
+		, bf->bf_state.bfs_txantenna	/* antenna mode */
+		, bf->bf_state.bfs_txflags	/* flags */
+		, bf->bf_state.bfs_ctsrate	/* rts/cts rate */
+		, bf->bf_state.bfs_ctsduration	/* rts/cts duration */
+	);
+
+	/*
+	 * This will be overriden when the descriptor chain is written.
+	 */
+	bf->bf_lastds = ds;
+	bf->bf_last = bf;
+
+	/* Set rate control and descriptor chain for this frame */
+	ath_tx_set_ratectrl(sc, bf->bf_node, bf);
+	ath_tx_chaindesclist(sc, ds, bf, 0, 0, 0);
+}
+
+/*
+ * Do a rate lookup.
+ *
+ * This performs a rate lookup for the given ath_buf only if it's required.
+ * Non-data frames and raw frames don't require it.
+ *
+ * This populates the primary and MRR entries; MRR values are
+ * then disabled later on if something requires it (eg RTS/CTS on
+ * pre-11n chipsets.
+ *
+ * This needs to be done before the RTS/CTS fields are calculated
+ * as they may depend upon the rate chosen.
+ */
+static void
+ath_tx_do_ratelookup(struct ath_softc *sc, struct ath_buf *bf)
+{
+	uint8_t rate, rix;
+	int try0;
+
+	if (! bf->bf_state.bfs_doratelookup)
+		return;
+
+	/* Get rid of any previous state */
+	bzero(bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc));
+
+	ATH_NODE_LOCK(ATH_NODE(bf->bf_node));
+	ath_rate_findrate(sc, ATH_NODE(bf->bf_node), bf->bf_state.bfs_shpream,
+	    bf->bf_state.bfs_pktlen, &rix, &try0, &rate);
+
+	/* In case MRR is disabled, make sure rc[0] is setup correctly */
+	bf->bf_state.bfs_rc[0].rix = rix;
+	bf->bf_state.bfs_rc[0].ratecode = rate;
+	bf->bf_state.bfs_rc[0].tries = try0;
+
+	if (bf->bf_state.bfs_ismrr && try0 != ATH_TXMAXTRY)
+		ath_rate_getxtxrates(sc, ATH_NODE(bf->bf_node), rix,
+		    bf->bf_state.bfs_rc);
+	ATH_NODE_UNLOCK(ATH_NODE(bf->bf_node));
+
+	sc->sc_txrix = rix;	/* for LED blinking */
+	sc->sc_lastdatarix = rix;	/* for fast frames */
+	bf->bf_state.bfs_try0 = try0;
+	bf->bf_state.bfs_txrate0 = rate;
+}
+
+/*
+ * Update the CLRDMASK bit in the ath_buf if it needs to be set.
+ */
+static void
+ath_tx_update_clrdmask(struct ath_softc *sc, struct ath_tid *tid,
+    struct ath_buf *bf)
+{
+	struct ath_node *an = ATH_NODE(bf->bf_node);
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (an->clrdmask == 1) {
+		bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+		an->clrdmask = 0;
+	}
+}
+
+/*
+ * Return whether this frame should be software queued or
+ * direct dispatched.
+ *
+ * When doing powersave, BAR frames should be queued but other management
+ * frames should be directly sent.
+ *
+ * When not doing powersave, stick BAR frames into the hardware queue
+ * so it goes out even though the queue is paused.
+ *
+ * For now, management frames are also software queued by default.
+ */
+static int
+ath_tx_should_swq_frame(struct ath_softc *sc, struct ath_node *an,
+    struct mbuf *m0, int *queue_to_head)
+{
+	struct ieee80211_node *ni = &an->an_node;
+	struct ieee80211_frame *wh;
+	uint8_t type, subtype;
+
+	wh = mtod(m0, struct ieee80211_frame *);
+	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+	(*queue_to_head) = 0;
+
+	/* If it's not in powersave - direct-dispatch BAR */
+	if ((ATH_NODE(ni)->an_is_powersave == 0)
+	    && type == IEEE80211_FC0_TYPE_CTL &&
+	    subtype == IEEE80211_FC0_SUBTYPE_BAR) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX,
+		    "%s: BAR: TX'ing direct\n", __func__);
+		return (0);
+	} else if ((ATH_NODE(ni)->an_is_powersave == 1)
+	    && type == IEEE80211_FC0_TYPE_CTL &&
+	    subtype == IEEE80211_FC0_SUBTYPE_BAR) {
+		/* BAR TX whilst asleep; queue */
+		DPRINTF(sc, ATH_DEBUG_SW_TX,
+		    "%s: swq: TX'ing\n", __func__);
+		(*queue_to_head) = 1;
+		return (1);
+	} else if ((ATH_NODE(ni)->an_is_powersave == 1)
+	    && (type == IEEE80211_FC0_TYPE_MGT ||
+	        type == IEEE80211_FC0_TYPE_CTL)) {
+		/*
+		 * Other control/mgmt frame; bypass software queuing
+		 * for now!
+		 */
+		DPRINTF(sc, ATH_DEBUG_XMIT, 
+		    "%s: %6D: Node is asleep; sending mgmt "
+		    "(type=%d, subtype=%d)\n",
+		    __func__, ni->ni_macaddr, ":", type, subtype);
+		return (0);
+	} else {
+		return (1);
+	}
+}
+
+
+/*
+ * Transmit the given frame to the hardware.
+ *
+ * The frame must already be setup; rate control must already have
+ * been done.
+ *
+ * XXX since the TXQ lock is being held here (and I dislike holding
+ * it for this long when not doing software aggregation), later on
+ * break this function into "setup_normal" and "xmit_normal". The
+ * lock only needs to be held for the ath_tx_handoff call.
+ *
+ * XXX we don't update the leak count here - if we're doing
+ * direct frame dispatch, we need to be able to do it without
+ * decrementing the leak count (eg multicast queue frames.)
+ */
+static void
+ath_tx_xmit_normal(struct ath_softc *sc, struct ath_txq *txq,
+    struct ath_buf *bf)
+{
+	struct ath_node *an = ATH_NODE(bf->bf_node);
+	struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid];
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * For now, just enable CLRDMASK. ath_tx_xmit_normal() does
+	 * set a completion handler however it doesn't (yet) properly
+	 * handle the strict ordering requirements needed for normal,
+	 * non-aggregate session frames.
+	 *
+	 * Once this is implemented, only set CLRDMASK like this for
+	 * frames that must go out - eg management/raw frames.
+	 */
+	bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+
+	/* Setup the descriptor before handoff */
+	ath_tx_do_ratelookup(sc, bf);
+	ath_tx_calc_duration(sc, bf);
+	ath_tx_calc_protection(sc, bf);
+	ath_tx_set_rtscts(sc, bf);
+	ath_tx_rate_fill_rcflags(sc, bf);
+	ath_tx_setds(sc, bf);
+
+	/* Track per-TID hardware queue depth correctly */
+	tid->hwq_depth++;
+
+	/* Assign the completion handler */
+	bf->bf_comp = ath_tx_normal_comp;
+
+	/* Hand off to hardware */
+	ath_tx_handoff(sc, txq, bf);
+}
+
+/*
+ * Do the basic frame setup stuff that's required before the frame
+ * is added to a software queue.
+ *
+ * All frames get mostly the same treatment and it's done once.
+ * Retransmits fiddle with things like the rate control setup,
+ * setting the retransmit bit in the packet; doing relevant DMA/bus
+ * syncing and relinking it (back) into the hardware TX queue.
+ *
+ * Note that this may cause the mbuf to be reallocated, so
+ * m0 may not be valid.
+ */
+static int
+ath_tx_normal_setup(struct ath_softc *sc, struct ieee80211_node *ni,
+    struct ath_buf *bf, struct mbuf *m0, struct ath_txq *txq)
+{
 	struct ieee80211vap *vap = ni->ni_vap;
-	struct ath_vap *avp = ATH_VAP(vap);
 	struct ath_hal *ah = sc->sc_ah;
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ieee80211com *ic = ifp->if_l2com;
 	const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
 	int error, iswep, ismcast, isfrag, ismrr;
-	int keyix, hdrlen, pktlen, try0;
-	u_int8_t rix, txrate, ctsrate;
-	u_int8_t cix = 0xff;		/* NB: silence compiler */
+	int keyix, hdrlen, pktlen, try0 = 0;
+	u_int8_t rix = 0, txrate = 0;
 	struct ath_desc *ds;
-	struct ath_txq *txq;
 	struct ieee80211_frame *wh;
-	u_int subtype, flags, ctsduration;
+	u_int subtype, flags;
 	HAL_PKT_TYPE atype;
 	const HAL_RATE_TABLE *rt;
 	HAL_BOOL shortPreamble;
 	struct ath_node *an;
 	u_int pri;
-	uint8_t try[4], rate[4];
 
-	bzero(try, sizeof(try));
-	bzero(rate, sizeof(rate));
+	/*
+	 * 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_TX_LOCK_ASSERT(sc);
 
 	wh = mtod(m0, struct ieee80211_frame *);
-	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
+	iswep = wh->i_fc[1] & IEEE80211_FC1_PROTECTED;
 	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
 	isfrag = m0->m_flags & M_FRAG;
 	hdrlen = ieee80211_anyhdrsize(wh);
@@ -528,7 +1596,8 @@
 	pktlen = m0->m_pkthdr.len - (hdrlen & 3);
 
 	/* Handle encryption twiddling if needed */
-	if (! ath_tx_tag_crypto(sc, ni, m0, iswep, isfrag, &hdrlen, &pktlen, &keyix)) {
+	if (! ath_tx_tag_crypto(sc, ni, m0, iswep, isfrag, &hdrlen,
+	    &pktlen, &keyix)) {
 		ath_freetx(m0);
 		return EIO;
 	}
@@ -568,7 +1637,8 @@
 	}
 
 	an = ATH_NODE(ni);
-	flags = HAL_TXDESC_CLRDMASK;		/* XXX needed for crypto errs */
+	//flags = HAL_TXDESC_CLRDMASK;		/* XXX needed for crypto errs */
+	flags = 0;
 	ismrr = 0;				/* default no multi-rate retry*/
 	pri = M_WME_GETAC(m0);			/* honor classification */
 	/* XXX use txparams instead of fixed values */
@@ -624,12 +1694,12 @@
 				txrate |= rt->info[rix].shortPreamble;
 			try0 = ATH_TXMAXTRY;	/* XXX?too many? */
 		} else {
-			ath_rate_findrate(sc, an, shortPreamble, pktlen,
-				&rix, &try0, &txrate);
-			sc->sc_txrix = rix;		/* for LED blinking */
-			sc->sc_lastdatarix = rix;	/* for fast frames */
-			if (try0 != ATH_TXMAXTRY)
-				ismrr = 1;
+			/*
+			 * Do rate lookup on each TX, rather than using
+			 * the hard-coded TX information decided here.
+			 */
+			ismrr = 1;
+			bf->bf_state.bfs_doratelookup = 1;
 		}
 		if (cap->cap_wmeParams[pri].wmep_noackPolicy)
 			flags |= HAL_TXDESC_NOACK;
@@ -638,19 +1708,44 @@
 		if_printf(ifp, "bogus frame type 0x%x (%s)\n",
 			wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
 		/* XXX statistic */
+		/* XXX free tx dmamap */
 		ath_freetx(m0);
 		return EIO;
 	}
-	txq = sc->sc_ac2q[pri];
 
 	/*
-	 * When servicing one or more stations in power-save mode
-	 * (or) if there is some mcast data waiting on the mcast
-	 * queue (to prevent out of order delivery) multicast
-	 * frames must be buffered until after the beacon.
+	 * 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 (ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth))
-		txq = &avp->av_mcastq;
+#if 0
+	if (txq != sc->sc_ac2q[pri]) {
+		DPRINTF(sc, ATH_DEBUG_XMIT, 
+		    "%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.
@@ -660,7 +1755,6 @@
 	} else if (pktlen > vap->iv_rtsthreshold &&
 	    (ni->ni_ath_flags & IEEE80211_NODE_FF) == 0) {
 		flags |= HAL_TXDESC_RTSENA;	/* RTS based on frame length */
-		cix = rt->info[rix].controlRate;
 		sc->sc_stats.ast_tx_rts++;
 	}
 	if (flags & HAL_TXDESC_NOACK)		/* NB: avoid double counting */
@@ -670,6 +1764,7 @@
 		DPRINTF(sc, ATH_DEBUG_TDMA,
 		    "%s: discard frame, ACK required w/ TDMA\n", __func__);
 		sc->sc_stats.ast_tdma_ack++;
+		/* XXX free tx dmamap */
 		ath_freetx(m0);
 		return EIO;
 	}
@@ -676,105 +1771,30 @@
 #endif
 
 	/*
-	 * If 802.11g protection is enabled, determine whether
-	 * to use RTS/CTS or just CTS.  Note that this is only
-	 * done for OFDM unicast frames.
-	 */
-	if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
-	    rt->info[rix].phy == IEEE80211_T_OFDM &&
-	    (flags & HAL_TXDESC_NOACK) == 0) {
-		/* XXX fragments must use CCK rates w/ protection */
-		if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
-			flags |= HAL_TXDESC_RTSENA;
-		else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
-			flags |= HAL_TXDESC_CTSENA;
-		if (isfrag) {
-			/*
-			 * For frags it would be desirable to use the
-			 * highest CCK rate for RTS/CTS.  But stations
-			 * farther away may detect it at a lower CCK rate
-			 * so use the configured protection rate instead
-			 * (for now).
-			 */
-			cix = rt->info[sc->sc_protrix].controlRate;
-		} else
-			cix = rt->info[sc->sc_protrix].controlRate;
-		sc->sc_stats.ast_tx_protect++;
-	}
-
-#if 0
-	/*
-	 * If 11n protection is enabled and it's a HT frame,
-	 * enable RTS.
+	 * Determine if a tx interrupt should be generated for
+	 * this descriptor.  We take a tx interrupt to reap
+	 * descriptors when the h/w hits an EOL condition or
+	 * when the descriptor is specifically marked to generate
+	 * an interrupt.  We periodically mark descriptors in this
+	 * way to insure timely replenishing of the supply needed
+	 * for sending frames.  Defering interrupts reduces system
+	 * load and potentially allows more concurrent work to be
+	 * done but if done to aggressively can cause senders to
+	 * backup.
 	 *
-	 * XXX ic_htprotmode or ic_curhtprotmode?
-	 * XXX should it_htprotmode only matter if ic_curhtprotmode 
-	 * XXX indicates it's not a HT pure environment?
+	 * NB: use >= to deal with sc_txintrperiod changing
+	 *     dynamically through sysctl.
 	 */
-	if ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) &&
-	    rt->info[rix].phy == IEEE80211_T_HT &&
-	    (flags & HAL_TXDESC_NOACK) == 0) {
-		cix = rt->info[sc->sc_protrix].controlRate;
-	    	flags |= HAL_TXDESC_RTSENA;
-		sc->sc_stats.ast_tx_htprotect++;
+	if (flags & HAL_TXDESC_INTREQ) {
+		txq->axq_intrcnt = 0;
+	} else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) {
+		flags |= HAL_TXDESC_INTREQ;
+		txq->axq_intrcnt = 0;
 	}
-#endif
 
-	/*
-	 * Calculate duration.  This logically belongs in the 802.11
-	 * layer but it lacks sufficient information to calculate it.
-	 */
-	if ((flags & HAL_TXDESC_NOACK) == 0 &&
-	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
-		u_int16_t dur;
-		if (shortPreamble)
-			dur = rt->info[rix].spAckDuration;
-		else
-			dur = rt->info[rix].lpAckDuration;
-		if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) {
-			dur += dur;		/* additional SIFS+ACK */
-			KASSERT(m0->m_nextpkt != NULL, ("no fragment"));
-			/*
-			 * Include the size of next fragment so NAV is
-			 * updated properly.  The last fragment uses only
-			 * the ACK duration
-			 */
-			dur += ath_hal_computetxtime(ah, rt,
-					m0->m_nextpkt->m_pkthdr.len,
-					rix, shortPreamble);
-		}
-		if (isfrag) {
-			/*
-			 * Force hardware to use computed duration for next
-			 * fragment by disabling multi-rate retry which updates
-			 * duration based on the multi-rate duration table.
-			 */
-			ismrr = 0;
-			try0 = ATH_TXMGTTRY;	/* XXX? */
-		}
-		*(u_int16_t *)wh->i_dur = htole16(dur);
-	}
+	/* This point forward is actual TX bits */
 
 	/*
-	 * Calculate RTS/CTS rate and duration if needed.
-	 */
-	ctsduration = 0;
-	if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
-		ctsrate = ath_tx_get_rtscts_rate(ah, rt, rix, cix, shortPreamble);
-
-		/* The 11n chipsets do ctsduration calculations for you */
-		if (! ath_tx_is_11n(sc))
-			ctsduration = ath_tx_calc_ctsduration(ah, rix, cix, shortPreamble,
-			    pktlen, rt, flags);
-		/*
-		 * Must disable multi-rate retry when using RTS/CTS.
-		 */
-		ismrr = 0;
-		try0 = ATH_TXMGTTRY;		/* XXX */
-	} else
-		ctsrate = 0;
-
-	/*
 	 * At this point we are committed to sending the frame
 	 * and we don't need to look at m_nextpkt; clear it in
 	 * case this frame is part of frag chain.
@@ -795,77 +1815,271 @@
 		if (isfrag)
 			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
 		sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate;
-		sc->sc_tx_th.wt_txpower = ni->ni_txpower;
+		sc->sc_tx_th.wt_txpower = ieee80211_get_node_txpower(ni);
 		sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
 
 		ieee80211_radiotap_tx(vap, m0);
 	}
 
+	/* Blank the legacy rate array */
+	bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc));
+
 	/*
-	 * Determine if a tx interrupt should be generated for
-	 * this descriptor.  We take a tx interrupt to reap
-	 * descriptors when the h/w hits an EOL condition or
-	 * when the descriptor is specifically marked to generate
-	 * an interrupt.  We periodically mark descriptors in this
-	 * way to insure timely replenishing of the supply needed
-	 * for sending frames.  Defering interrupts reduces system
-	 * load and potentially allows more concurrent work to be
-	 * done but if done to aggressively can cause senders to
-	 * backup.
+	 * ath_buf_set_rate needs at least one rate/try to setup
+	 * the rate scenario.
+	 */
+	bf->bf_state.bfs_rc[0].rix = rix;
+	bf->bf_state.bfs_rc[0].tries = try0;
+	bf->bf_state.bfs_rc[0].ratecode = txrate;
+
+	/* Store the decided rate index values away */
+	bf->bf_state.bfs_pktlen = pktlen;
+	bf->bf_state.bfs_hdrlen = hdrlen;
+	bf->bf_state.bfs_atype = atype;
+	bf->bf_state.bfs_txpower = ieee80211_get_node_txpower(ni);
+	bf->bf_state.bfs_txrate0 = txrate;
+	bf->bf_state.bfs_try0 = try0;
+	bf->bf_state.bfs_keyix = keyix;
+	bf->bf_state.bfs_txantenna = sc->sc_txantenna;
+	bf->bf_state.bfs_txflags = flags;
+	bf->bf_state.bfs_shpream = shortPreamble;
+
+	/* XXX this should be done in ath_tx_setrate() */
+	bf->bf_state.bfs_ctsrate0 = 0;	/* ie, no hard-coded ctsrate */
+	bf->bf_state.bfs_ctsrate = 0;	/* calculated later */
+	bf->bf_state.bfs_ctsduration = 0;
+	bf->bf_state.bfs_ismrr = ismrr;
+
+	return 0;
+}
+
+/*
+ * Queue a frame to the hardware or software queue.
+ *
+ * This can be called by the net80211 code.
+ *
+ * XXX what about locking? Or, push the seqno assign into the
+ * XXX aggregate scheduler so its serialised?
+ *
+ * XXX When sending management frames via ath_raw_xmit(),
+ *     should CLRDMASK be set unconditionally?
+ */
+int
+ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
+    struct ath_buf *bf, struct mbuf *m0)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ath_vap *avp = ATH_VAP(vap);
+	int r = 0;
+	u_int pri;
+	int tid;
+	struct ath_txq *txq;
+	int ismcast;
+	const struct ieee80211_frame *wh;
+	int is_ampdu, is_ampdu_tx, is_ampdu_pending;
+	ieee80211_seq seqno;
+	uint8_t type, subtype;
+	int queue_to_head;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * Determine the target hardware queue.
 	 *
-	 * NB: use >= to deal with sc_txintrperiod changing
-	 *     dynamically through sysctl.
+	 * For multicast frames, the txq gets overridden appropriately
+	 * depending upon the state of PS.
+	 *
+	 * For any other frame, we do a TID/QoS lookup inside the frame
+	 * to see what the TID should be. If it's a non-QoS frame, the
+	 * AC and TID are overridden. The TID/TXQ code assumes the
+	 * TID is on a predictable hardware TXQ, so we don't support
+	 * having a node TID queued to multiple hardware TXQs.
+	 * This may change in the future but would require some locking
+	 * fudgery.
 	 */
-	if (flags & HAL_TXDESC_INTREQ) {
-		txq->axq_intrcnt = 0;
-	} else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) {
-		flags |= HAL_TXDESC_INTREQ;
-		txq->axq_intrcnt = 0;
+	pri = ath_tx_getac(sc, m0);
+	tid = ath_tx_gettid(sc, m0);
+
+	txq = sc->sc_ac2q[pri];
+	wh = mtod(m0, struct ieee80211_frame *);
+	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+	/*
+	 * Enforce how deep the multicast queue can grow.
+	 *
+	 * XXX duplicated in ath_raw_xmit().
+	 */
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+		if (sc->sc_cabq->axq_depth + sc->sc_cabq->fifo.axq_depth
+		    > sc->sc_txq_mcastq_maxdepth) {
+			sc->sc_stats.ast_tx_mcastq_overflow++;
+			m_freem(m0);
+			return (ENOBUFS);
+		}
 	}
 
-	if (ath_tx_is_11n(sc)) {
-		rate[0] = rix;
-		try[0] = try0;
+	/*
+	 * Enforce how deep the unicast queue can grow.
+	 *
+	 * If the node is in power save then we don't want
+	 * the software queue to grow too deep, or a node may
+	 * end up consuming all of the ath_buf entries.
+	 *
+	 * For now, only do this for DATA frames.
+	 *
+	 * We will want to cap how many management/control
+	 * frames get punted to the software queue so it doesn't
+	 * fill up.  But the correct solution isn't yet obvious.
+	 * In any case, this check should at least let frames pass
+	 * that we are direct-dispatching.
+	 *
+	 * XXX TODO: duplicate this to the raw xmit path!
+	 */
+	if (type == IEEE80211_FC0_TYPE_DATA &&
+	    ATH_NODE(ni)->an_is_powersave &&
+	    ATH_NODE(ni)->an_swq_depth >
+	     sc->sc_txq_node_psq_maxdepth) {
+		sc->sc_stats.ast_tx_node_psq_overflow++;
+		m_freem(m0);
+		return (ENOBUFS);
 	}
 
+	/* A-MPDU TX */
+	is_ampdu_tx = ath_tx_ampdu_running(sc, ATH_NODE(ni), tid);
+	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: tid=%d, ac=%d, is_ampdu=%d\n",
+	    __func__, tid, pri, is_ampdu);
+
+	/* Set local packet state, used to queue packets to hardware */
+	bf->bf_state.bfs_tid = tid;
+	bf->bf_state.bfs_tx_queue = txq->axq_qnum;
+	bf->bf_state.bfs_pri = pri;
+
+#if 1
 	/*
-	 * Formulate first tx descriptor with tx controls.
+	 * When servicing one or more stations in power-save mode
+	 * (or) if there is some mcast data waiting on the mcast
+	 * queue (to prevent out of order delivery) multicast frames
+	 * must be bufferd until after the beacon.
+	 *
+	 * TODO: we should lock the mcastq before we check the length.
 	 */
-	/* XXX check return value? */
-	/* XXX is this ok to call for 11n descriptors? */
-	/* XXX or should it go through the first, next, last 11n calls? */
-	ath_hal_setuptxdesc(ah, ds
-		, pktlen		/* packet length */
-		, hdrlen		/* header length */
-		, atype			/* Atheros packet type */
-		, ni->ni_txpower	/* txpower */
-		, txrate, try0		/* series 0 rate/tries */
-		, keyix			/* key cache index */
-		, sc->sc_txantenna	/* antenna mode */
-		, flags			/* flags */
-		, ctsrate		/* rts/cts rate */
-		, ctsduration		/* rts/cts duration */
-	);
-	bf->bf_txflags = flags;
+	if (sc->sc_cabq_enable && ismcast && (vap->iv_ps_sta || avp->av_mcastq.axq_depth)) {
+		txq = &avp->av_mcastq;
+		/*
+		 * Mark the frame as eventually belonging on the CAB
+		 * queue, so the descriptor setup functions will
+		 * correctly initialise the descriptor 'qcuId' field.
+		 */
+		bf->bf_state.bfs_tx_queue = sc->sc_cabq->axq_qnum;
+	}
+#endif
+
+	/* Do the generic frame setup */
+	/* XXX should just bzero the bf_state? */
+	bf->bf_state.bfs_dobaw = 0;
+
+	/* A-MPDU TX? Manually set sequence number */
 	/*
-	 * Setup the multi-rate retry state only when we're
-	 * going to use it.  This assumes ath_hal_setuptxdesc
-	 * initializes the descriptors (so we don't have to)
-	 * when the hardware supports multi-rate retry and
-	 * we don't use it.
+	 * Don't do it whilst pending; the net80211 layer still
+	 * assigns them.
 	 */
-        if (ismrr) {
-                if (ath_tx_is_11n(sc))
-                        ath_rate_getxtxrates(sc, an, rix, rate, try);
-                else
-                        ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix);
-        }
+	if (is_ampdu_tx) {
+		/*
+		 * 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_is_11n(sc)) {
-                ath_buf_set_rate(sc, ni, bf, pktlen, flags, ctsrate, (atype == HAL_PKT_TYPE_PSPOLL), rate, try);
-        }
+		/*
+		 * 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;
+		}
+	}
 
-	ath_tx_handoff(sc, txq, bf);
+	/*
+	 * 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;
+
+	/* Is ampdu pending? fetch the seqno and print it out */
+	if (is_ampdu_pending)
+		DPRINTF(sc, ATH_DEBUG_SW_TX,
+		    "%s: tid %d: ampdu pending, seqno %d\n",
+		    __func__, tid, M_SEQNO_GET(m0));
+
+	/* This also sets up the DMA map */
+	r = ath_tx_normal_setup(sc, ni, bf, m0, txq);
+
+	if (r != 0)
+		goto done;
+
+	/* At this point m0 could have changed! */
+	m0 = bf->bf_m;
+
+#if 1
+	/*
+	 * If it's a multicast frame, do a direct-dispatch to the
+	 * destination hardware queue. Don't bother software
+	 * queuing it.
+	 */
+	/*
+	 * If it's a BAR frame, do a direct dispatch to the
+	 * destination hardware queue. Don't bother software
+	 * queuing it, as the TID will now be paused.
+	 * Sending a BAR frame can occur from the net80211 txa timer
+	 * (ie, retries) or from the ath txtask (completion call.)
+	 * It queues directly to hardware because the TID is paused
+	 * at this point (and won't be unpaused until the BAR has
+	 * either been TXed successfully or max retries has been
+	 * reached.)
+	 */
+	/*
+	 * Until things are better debugged - if this node is asleep
+	 * and we're sending it a non-BAR frame, direct dispatch it.
+	 * Why? Because we need to figure out what's actually being
+	 * sent - eg, during reassociation/reauthentication after
+	 * the node (last) disappeared whilst asleep, the driver should
+	 * have unpaused/unsleep'ed the node.  So until that is
+	 * sorted out, use this workaround.
+	 */
+	if (txq == &avp->av_mcastq) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX,
+		    "%s: bf=%p: mcastq: TX'ing\n", __func__, bf);
+		bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+		ath_tx_xmit_normal(sc, txq, bf);
+	} else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0,
+	    &queue_to_head)) {
+		ath_tx_swq(sc, ni, txq, queue_to_head, bf);
+	} else {
+		bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+		ath_tx_xmit_normal(sc, txq, bf);
+	}
+#else
+	/*
+	 * For now, since there's no software queue,
+	 * direct-dispatch to the hardware.
+	 */
+	bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+	/*
+	 * Update the current leak count if
+	 * we're leaking frames; and set the
+	 * MORE flag as appropriate.
+	 */
+	ath_tx_leak_count_update(sc, tid, bf);
+	ath_tx_xmit_normal(sc, txq, bf);
+#endif
+done:
 	return 0;
 }
 
@@ -880,17 +2094,19 @@
 	struct ieee80211vap *vap = ni->ni_vap;
 	int error, ismcast, ismrr;
 	int keyix, hdrlen, pktlen, try0, txantenna;
-	u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3;
+	u_int8_t rix, txrate;
 	struct ieee80211_frame *wh;
-	u_int flags, ctsduration;
+	u_int flags;
 	HAL_PKT_TYPE atype;
 	const HAL_RATE_TABLE *rt;
 	struct ath_desc *ds;
 	u_int pri;
-	uint8_t try[4], rate[4];
+	int o_tid = -1;
+	int do_override;
+	uint8_t type, subtype;
+	int queue_to_head;
 
-	bzero(try, sizeof(try));
-	bzero(rate, sizeof(rate));
+	ATH_TX_LOCK_ASSERT(sc);
 
 	wh = mtod(m0, struct ieee80211_frame *);
 	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
@@ -902,8 +2118,37 @@
 	/* XXX honor IEEE80211_BPF_DATAPAD */
 	pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN;
 
+	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+	ATH_KTR(sc, ATH_KTR_TX, 2,
+	     "ath_tx_raw_start: ni=%p, bf=%p, raw", ni, bf);
+
+	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
+		DPRINTF(sc, ATH_DEBUG_XMIT, 
+		    "%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);
+	}
+
 	/* Handle encryption twiddling if needed */
-	if (! ath_tx_tag_crypto(sc, ni, m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0, &hdrlen, &pktlen, &keyix)) {
+	if (! ath_tx_tag_crypto(sc, ni,
+	    m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0,
+	    &hdrlen, &pktlen, &keyix)) {
 		ath_freetx(m0);
 		return EIO;
 	}
@@ -910,6 +2155,10 @@
 	/* packet header may have moved, reset our local pointer */
 	wh = mtod(m0, struct ieee80211_frame *);
 
+	/* Do the generic frame setup */
+	/* XXX should just bzero the bf_state? */
+	bf->bf_state.bfs_dobaw = 0;
+
 	error = ath_tx_dmasetup(sc, bf, m0);
 	if (error != 0)
 		return error;
@@ -917,12 +2166,16 @@
 	wh = mtod(m0, struct ieee80211_frame *);
 	bf->bf_node = ni;			/* NB: held reference */
 
+	/* Always enable CLRDMASK for raw frames for now.. */
 	flags = HAL_TXDESC_CLRDMASK;		/* XXX needed for crypto errs */
 	flags |= HAL_TXDESC_INTREQ;		/* force interrupt */
 	if (params->ibp_flags & IEEE80211_BPF_RTS)
 		flags |= HAL_TXDESC_RTSENA;
-	else if (params->ibp_flags & IEEE80211_BPF_CTS)
+	else if (params->ibp_flags & IEEE80211_BPF_CTS) {
+		/* XXX assume 11g/11n protection? */
+		bf->bf_state.bfs_doprot = 1;
 		flags |= HAL_TXDESC_CTSENA;
+	}
 	/* XXX leave ismcast to injector? */
 	if ((params->ibp_flags & IEEE80211_BPF_NOACK) || ismcast)
 		flags |= HAL_TXDESC_NOACK;
@@ -940,23 +2193,13 @@
 	if (txantenna == 0)			/* XXX? */
 		txantenna = sc->sc_txantenna;
 
-	ctsduration = 0;
-	if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
-		cix = ath_tx_findrix(sc, params->ibp_ctsrate);
-		ctsrate = ath_tx_get_rtscts_rate(ah, rt, rix, cix, params->ibp_flags & IEEE80211_BPF_SHORTPRE);
-		/* The 11n chipsets do ctsduration calculations for you */
-		if (! ath_tx_is_11n(sc))
-			ctsduration = ath_tx_calc_ctsduration(ah, rix, cix,
-			    params->ibp_flags & IEEE80211_BPF_SHORTPRE, pktlen,
-			    rt, flags);
-		/*
-		 * Must disable multi-rate retry when using RTS/CTS.
-		 */
-		ismrr = 0;			/* XXX */
-	} else
-		ctsrate = 0;
+	/*
+	 * Since ctsrate is fixed, store it away for later
+	 * use when the descriptor fields are being set.
+	 */
+	if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA))
+		bf->bf_state.bfs_ctsrate0 = params->ibp_ctsrate;
 
-	pri = params->ibp_pri & 3;
 	/*
 	 * NB: we mark all packets as type PSPOLL so the h/w won't
 	 * set the sequence number, duration, etc.
@@ -972,12 +2215,13 @@
 
 		sc->sc_tx_th.wt_tsf = htole64(tsf);
 		sc->sc_tx_th.wt_flags = sc->sc_hwmap[rix].txflags;
-		if (wh->i_fc[1] & IEEE80211_FC1_WEP)
+		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)
 			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
 		if (m0->m_flags & M_FRAG)
 			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
 		sc->sc_tx_th.wt_rate = sc->sc_hwmap[rix].ieeerate;
-		sc->sc_tx_th.wt_txpower = ni->ni_txpower;
+		sc->sc_tx_th.wt_txpower = MIN(params->ibp_power,
+		    ieee80211_get_node_txpower(ni));
 		sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
 
 		ieee80211_radiotap_tx(vap, m0);
@@ -988,77 +2232,110 @@
 	 */
 	ds = bf->bf_desc;
 	/* XXX check return value? */
-	ath_hal_setuptxdesc(ah, ds
-		, pktlen		/* packet length */
-		, hdrlen		/* header length */
-		, atype			/* Atheros packet type */
-		, params->ibp_power	/* txpower */
-		, txrate, try0		/* series 0 rate/tries */
-		, keyix			/* key cache index */
-		, txantenna		/* antenna mode */
-		, flags			/* flags */
-		, ctsrate		/* rts/cts rate */
-		, ctsduration		/* rts/cts duration */
-	);
-	bf->bf_txflags = flags;
 
-	if (ath_tx_is_11n(sc)) {
-		rate[0] = ath_tx_findrix(sc, params->ibp_rate0);
-		try[0] = params->ibp_try0;
+	/* Store the decided rate index values away */
+	bf->bf_state.bfs_pktlen = pktlen;
+	bf->bf_state.bfs_hdrlen = hdrlen;
+	bf->bf_state.bfs_atype = atype;
+	bf->bf_state.bfs_txpower = MIN(params->ibp_power,
+	    ieee80211_get_node_txpower(ni));
+	bf->bf_state.bfs_txrate0 = txrate;
+	bf->bf_state.bfs_try0 = try0;
+	bf->bf_state.bfs_keyix = keyix;
+	bf->bf_state.bfs_txantenna = txantenna;
+	bf->bf_state.bfs_txflags = flags;
+	bf->bf_state.bfs_shpream =
+	    !! (params->ibp_flags & IEEE80211_BPF_SHORTPRE);
 
-		if (ismrr) {
-			/* Remember, rate[] is actually an array of rix's -adrian */
-			rate[0] = ath_tx_findrix(sc, params->ibp_rate0);
-			rate[1] = ath_tx_findrix(sc, params->ibp_rate1);
-			rate[2] = ath_tx_findrix(sc, params->ibp_rate2);
-			rate[3] = ath_tx_findrix(sc, params->ibp_rate3);
+	/* Set local packet state, used to queue packets to hardware */
+	bf->bf_state.bfs_tid = WME_AC_TO_TID(pri);
+	bf->bf_state.bfs_tx_queue = sc->sc_ac2q[pri]->axq_qnum;
+	bf->bf_state.bfs_pri = pri;
 
-			try[0] = params->ibp_try0;
-			try[1] = params->ibp_try1;
-			try[2] = params->ibp_try2;
-			try[3] = params->ibp_try3;
-		}
-	} else {
-		if (ismrr) {
-			rix = ath_tx_findrix(sc, params->ibp_rate1);
-			rate1 = rt->info[rix].rateCode;
-			if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
-				rate1 |= rt->info[rix].shortPreamble;
-			if (params->ibp_try2) {
-				rix = ath_tx_findrix(sc, params->ibp_rate2);
-				rate2 = rt->info[rix].rateCode;
-				if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
-					rate2 |= rt->info[rix].shortPreamble;
-			} else
-				rate2 = 0;
-			if (params->ibp_try3) {
-				rix = ath_tx_findrix(sc, params->ibp_rate3);
-				rate3 = rt->info[rix].rateCode;
-				if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
-					rate3 |= rt->info[rix].shortPreamble;
-			} else
-				rate3 = 0;
-			ath_hal_setupxtxdesc(ah, ds
-				, rate1, params->ibp_try1	/* series 1 */
-				, rate2, params->ibp_try2	/* series 2 */
-				, rate3, params->ibp_try3	/* series 3 */
-			);
-		}
+	/* XXX this should be done in ath_tx_setrate() */
+	bf->bf_state.bfs_ctsrate = 0;
+	bf->bf_state.bfs_ctsduration = 0;
+	bf->bf_state.bfs_ismrr = ismrr;
+
+	/* Blank the legacy rate array */
+	bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc));
+
+	bf->bf_state.bfs_rc[0].rix =
+	    ath_tx_findrix(sc, params->ibp_rate0);
+	bf->bf_state.bfs_rc[0].tries = try0;
+	bf->bf_state.bfs_rc[0].ratecode = txrate;
+
+	if (ismrr) {
+		int rix;
+
+		rix = ath_tx_findrix(sc, params->ibp_rate1);
+		bf->bf_state.bfs_rc[1].rix = rix;
+		bf->bf_state.bfs_rc[1].tries = params->ibp_try1;
+
+		rix = ath_tx_findrix(sc, params->ibp_rate2);
+		bf->bf_state.bfs_rc[2].rix = rix;
+		bf->bf_state.bfs_rc[2].tries = params->ibp_try2;
+
+		rix = ath_tx_findrix(sc, params->ibp_rate3);
+		bf->bf_state.bfs_rc[3].rix = rix;
+		bf->bf_state.bfs_rc[3].tries = params->ibp_try3;
 	}
+	/*
+	 * All the required rate control decisions have been made;
+	 * fill in the rc flags.
+	 */
+	ath_tx_rate_fill_rcflags(sc, bf);
 
-	if (ath_tx_is_11n(sc)) {
+	/* NB: no buffered multicast in power save support */
+
+	/*
+	 * If we're overiding the ADDBA destination, dump directly
+	 * into the hardware queue, right after any pending
+	 * frames to that node are.
+	 */
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: dooverride=%d\n",
+	    __func__, do_override);
+
+#if 1
+	/*
+	 * Put addba frames in the right place in the right TID/HWQ.
+	 */
+	if (do_override) {
+		bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
 		/*
-		 * notice that rix doesn't include any of the "magic" flags txrate
-		 * does for communicating "other stuff" to the HAL.
+		 * XXX if it's addba frames, should we be leaking
+		 * them out via the frame leak method?
+		 * XXX for now let's not risk it; but we may wish
+		 * to investigate this later.
 		 */
-		ath_buf_set_rate(sc, ni, bf, pktlen, flags, ctsrate, (atype == HAL_PKT_TYPE_PSPOLL), rate, try);
+		ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
+	} else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0,
+	    &queue_to_head)) {
+		/* Queue to software queue */
+		ath_tx_swq(sc, ni, sc->sc_ac2q[pri], queue_to_head, bf);
+	} else {
+		bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+		ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
 	}
-
-	/* NB: no buffered multicast in power save support */
-	ath_tx_handoff(sc, sc->sc_ac2q[pri], bf);
+#else
+	/* Direct-dispatch to the hardware */
+	bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+	/*
+	 * Update the current leak count if
+	 * we're leaking frames; and set the
+	 * MORE flag as appropriate.
+	 */
+	ath_tx_leak_count_update(sc, tid, bf);
+	ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
+#endif
 	return 0;
 }
 
+/*
+ * Send a raw frame.
+ *
+ * This can be called by net80211.
+ */
 int
 ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
 	const struct ieee80211_bpf_params *params)
@@ -1067,8 +2344,22 @@
 	struct ifnet *ifp = ic->ic_ifp;
 	struct ath_softc *sc = ifp->if_softc;
 	struct ath_buf *bf;
-	int error;
+	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+	int error = 0;
 
+	ATH_PCU_LOCK(sc);
+	if (sc->sc_inreset_cnt > 0) {
+		DPRINTF(sc, ATH_DEBUG_XMIT, 
+		    "%s: sc_inreset_cnt > 0; bailing\n", __func__);
+		error = EIO;
+		ATH_PCU_UNLOCK(sc);
+		goto bad0;
+	}
+	sc->sc_txstart_cnt++;
+	ATH_PCU_UNLOCK(sc);
+
+	ATH_TX_LOCK(sc);
+
 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) {
 		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard frame, %s", __func__,
 		    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ?
@@ -1077,10 +2368,29 @@
 		error = ENETDOWN;
 		goto bad;
 	}
+
 	/*
+	 * Enforce how deep the multicast queue can grow.
+	 *
+	 * XXX duplicated in ath_tx_start().
+	 */
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+		if (sc->sc_cabq->axq_depth + sc->sc_cabq->fifo.axq_depth
+		    > sc->sc_txq_mcastq_maxdepth) {
+			sc->sc_stats.ast_tx_mcastq_overflow++;
+			error = ENOBUFS;
+		}
+
+		if (error != 0) {
+			m_freem(m);
+			goto bad;
+		}
+	}
+
+	/*
 	 * 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);
@@ -1087,6 +2397,8 @@
 		error = ENOBUFS;
 		goto bad;
 	}
+	ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: m=%p, params=%p, bf=%p\n",
+	    m, params,  bf);
 
 	if (params == NULL) {
 		/*
@@ -1111,14 +2423,3641 @@
 	ifp->if_opackets++;
 	sc->sc_stats.ast_tx_raw++;
 
+	/*
+	 * Update the TIM - if there's anything queued to the
+	 * software queue and power save is enabled, we should
+	 * set the TIM.
+	 */
+	ath_tx_update_tim(sc, ni, 1);
+
+	ATH_TX_UNLOCK(sc);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_txstart_cnt--;
+	ATH_PCU_UNLOCK(sc);
+
 	return 0;
 bad2:
+	ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: bad2: m=%p, params=%p, "
+	    "bf=%p",
+	    m,
+	    params,
+	    bf);
 	ATH_TXBUF_LOCK(sc);
-	STAILQ_INSERT_HEAD(&sc->sc_txbuf, bf, bf_list);
+	ath_returnbuf_head(sc, bf);
 	ATH_TXBUF_UNLOCK(sc);
 bad:
+
+	ATH_TX_UNLOCK(sc);
+
+	ATH_PCU_LOCK(sc);
+	sc->sc_txstart_cnt--;
+	ATH_PCU_UNLOCK(sc);
+bad0:
+	ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p",
+	    m, params);
 	ifp->if_oerrors++;
 	sc->sc_stats.ast_tx_raw_fail++;
 	ieee80211_free_node(ni);
+
 	return error;
 }
+
+/* Some helper functions */
+
+/*
+ * ADDBA (and potentially others) need to be placed in the same
+ * hardware queue as the TID/node it's relating to. This is so
+ * it goes out after any pending non-aggregate frames to the
+ * same node/TID.
+ *
+ * If this isn't done, the ADDBA can go out before the frames
+ * queued in hardware. Even though these frames have a sequence
+ * number -earlier- than the ADDBA can be transmitted (but
+ * no frames whose sequence numbers are after the ADDBA should
+ * be!) they'll arrive after the ADDBA - and the receiving end
+ * will simply drop them as being out of the BAW.
+ *
+ * The frames can't be appended to the TID software queue - it'll
+ * never be sent out. So these frames have to be directly
+ * dispatched to the hardware, rather than queued in software.
+ * So if this function returns true, the TXQ has to be
+ * overridden and it has to be directly dispatched.
+ *
+ * It's a dirty hack, but someone's gotta do it.
+ */
+
+/*
+ * XXX doesn't belong here!
+ */
+static int
+ieee80211_is_action(struct ieee80211_frame *wh)
+{
+	/* Type: Management frame? */
+	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
+	    IEEE80211_FC0_TYPE_MGT)
+		return 0;
+
+	/* Subtype: Action frame? */
+	if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) !=
+	    IEEE80211_FC0_SUBTYPE_ACTION)
+		return 0;
+
+	return 1;
+}
+
+#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
+/*
+ * Return an alternate TID for ADDBA request frames.
+ *
+ * Yes, this likely should be done in the net80211 layer.
+ */
+static int
+ath_tx_action_frame_override_queue(struct ath_softc *sc,
+    struct ieee80211_node *ni,
+    struct mbuf *m0, int *tid)
+{
+	struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *);
+	struct ieee80211_action_ba_addbarequest *ia;
+	uint8_t *frm;
+	uint16_t baparamset;
+
+	/* Not action frame? Bail */
+	if (! ieee80211_is_action(wh))
+		return 0;
+
+	/* XXX Not needed for frames we send? */
+#if 0
+	/* Correct length? */
+	if (! ieee80211_parse_action(ni, m))
+		return 0;
+#endif
+
+	/* Extract out action frame */
+	frm = (u_int8_t *)&wh[1];
+	ia = (struct ieee80211_action_ba_addbarequest *) frm;
+
+	/* Not ADDBA? Bail */
+	if (ia->rq_header.ia_category != IEEE80211_ACTION_CAT_BA)
+		return 0;
+	if (ia->rq_header.ia_action != IEEE80211_ACTION_BA_ADDBA_REQUEST)
+		return 0;
+
+	/* Extract TID, return it */
+	baparamset = le16toh(ia->rq_baparamset);
+	*tid = (int) MS(baparamset, IEEE80211_BAPS_TID);
+
+	return 1;
+}
+#undef	MS
+
+/* Per-node software queue operations */
+
+/*
+ * Add the current packet to the given BAW.
+ * It is assumed that the current packet
+ *
+ * + fits inside the BAW;
+ * + already has had a sequence number allocated.
+ *
+ * Since the BAW status may be modified by both the ath task and
+ * the net80211/ifnet contexts, the TID must be locked.
+ */
+void
+ath_tx_addto_baw(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid, struct ath_buf *bf)
+{
+	int index, cindex;
+	struct ieee80211_tx_ampdu *tap;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (bf->bf_state.bfs_isretried)
+		return;
+
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+
+	if (! bf->bf_state.bfs_dobaw) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: dobaw=0, seqno=%d, window %d:%d\n",
+		    __func__, SEQNO(bf->bf_state.bfs_seqno),
+		    tap->txa_start, tap->txa_wnd);
+	}
+
+	if (bf->bf_state.bfs_addedbaw)
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: re-added? tid=%d, seqno %d; window %d:%d; "
+		    "baw head=%d tail=%d\n",
+		    __func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
+		    tap->txa_start, tap->txa_wnd, tid->baw_head,
+		    tid->baw_tail);
+
+	/*
+	 * Verify that the given sequence number is not outside of the
+	 * BAW.  Complain loudly if that's the case.
+	 */
+	if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
+	    SEQNO(bf->bf_state.bfs_seqno))) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: bf=%p: outside of BAW?? tid=%d, seqno %d; window %d:%d; "
+		    "baw head=%d tail=%d\n",
+		    __func__, bf, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
+		    tap->txa_start, tap->txa_wnd, tid->baw_head,
+		    tid->baw_tail);
+	}
+
+	/*
+	 * ni->ni_txseqs[] is the currently allocated seqno.
+	 * the txa state contains the current baw start.
+	 */
+	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: tid=%d, seqno %d; window %d:%d; index=%d cindex=%d "
+	    "baw head=%d tail=%d\n",
+	    __func__, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
+	    tap->txa_start, tap->txa_wnd, index, cindex, tid->baw_head,
+	    tid->baw_tail);
+
+
+#if 0
+	assert(tid->tx_buf[cindex] == NULL);
+#endif
+	if (tid->tx_buf[cindex] != NULL) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: ba packet dup (index=%d, cindex=%d, "
+		    "head=%d, tail=%d)\n",
+		    __func__, index, cindex, tid->baw_head, tid->baw_tail);
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: BA bf: %p; seqno=%d ; new bf: %p; seqno=%d\n",
+		    __func__,
+		    tid->tx_buf[cindex],
+		    SEQNO(tid->tx_buf[cindex]->bf_state.bfs_seqno),
+		    bf,
+		    SEQNO(bf->bf_state.bfs_seqno)
+		);
+	}
+	tid->tx_buf[cindex] = bf;
+
+	if (index >= ((tid->baw_tail - tid->baw_head) &
+	    (ATH_TID_MAX_BUFS - 1))) {
+		tid->baw_tail = cindex;
+		INCR(tid->baw_tail, ATH_TID_MAX_BUFS);
+	}
+}
+
+/*
+ * Flip the BAW buffer entry over from the existing one to the new one.
+ *
+ * When software retransmitting a (sub-)frame, it is entirely possible that
+ * the frame ath_buf is marked as BUSY and can't be immediately reused.
+ * In that instance the buffer is cloned and the new buffer is used for
+ * retransmit. We thus need to update the ath_buf slot in the BAW buf
+ * tracking array to maintain consistency.
+ */
+static void
+ath_tx_switch_baw_buf(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid, struct ath_buf *old_bf, struct ath_buf *new_bf)
+{
+	int index, cindex;
+	struct ieee80211_tx_ampdu *tap;
+	int seqno = SEQNO(old_bf->bf_state.bfs_seqno);
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	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);
+
+	/*
+	 * Just warn for now; if it happens then we should find out
+	 * about it. It's highly likely the aggregation session will
+	 * soon hang.
+	 */
+	if (old_bf->bf_state.bfs_seqno != new_bf->bf_state.bfs_seqno) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: retransmitted buffer"
+		    " has mismatching seqno's, BA session may hang.\n",
+		    __func__);
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: old seqno=%d, new_seqno=%d\n", __func__,
+		    old_bf->bf_state.bfs_seqno, new_bf->bf_state.bfs_seqno);
+	}
+
+	if (tid->tx_buf[cindex] != old_bf) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: ath_buf pointer incorrect; "
+		    " has m BA session may hang.\n", __func__);
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: old bf=%p, new bf=%p\n", __func__, old_bf, new_bf);
+	}
+
+	tid->tx_buf[cindex] = new_bf;
+}
+
+/*
+ * seq_start - left edge of BAW
+ * seq_next - current/next sequence number to allocate
+ *
+ * Since the BAW status may be modified by both the ath task and
+ * the net80211/ifnet contexts, the TID must be locked.
+ */
+static void
+ath_tx_update_baw(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid, const struct ath_buf *bf)
+{
+	int index, cindex;
+	struct ieee80211_tx_ampdu *tap;
+	int seqno = SEQNO(bf->bf_state.bfs_seqno);
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	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: tid=%d, baw=%d:%d, seqno=%d, index=%d, cindex=%d, "
+	    "baw head=%d, tail=%d\n",
+	    __func__, tid->tid, tap->txa_start, tap->txa_wnd, seqno, index,
+	    cindex, tid->baw_head, tid->baw_tail);
+
+	/*
+	 * If this occurs then we have a big problem - something else
+	 * has slid tap->txa_start along without updating the BAW
+	 * tracking start/end pointers. Thus the TX BAW state is now
+	 * completely busted.
+	 *
+	 * But for now, since I haven't yet fixed TDMA and buffer cloning,
+	 * it's quite possible that a cloned buffer is making its way
+	 * here and causing it to fire off. Disable TDMA for now.
+	 */
+	if (tid->tx_buf[cindex] != bf) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+		    "%s: comp bf=%p, seq=%d; slot bf=%p, seqno=%d\n",
+		    __func__, bf, SEQNO(bf->bf_state.bfs_seqno),
+		    tid->tx_buf[cindex],
+		    (tid->tx_buf[cindex] != NULL) ?
+		      SEQNO(tid->tx_buf[cindex]->bf_state.bfs_seqno) : -1);
+	}
+
+	tid->tx_buf[cindex] = NULL;
+
+	while (tid->baw_head != tid->baw_tail &&
+	    !tid->tx_buf[tid->baw_head]) {
+		INCR(tap->txa_start, IEEE80211_SEQ_RANGE);
+		INCR(tid->baw_head, ATH_TID_MAX_BUFS);
+	}
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+	    "%s: baw is now %d:%d, baw head=%d\n",
+	    __func__, tap->txa_start, tap->txa_wnd, tid->baw_head);
+}
+
+static void
+ath_tx_leak_count_update(struct ath_softc *sc, struct ath_tid *tid,
+    struct ath_buf *bf)
+{
+	struct ieee80211_frame *wh;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (tid->an->an_leak_count > 0) {
+		wh = mtod(bf->bf_m, struct ieee80211_frame *);
+
+		/*
+		 * Update MORE based on the software/net80211 queue states.
+		 */
+		if ((tid->an->an_stack_psq > 0)
+		    || (tid->an->an_swq_depth > 0))
+			wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+		else
+			wh->i_fc[1] &= ~IEEE80211_FC1_MORE_DATA;
+
+		DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+		    "%s: %6D: leak count = %d, psq=%d, swq=%d, MORE=%d\n",
+		    __func__,
+		    tid->an->an_node.ni_macaddr,
+		    ":",
+		    tid->an->an_leak_count,
+		    tid->an->an_stack_psq,
+		    tid->an->an_swq_depth,
+		    !! (wh->i_fc[1] & IEEE80211_FC1_MORE_DATA));
+
+		/*
+		 * Re-sync the underlying buffer.
+		 */
+		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+		    BUS_DMASYNC_PREWRITE);
+
+		tid->an->an_leak_count --;
+	}
+}
+
+static int
+ath_tx_tid_can_tx_or_sched(struct ath_softc *sc, struct ath_tid *tid)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (tid->an->an_leak_count > 0) {
+		return (1);
+	}
+	if (tid->paused)
+		return (0);
+	return (1);
+}
+
+/*
+ * Mark the current node/TID as ready to TX.
+ *
+ * This is done to make it easy for the software scheduler to
+ * find which nodes have data to send.
+ *
+ * The TXQ lock must be held.
+ */
+void
+ath_tx_tid_sched(struct ath_softc *sc, struct ath_tid *tid)
+{
+	struct ath_txq *txq = sc->sc_ac2q[tid->ac];
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * If we are leaking out a frame to this destination
+	 * for PS-POLL, ensure that we allow scheduling to
+	 * occur.
+	 */
+	if (! ath_tx_tid_can_tx_or_sched(sc, tid))
+		return;		/* paused, can't schedule yet */
+
+	if (tid->sched)
+		return;		/* already scheduled */
+
+	tid->sched = 1;
+
+#if 0
+	/*
+	 * If this is a sleeping node we're leaking to, given
+	 * it a higher priority.  This is so bad for QoS it hurts.
+	 */
+	if (tid->an->an_leak_count) {
+		TAILQ_INSERT_HEAD(&txq->axq_tidq, tid, axq_qelem);
+	} else {
+		TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem);
+	}
+#endif
+
+	/*
+	 * We can't do the above - it'll confuse the TXQ software
+	 * scheduler which will keep checking the _head_ TID
+	 * in the list to see if it has traffic.  If we queue
+	 * a TID to the head of the list and it doesn't transmit,
+	 * we'll check it again.
+	 *
+	 * So, get the rest of this leaking frames support working
+	 * and reliable first and _then_ optimise it so they're
+	 * pushed out in front of any other pending software
+	 * queued nodes.
+	 */
+	TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem);
+}
+
+/*
+ * Mark the current node as no longer needing to be polled for
+ * TX packets.
+ *
+ * The TXQ lock must be held.
+ */
+static void
+ath_tx_tid_unsched(struct ath_softc *sc, struct ath_tid *tid)
+{
+	struct ath_txq *txq = sc->sc_ac2q[tid->ac];
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (tid->sched == 0)
+		return;
+
+	tid->sched = 0;
+	TAILQ_REMOVE(&txq->axq_tidq, tid, axq_qelem);
+}
+
+/*
+ * Assign a sequence number manually to the given frame.
+ *
+ * This should only be called for A-MPDU TX frames.
+ */
+static ieee80211_seq
+ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni,
+    struct ath_buf *bf, struct mbuf *m0)
+{
+	struct ieee80211_frame *wh;
+	int tid, pri;
+	ieee80211_seq seqno;
+	uint8_t subtype;
+
+	/* TID lookup */
+	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: pri=%d, tid=%d, qos has seq=%d\n",
+	    __func__, pri, tid, IEEE80211_QOS_HAS_SEQ(wh));
+
+	/* XXX Is it a control frame? Ignore */
+
+	/* Does the packet require a sequence number? */
+	if (! IEEE80211_QOS_HAS_SEQ(wh))
+		return -1;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * Is it a QOS NULL Data frame? Give it a sequence number from
+	 * the default TID (IEEE80211_NONQOS_TID.)
+	 *
+	 * The RX path of everything I've looked at doesn't include the NULL
+	 * data frame sequence number in the aggregation state updates, so
+	 * assigning it a sequence number there will cause a BAW hole on the
+	 * RX side.
+	 */
+	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 {
+		/* Manually assign sequence number */
+		seqno = ni->ni_txseqs[tid];
+		INCR(ni->ni_txseqs[tid], IEEE80211_SEQ_RANGE);
+	}
+	*(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
+	M_SEQNO_SET(m0, seqno);
+
+	/* Return so caller can do something with it if needed */
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s:  -> seqno=%d\n", __func__, seqno);
+	return seqno;
+}
+
+/*
+ * Attempt to direct dispatch an aggregate frame to hardware.
+ * If the frame is out of BAW, queue.
+ * Otherwise, schedule it as a single frame.
+ */
+static void
+ath_tx_xmit_aggr(struct ath_softc *sc, struct ath_node *an,
+    struct ath_txq *txq, struct ath_buf *bf)
+{
+	struct ath_tid *tid = &an->an_tid[bf->bf_state.bfs_tid];
+	struct ieee80211_tx_ampdu *tap;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+
+	/* paused? queue */
+	if (! ath_tx_tid_can_tx_or_sched(sc, tid)) {
+		ATH_TID_INSERT_HEAD(tid, bf, bf_list);
+		/* XXX don't sched - we're paused! */
+		return;
+	}
+
+	/* outside baw? queue */
+	if (bf->bf_state.bfs_dobaw &&
+	    (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
+	    SEQNO(bf->bf_state.bfs_seqno)))) {
+		ATH_TID_INSERT_HEAD(tid, bf, bf_list);
+		ath_tx_tid_sched(sc, tid);
+		return;
+	}
+
+	/*
+	 * This is a temporary check and should be removed once
+	 * all the relevant code paths have been fixed.
+	 *
+	 * During aggregate retries, it's possible that the head
+	 * frame will fail (which has the bfs_aggr and bfs_nframes
+	 * fields set for said aggregate) and will be retried as
+	 * a single frame.  In this instance, the values should
+	 * be reset or the completion code will get upset with you.
+	 */
+	if (bf->bf_state.bfs_aggr != 0 || bf->bf_state.bfs_nframes > 1) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+		    "%s: bfs_aggr=%d, bfs_nframes=%d\n", __func__,
+		    bf->bf_state.bfs_aggr, bf->bf_state.bfs_nframes);
+		bf->bf_state.bfs_aggr = 0;
+		bf->bf_state.bfs_nframes = 1;
+	}
+
+	/* Update CLRDMASK just before this frame is queued */
+	ath_tx_update_clrdmask(sc, tid, bf);
+
+	/* Direct dispatch to hardware */
+	ath_tx_do_ratelookup(sc, bf);
+	ath_tx_calc_duration(sc, bf);
+	ath_tx_calc_protection(sc, bf);
+	ath_tx_set_rtscts(sc, bf);
+	ath_tx_rate_fill_rcflags(sc, bf);
+	ath_tx_setds(sc, bf);
+
+	/* Statistics */
+	sc->sc_aggr_stats.aggr_low_hwq_single_pkt++;
+
+	/* Track per-TID hardware queue depth correctly */
+	tid->hwq_depth++;
+
+	/* Add to BAW */
+	if (bf->bf_state.bfs_dobaw) {
+		ath_tx_addto_baw(sc, an, tid, bf);
+		bf->bf_state.bfs_addedbaw = 1;
+	}
+
+	/* Set completion handler, multi-frame aggregate or not */
+	bf->bf_comp = ath_tx_aggr_comp;
+
+	/*
+	 * Update the current leak count if
+	 * we're leaking frames; and set the
+	 * MORE flag as appropriate.
+	 */
+	ath_tx_leak_count_update(sc, tid, bf);
+
+	/* Hand off to hardware */
+	ath_tx_handoff(sc, txq, bf);
+}
+
+/*
+ * Attempt to send the packet.
+ * If the queue isn't busy, direct-dispatch.
+ * If the queue is busy enough, queue the given packet on the
+ *  relevant software queue.
+ */
+void
+ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni,
+    struct ath_txq *txq, int queue_to_head, struct ath_buf *bf)
+{
+	struct ath_node *an = ATH_NODE(ni);
+	struct ieee80211_frame *wh;
+	struct ath_tid *atid;
+	int pri, tid;
+	struct mbuf *m0 = bf->bf_m;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/* 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\n",
+	    __func__, bf, pri, tid, IEEE80211_QOS_HAS_SEQ(wh));
+
+	/* Set local packet state, used to queue packets to hardware */
+	/* XXX potentially duplicate info, re-check */
+	bf->bf_state.bfs_tid = tid;
+	bf->bf_state.bfs_tx_queue = txq->axq_qnum;
+	bf->bf_state.bfs_pri = pri;
+
+	/*
+	 * If the hardware queue isn't busy, queue it directly.
+	 * If the hardware queue is busy, queue it.
+	 * If the TID is paused or the traffic it outside BAW, software
+	 * queue it.
+	 *
+	 * If the node is in power-save and we're leaking a frame,
+	 * leak a single frame.
+	 */
+	if (! ath_tx_tid_can_tx_or_sched(sc, atid)) {
+		/* TID is paused, queue */
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: paused\n", __func__);
+		/*
+		 * If the caller requested that it be sent at a high
+		 * priority, queue it at the head of the list.
+		 */
+		if (queue_to_head)
+			ATH_TID_INSERT_HEAD(atid, bf, bf_list);
+		else
+			ATH_TID_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: pending\n", __func__);
+		ATH_TID_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_TID_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?
+		 *
+		 * When running A-MPDU, always just check the hardware
+		 * queue depth against the aggregate frame limit.
+		 * We don't want to burst a large number of single frames
+		 * out to the hardware; we want to aggressively hold back.
+		 *
+		 * Otherwise, schedule the TID.
+		 */
+		/* XXX TXQ locking */
+		if (txq->axq_depth + txq->fifo.axq_depth < sc->sc_hwq_limit_aggr) {
+			bf = ATH_TID_FIRST(atid);
+			ATH_TID_REMOVE(atid, bf, bf_list);
+
+			/*
+			 * Ensure it's definitely treated as a non-AMPDU
+			 * frame - this information may have been left
+			 * over from a previous attempt.
+			 */
+			bf->bf_state.bfs_aggr = 0;
+			bf->bf_state.bfs_nframes = 1;
+
+			/* Queue to the hardware */
+			ath_tx_xmit_aggr(sc, an, txq, bf);
+			DPRINTF(sc, ATH_DEBUG_SW_TX,
+			    "%s: xmit_aggr\n",
+			    __func__);
+		} else {
+			DPRINTF(sc, ATH_DEBUG_SW_TX,
+			    "%s: ampdu; swq'ing\n",
+			    __func__);
+
+			ath_tx_tid_sched(sc, atid);
+		}
+	/*
+	 * If we're not doing A-MPDU, be prepared to direct dispatch
+	 * up to both limits if possible.  This particular corner
+	 * case may end up with packet starvation between aggregate
+	 * traffic and non-aggregate traffic: we wnat to ensure
+	 * that non-aggregate stations get a few frames queued to the
+	 * hardware before the aggregate station(s) get their chance.
+	 *
+	 * So if you only ever see a couple of frames direct dispatched
+	 * to the hardware from a non-AMPDU client, check both here
+	 * and in the software queue dispatcher to ensure that those
+	 * non-AMPDU stations get a fair chance to transmit.
+	 */
+	/* XXX TXQ locking */
+	} else if ((txq->axq_depth + txq->fifo.axq_depth < sc->sc_hwq_limit_nonaggr) &&
+		    (txq->axq_aggr_depth < sc->sc_hwq_limit_aggr)) {
+		/* AMPDU not running, attempt direct dispatch */
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: xmit_normal\n", __func__);
+		/* See if clrdmask needs to be set */
+		ath_tx_update_clrdmask(sc, atid, bf);
+
+		/*
+		 * Update the current leak count if
+		 * we're leaking frames; and set the
+		 * MORE flag as appropriate.
+		 */
+		ath_tx_leak_count_update(sc, atid, bf);
+
+		/*
+		 * Dispatch the frame.
+		 */
+		ath_tx_xmit_normal(sc, txq, bf);
+	} else {
+		/* Busy; queue */
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: swq'ing\n", __func__);
+		ATH_TID_INSERT_TAIL(atid, bf, bf_list);
+		ath_tx_tid_sched(sc, atid);
+	}
+}
+
+/*
+ * Only set the clrdmask bit if none of the nodes are currently
+ * filtered.
+ *
+ * XXX TODO: go through all the callers and check to see
+ * which are being called in the context of looping over all
+ * TIDs (eg, if all tids are being paused, resumed, etc.)
+ * That'll avoid O(n^2) complexity here.
+ */
+static void
+ath_tx_set_clrdmask(struct ath_softc *sc, struct ath_node *an)
+{
+	int i;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	for (i = 0; i < IEEE80211_TID_SIZE; i++) {
+		if (an->an_tid[i].isfiltered == 1)
+			return;
+	}
+	an->clrdmask = 1;
+}
+
+/*
+ * Configure the per-TID node state.
+ *
+ * This likely belongs in if_ath_node.c but I can't think of anywhere
+ * else to put it just yet.
+ *
+ * This sets up the SLISTs and the mutex as appropriate.
+ */
+void
+ath_tx_tid_init(struct ath_softc *sc, struct ath_node *an)
+{
+	int i, j;
+	struct ath_tid *atid;
+
+	for (i = 0; i < IEEE80211_TID_SIZE; i++) {
+		atid = &an->an_tid[i];
+
+		/* XXX now with this bzer(), is the field 0'ing needed? */
+		bzero(atid, sizeof(*atid));
+
+		TAILQ_INIT(&atid->tid_q);
+		TAILQ_INIT(&atid->filtq.tid_q);
+		atid->tid = i;
+		atid->an = an;
+		for (j = 0; j < ATH_TID_MAX_BUFS; j++)
+			atid->tx_buf[j] = NULL;
+		atid->baw_head = atid->baw_tail = 0;
+		atid->paused = 0;
+		atid->sched = 0;
+		atid->hwq_depth = 0;
+		atid->cleanup_inprogress = 0;
+		if (i == IEEE80211_NONQOS_TID)
+			atid->ac = ATH_NONQOS_TID_AC;
+		else
+			atid->ac = TID_TO_WME_AC(i);
+	}
+	an->clrdmask = 1;	/* Always start by setting this bit */
+}
+
+/*
+ * Pause the current TID. This stops packets from being transmitted
+ * on it.
+ *
+ * Since this is also called from upper layers as well as the driver,
+ * it will get the TID lock.
+ */
+static void
+ath_tx_tid_pause(struct ath_softc *sc, struct ath_tid *tid)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+	tid->paused++;
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: paused = %d\n",
+	    __func__, tid->paused);
+}
+
+/*
+ * Unpause the current TID, and schedule it if needed.
+ */
+static void
+ath_tx_tid_resume(struct ath_softc *sc, struct ath_tid *tid)
+{
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * There's some odd places where ath_tx_tid_resume() is called
+	 * when it shouldn't be; this works around that particular issue
+	 * until it's actually resolved.
+	 */
+	if (tid->paused == 0) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		    "%s: %6D: paused=0?\n", __func__,
+		    tid->an->an_node.ni_macaddr, ":");
+	} else {
+		tid->paused--;
+	}
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: unpaused = %d\n",
+	    __func__, tid->paused);
+
+	if (tid->paused)
+		return;
+
+	/*
+	 * Override the clrdmask configuration for the next frame
+	 * from this TID, just to get the ball rolling.
+	 */
+	ath_tx_set_clrdmask(sc, tid->an);
+
+	if (tid->axq_depth == 0)
+		return;
+
+	/* XXX isfiltered shouldn't ever be 0 at this point */
+	if (tid->isfiltered == 1) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: filtered?!\n",
+		    __func__);
+		return;
+	}
+
+	ath_tx_tid_sched(sc, tid);
+
+	/*
+	 * Queue the software TX scheduler.
+	 */
+	ath_tx_swq_kick(sc);
+}
+
+/*
+ * Add the given ath_buf to the TID filtered frame list.
+ * This requires the TID be filtered.
+ */
+static void
+ath_tx_tid_filt_addbuf(struct ath_softc *sc, struct ath_tid *tid,
+    struct ath_buf *bf)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (!tid->isfiltered)
+		DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: not filtered?!\n",
+		    __func__);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: bf=%p\n", __func__, bf);
+
+	/* Set the retry bit and bump the retry counter */
+	ath_tx_set_retry(sc, bf);
+	sc->sc_stats.ast_tx_swfiltered++;
+
+	ATH_TID_FILT_INSERT_TAIL(tid, bf, bf_list);
+}
+
+/*
+ * Handle a completed filtered frame from the given TID.
+ * This just enables/pauses the filtered frame state if required
+ * and appends the filtered frame to the filtered queue.
+ */
+static void
+ath_tx_tid_filt_comp_buf(struct ath_softc *sc, struct ath_tid *tid,
+    struct ath_buf *bf)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (! tid->isfiltered) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: filter transition\n",
+		    __func__);
+		tid->isfiltered = 1;
+		ath_tx_tid_pause(sc, tid);
+	}
+
+	/* Add the frame to the filter queue */
+	ath_tx_tid_filt_addbuf(sc, tid, bf);
+}
+
+/*
+ * Complete the filtered frame TX completion.
+ *
+ * If there are no more frames in the hardware queue, unpause/unfilter
+ * the TID if applicable.  Otherwise we will wait for a node PS transition
+ * to unfilter.
+ */
+static void
+ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid)
+{
+	struct ath_buf *bf;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (tid->hwq_depth != 0)
+		return;
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: hwq=0, transition back\n",
+	    __func__);
+	tid->isfiltered = 0;
+	/* XXX ath_tx_tid_resume() also calls ath_tx_set_clrdmask()! */
+	ath_tx_set_clrdmask(sc, tid->an);
+
+	/* XXX this is really quite inefficient */
+	while ((bf = ATH_TID_FILT_LAST(tid, ath_bufhead_s)) != NULL) {
+		ATH_TID_FILT_REMOVE(tid, bf, bf_list);
+		ATH_TID_INSERT_HEAD(tid, bf, bf_list);
+	}
+
+	ath_tx_tid_resume(sc, tid);
+}
+
+/*
+ * Called when a single (aggregate or otherwise) frame is completed.
+ *
+ * Returns 1 if the buffer could be added to the filtered list
+ * (cloned or otherwise), 0 if the buffer couldn't be added to the
+ * filtered list (failed clone; expired retry) and the caller should
+ * free it and handle it like a failure (eg by sending a BAR.)
+ */
+static int
+ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid,
+    struct ath_buf *bf)
+{
+	struct ath_buf *nbf;
+	int retval;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * Don't allow a filtered frame to live forever.
+	 */
+	if (bf->bf_state.bfs_retries > SWMAX_RETRIES) {
+		sc->sc_stats.ast_tx_swretrymax++;
+		DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
+		    "%s: bf=%p, seqno=%d, exceeded retries\n",
+		    __func__,
+		    bf,
+		    bf->bf_state.bfs_seqno);
+		return (0);
+	}
+
+	/*
+	 * A busy buffer can't be added to the retry list.
+	 * It needs to be cloned.
+	 */
+	if (bf->bf_flags & ATH_BUF_BUSY) {
+		nbf = ath_tx_retry_clone(sc, tid->an, tid, bf);
+		DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
+		    "%s: busy buffer clone: %p -> %p\n",
+		    __func__, bf, nbf);
+	} else {
+		nbf = bf;
+	}
+
+	if (nbf == NULL) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
+		    "%s: busy buffer couldn't be cloned (%p)!\n",
+		    __func__, bf);
+		retval = 1;
+	} else {
+		ath_tx_tid_filt_comp_buf(sc, tid, nbf);
+		retval = 0;
+	}
+	ath_tx_tid_filt_comp_complete(sc, tid);
+
+	return (retval);
+}
+
+static void
+ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid,
+    struct ath_buf *bf_first, ath_bufhead *bf_q)
+{
+	struct ath_buf *bf, *bf_next, *nbf;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	bf = bf_first;
+	while (bf) {
+		bf_next = bf->bf_next;
+		bf->bf_next = NULL;	/* Remove it from the aggr list */
+
+		/*
+		 * Don't allow a filtered frame to live forever.
+		 */
+		if (bf->bf_state.bfs_retries > SWMAX_RETRIES) {
+			sc->sc_stats.ast_tx_swretrymax++;
+			DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
+			    "%s: bf=%p, seqno=%d, exceeded retries\n",
+			    __func__,
+			    bf,
+			    bf->bf_state.bfs_seqno);
+			TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
+			goto next;
+		}
+
+		if (bf->bf_flags & ATH_BUF_BUSY) {
+			nbf = ath_tx_retry_clone(sc, tid->an, tid, bf);
+			DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
+			    "%s: busy buffer cloned: %p -> %p",
+			    __func__, bf, nbf);
+		} else {
+			nbf = bf;
+		}
+
+		/*
+		 * If the buffer couldn't be cloned, add it to bf_q;
+		 * the caller will free the buffer(s) as required.
+		 */
+		if (nbf == NULL) {
+			DPRINTF(sc, ATH_DEBUG_SW_TX_FILT,
+			    "%s: buffer couldn't be cloned! (%p)\n",
+			    __func__, bf);
+			TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
+		} else {
+			ath_tx_tid_filt_comp_buf(sc, tid, nbf);
+		}
+next:
+		bf = bf_next;
+	}
+
+	ath_tx_tid_filt_comp_complete(sc, tid);
+}
+
+/*
+ * Suspend the queue because we need to TX a BAR.
+ */
+static void
+ath_tx_tid_bar_suspend(struct ath_softc *sc, struct ath_tid *tid)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: tid=%d, 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) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+		    "%s: bar_tx is 1?!\n", __func__);
+	}
+
+	/* If we've already been called, just be patient. */
+	if (tid->bar_wait)
+		return;
+
+	/* Wait! */
+	tid->bar_wait = 1;
+
+	/* Only one pause, no matter how many frames fail */
+	ath_tx_tid_pause(sc, tid);
+}
+
+/*
+ * We've finished with BAR handling - either we succeeded or
+ * failed. Either way, unsuspend TX.
+ */
+static void
+ath_tx_tid_bar_unsuspend(struct ath_softc *sc, struct ath_tid *tid)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: %6D: TID=%d, called\n",
+	    __func__,
+	    tid->an->an_node.ni_macaddr,
+	    ":",
+	    tid->tid);
+
+	if (tid->bar_tx == 0 || tid->bar_wait == 0) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+		    "%s: %6D: TID=%d, bar_tx=%d, bar_wait=%d: ?\n",
+		    __func__, tid->an->an_node.ni_macaddr, ":",
+		    tid->tid, tid->bar_tx, tid->bar_wait);
+	}
+
+	tid->bar_tx = tid->bar_wait = 0;
+	ath_tx_tid_resume(sc, tid);
+}
+
+/*
+ * Return whether we're ready to TX a BAR frame.
+ *
+ * Requires the TID lock be held.
+ */
+static int
+ath_tx_tid_bar_tx_ready(struct ath_softc *sc, struct ath_tid *tid)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	if (tid->bar_wait == 0 || tid->hwq_depth > 0)
+		return (0);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: %6D: TID=%d, bar ready\n",
+	    __func__,
+	    tid->an->an_node.ni_macaddr,
+	    ":",
+	    tid->tid);
+
+	return (1);
+}
+
+/*
+ * Check whether the current TID is ready to have a BAR
+ * TXed and if so, do the TX.
+ *
+ * Since the TID/TXQ lock can't be held during a call to
+ * ieee80211_send_bar(), we have to do the dirty thing of unlocking it,
+ * sending the BAR and locking it again.
+ *
+ * Eventually, the code to send the BAR should be broken out
+ * from this routine so the lock doesn't have to be reacquired
+ * just to be immediately dropped by the caller.
+ */
+static void
+ath_tx_tid_bar_tx(struct ath_softc *sc, struct ath_tid *tid)
+{
+	struct ieee80211_tx_ampdu *tap;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: %6D: TID=%d, called\n",
+	    __func__,
+	    tid->an->an_node.ni_macaddr,
+	    ":",
+	    tid->tid);
+
+	tap = ath_tx_get_tx_tid(tid->an, tid->tid);
+
+	/*
+	 * This is an error condition!
+	 */
+	if (tid->bar_wait == 0 || tid->bar_tx == 1) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+		    "%s: %6D: TID=%d, bar_tx=%d, bar_wait=%d: ?\n",
+		    __func__, tid->an->an_node.ni_macaddr, ":",
+		    tid->tid, tid->bar_tx, tid->bar_wait);
+		return;
+	}
+
+	/* Don't do anything if we still have pending frames */
+	if (tid->hwq_depth > 0) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+		    "%s: %6D: TID=%d, hwq_depth=%d, waiting\n",
+		    __func__,
+		    tid->an->an_node.ni_macaddr,
+		    ":",
+		    tid->tid,
+		    tid->hwq_depth);
+		return;
+	}
+
+	/* We're now about to TX */
+	tid->bar_tx = 1;
+
+	/*
+	 * Override the clrdmask configuration for the next frame,
+	 * just to get the ball rolling.
+	 */
+	ath_tx_set_clrdmask(sc, tid->an);
+
+	/*
+	 * Calculate new BAW left edge, now that all frames have either
+	 * succeeded or failed.
+	 *
+	 * XXX verify this is _actually_ the valid value to begin at!
+	 */
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: %6D: TID=%d, new BAW left edge=%d\n",
+	    __func__,
+	    tid->an->an_node.ni_macaddr,
+	    ":",
+	    tid->tid,
+	    tap->txa_start);
+
+	/* Try sending the BAR frame */
+	/* We can't hold the lock here! */
+
+	ATH_TX_UNLOCK(sc);
+	if (ieee80211_send_bar(&tid->an->an_node, tap, tap->txa_start) == 0) {
+		/* Success? Now we wait for notification that it's done */
+		ATH_TX_LOCK(sc);
+		return;
+	}
+
+	/* Failure? For now, warn loudly and continue */
+	ATH_TX_LOCK(sc);
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: %6D: TID=%d, failed to TX BAR, continue!\n",
+	    __func__, tid->an->an_node.ni_macaddr, ":",
+	    tid->tid);
+	ath_tx_tid_bar_unsuspend(sc, tid);
+}
+
+static void
+ath_tx_tid_drain_pkt(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid, ath_bufhead *bf_cq, struct ath_buf *bf)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * If the current TID is running AMPDU, update
+	 * the BAW.
+	 */
+	if (ath_tx_ampdu_running(sc, an, tid->tid) &&
+	    bf->bf_state.bfs_dobaw) {
+		/*
+		 * Only remove the frame from the BAW if it's
+		 * been transmitted at least once; this means
+		 * the frame was in the BAW to begin with.
+		 */
+		if (bf->bf_state.bfs_retries > 0) {
+			ath_tx_update_baw(sc, an, tid, bf);
+			bf->bf_state.bfs_dobaw = 0;
+		}
+#if 0
+		/*
+		 * This has become a non-fatal error now
+		 */
+		if (! bf->bf_state.bfs_addedbaw)
+			DPRINTF(sc, ATH_DEBUG_SW_TX_BAW
+			    "%s: wasn't added: seqno %d\n",
+			    __func__, SEQNO(bf->bf_state.bfs_seqno));
+#endif
+	}
+
+	/* Strip it out of an aggregate list if it was in one */
+	bf->bf_next = NULL;
+
+	/* Insert on the free queue to be freed by the caller */
+	TAILQ_INSERT_TAIL(bf_cq, bf, bf_list);
+}
+
+static void
+ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an,
+    const char *pfx, struct ath_tid *tid, struct ath_buf *bf)
+{
+	struct ieee80211_node *ni = &an->an_node;
+	struct ath_txq *txq;
+	struct ieee80211_tx_ampdu *tap;
+
+	txq = sc->sc_ac2q[tid->ac];
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX,
+	    "%s: %s: %6D: bf=%p: addbaw=%d, dobaw=%d, "
+	    "seqno=%d, retry=%d\n",
+	    __func__,
+	    pfx,
+	    ni->ni_macaddr,
+	    ":",
+	    bf,
+	    bf->bf_state.bfs_addedbaw,
+	    bf->bf_state.bfs_dobaw,
+	    SEQNO(bf->bf_state.bfs_seqno),
+	    bf->bf_state.bfs_retries);
+	DPRINTF(sc, ATH_DEBUG_SW_TX,
+	    "%s: %s: %6D: bf=%p: txq[%d] axq_depth=%d, axq_aggr_depth=%d\n",
+	    __func__,
+	    pfx,
+	    ni->ni_macaddr,
+	    ":",
+	    bf,
+	    txq->axq_qnum,
+	    txq->axq_depth,
+	    txq->axq_aggr_depth);
+	DPRINTF(sc, ATH_DEBUG_SW_TX,
+	    "%s: %s: %6D: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, "
+	      "isfiltered=%d\n",
+	    __func__,
+	    pfx,
+	    ni->ni_macaddr,
+	    ":",
+	    bf,
+	    tid->axq_depth,
+	    tid->hwq_depth,
+	    tid->bar_wait,
+	    tid->isfiltered);
+	DPRINTF(sc, ATH_DEBUG_SW_TX,
+	    "%s: %s: %6D: tid %d: "
+	    "sched=%d, paused=%d, "
+	    "incomp=%d, baw_head=%d, "
+	    "baw_tail=%d txa_start=%d, ni_txseqs=%d\n",
+	     __func__,
+	     pfx,
+	     ni->ni_macaddr,
+	     ":",
+	     tid->tid,
+	     tid->sched, tid->paused,
+	     tid->incomp, tid->baw_head,
+	     tid->baw_tail, tap == NULL ? -1 : tap->txa_start,
+	     ni->ni_txseqs[tid->tid]);
+
+	/* XXX Dump the frame, see what it is? */
+	if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
+		ieee80211_dump_pkt(ni->ni_ic,
+		    mtod(bf->bf_m, const uint8_t *),
+		    bf->bf_m->m_len, 0, -1);
+}
+
+/*
+ * Free any packets currently pending in the software TX queue.
+ *
+ * This will be called when a node is being deleted.
+ *
+ * It can also be called on an active node during an interface
+ * reset or state transition.
+ *
+ * (From Linux/reference):
+ *
+ * TODO: For frame(s) that are in the retry state, we will reuse the
+ * sequence number(s) without setting the retry bit. The
+ * alternative is to give up on these and BAR the receiver's window
+ * forward.
+ */
+static void
+ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid, ath_bufhead *bf_cq)
+{
+	struct ath_buf *bf;
+	struct ieee80211_tx_ampdu *tap;
+	struct ieee80211_node *ni = &an->an_node;
+	int t;
+
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/* Walk the queue, free frames */
+	t = 0;
+	for (;;) {
+		bf = ATH_TID_FIRST(tid);
+		if (bf == NULL) {
+			break;
+		}
+
+		if (t == 0) {
+			ath_tx_tid_drain_print(sc, an, "norm", tid, bf);
+			t = 1;
+		}
+
+		ATH_TID_REMOVE(tid, bf, bf_list);
+		ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf);
+	}
+
+	/* And now, drain the filtered frame queue */
+	t = 0;
+	for (;;) {
+		bf = ATH_TID_FILT_FIRST(tid);
+		if (bf == NULL)
+			break;
+
+		if (t == 0) {
+			ath_tx_tid_drain_print(sc, an, "filt", tid, bf);
+			t = 1;
+		}
+
+		ATH_TID_FILT_REMOVE(tid, bf, bf_list);
+		ath_tx_tid_drain_pkt(sc, an, tid, bf_cq, bf);
+	}
+
+	/*
+	 * Override the clrdmask configuration for the next frame
+	 * in case there is some future transmission, just to get
+	 * the ball rolling.
+	 *
+	 * This won't hurt things if the TID is about to be freed.
+	 */
+	ath_tx_set_clrdmask(sc, tid->an);
+
+	/*
+	 * Now that it's completed, grab the TID lock and update
+	 * the sequence number and BAW window.
+	 * Because sequence numbers have been assigned to frames
+	 * that haven't been sent yet, it's entirely possible
+	 * we'll be called with some pending frames that have not
+	 * been transmitted.
+	 *
+	 * The cleaner solution is to do the sequence number allocation
+	 * when the packet is first transmitted - and thus the "retries"
+	 * check above would be enough to update the BAW/seqno.
+	 */
+
+	/* But don't do it for non-QoS TIDs */
+	if (tap) {
+#if 1
+		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		    "%s: %6D: node %p: TID %d: sliding BAW left edge to %d\n",
+		    __func__,
+		    ni->ni_macaddr,
+		    ":",
+		    an,
+		    tid->tid,
+		    tap->txa_start);
+#endif
+		ni->ni_txseqs[tid->tid] = tap->txa_start;
+		tid->baw_tail = tid->baw_head;
+	}
+}
+
+/*
+ * Reset the TID state.  This must be only called once the node has
+ * had its frames flushed from this TID, to ensure that no other
+ * pause / unpause logic can kick in.
+ */
+static void
+ath_tx_tid_reset(struct ath_softc *sc, struct ath_tid *tid)
+{
+
+#if 0
+	tid->bar_wait = tid->bar_tx = tid->isfiltered = 0;
+	tid->paused = tid->sched = tid->addba_tx_pending = 0;
+	tid->incomp = tid->cleanup_inprogress = 0;
+#endif
+
+	/*
+	 * If we have a bar_wait set, we need to unpause the TID
+	 * here.  Otherwise once cleanup has finished, the TID won't
+	 * have the right paused counter.
+	 *
+	 * XXX I'm not going through resume here - I don't want the
+	 * node to be rescheuled just yet.  This however should be
+	 * methodized!
+	 */
+	if (tid->bar_wait) {
+		if (tid->paused > 0) {
+			tid->paused --;
+		}
+	}
+
+	/*
+	 * XXX same with a currently filtered TID.
+	 *
+	 * Since this is being called during a flush, we assume that
+	 * the filtered frame list is actually empty.
+	 *
+	 * XXX TODO: add in a check to ensure that the filtered queue
+	 * depth is actually 0!
+	 */
+	if (tid->isfiltered) {
+		if (tid->paused > 0) {
+			tid->paused --;
+		}
+	}
+
+	/*
+	 * Clear BAR, filtered frames, scheduled and ADDBA pending.
+	 * The TID may be going through cleanup from the last association
+	 * where things in the BAW are still in the hardware queue.
+	 */
+	tid->bar_wait = 0;
+	tid->bar_tx = 0;
+	tid->isfiltered = 0;
+	tid->sched = 0;
+	tid->addba_tx_pending = 0;
+
+	/*
+	 * XXX TODO: it may just be enough to walk the HWQs and mark
+	 * frames for that node as non-aggregate; or mark the ath_node
+	 * with something that indicates that aggregation is no longer
+	 * occuring.  Then we can just toss the BAW complaints and
+	 * do a complete hard reset of state here - no pause, no
+	 * complete counter, etc.
+	 */
+
+}
+
+/*
+ * Flush all software queued packets for the given node.
+ *
+ * This occurs when a completion handler frees the last buffer
+ * for a node, and the node is thus freed. This causes the node
+ * to be cleaned up, which ends up calling ath_tx_node_flush.
+ */
+void
+ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an)
+{
+	int tid;
+	ath_bufhead bf_cq;
+	struct ath_buf *bf;
+
+	TAILQ_INIT(&bf_cq);
+
+	ATH_KTR(sc, ATH_KTR_NODE, 1, "ath_tx_node_flush: flush node; ni=%p",
+	    &an->an_node);
+
+	ATH_TX_LOCK(sc);
+	DPRINTF(sc, ATH_DEBUG_NODE,
+	    "%s: %6D: flush; is_powersave=%d, stack_psq=%d, tim=%d, "
+	    "swq_depth=%d, clrdmask=%d, leak_count=%d\n",
+	    __func__,
+	    an->an_node.ni_macaddr,
+	    ":",
+	    an->an_is_powersave,
+	    an->an_stack_psq,
+	    an->an_tim_set,
+	    an->an_swq_depth,
+	    an->clrdmask,
+	    an->an_leak_count);
+
+	for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) {
+		struct ath_tid *atid = &an->an_tid[tid];
+
+		/* Free packets */
+		ath_tx_tid_drain(sc, an, atid, &bf_cq);
+
+		/* Remove this tid from the list of active tids */
+		ath_tx_tid_unsched(sc, atid);
+
+		/* Reset the per-TID pause, BAR, etc state */
+		ath_tx_tid_reset(sc, atid);
+	}
+
+	/*
+	 * Clear global leak count
+	 */
+	an->an_leak_count = 0;
+	ATH_TX_UNLOCK(sc);
+
+	/* Handle completed frames */
+	while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
+		TAILQ_REMOVE(&bf_cq, bf, bf_list);
+		ath_tx_default_comp(sc, bf, 0);
+	}
+}
+
+/*
+ * Drain all the software TXQs currently with traffic queued.
+ */
+void
+ath_tx_txq_drain(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_tid *tid;
+	ath_bufhead bf_cq;
+	struct ath_buf *bf;
+
+	TAILQ_INIT(&bf_cq);
+	ATH_TX_LOCK(sc);
+
+	/*
+	 * Iterate over all active tids for the given txq,
+	 * flushing and unsched'ing them
+	 */
+	while (! TAILQ_EMPTY(&txq->axq_tidq)) {
+		tid = TAILQ_FIRST(&txq->axq_tidq);
+		ath_tx_tid_drain(sc, tid->an, tid, &bf_cq);
+		ath_tx_tid_unsched(sc, tid);
+	}
+
+	ATH_TX_UNLOCK(sc);
+
+	while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
+		TAILQ_REMOVE(&bf_cq, bf, bf_list);
+		ath_tx_default_comp(sc, bf, 0);
+	}
+}
+
+/*
+ * Handle completion of non-aggregate session frames.
+ *
+ * This (currently) doesn't implement software retransmission of
+ * non-aggregate frames!
+ *
+ * Software retransmission of non-aggregate frames needs to obey
+ * the strict sequence number ordering, and drop any frames that
+ * will fail this.
+ *
+ * For now, filtered frames and frame transmission will cause
+ * all kinds of issues.  So we don't support them.
+ *
+ * So anyone queuing frames via ath_tx_normal_xmit() or
+ * ath_tx_hw_queue_norm() must override and set CLRDMASK.
+ */
+void
+ath_tx_normal_comp(struct ath_softc *sc, struct ath_buf *bf, int fail)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+	struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
+
+	/* The TID state is protected behind the TXQ lock */
+	ATH_TX_LOCK(sc);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bf=%p: fail=%d, hwq_depth now %d\n",
+	    __func__, bf, fail, atid->hwq_depth - 1);
+
+	atid->hwq_depth--;
+
+#if 0
+	/*
+	 * If the frame was filtered, stick it on the filter frame
+	 * queue and complain about it.  It shouldn't happen!
+	 */
+	if ((ts->ts_status & HAL_TXERR_FILT) ||
+	    (ts->ts_status != 0 && atid->isfiltered)) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX,
+		    "%s: isfiltered=%d, ts_status=%d: huh?\n",
+		    __func__,
+		    atid->isfiltered,
+		    ts->ts_status);
+		ath_tx_tid_filt_comp_buf(sc, atid, bf);
+	}
+#endif
+	if (atid->isfiltered)
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: filtered?!\n", __func__);
+	if (atid->hwq_depth < 0)
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n",
+		    __func__, atid->hwq_depth);
+
+	/*
+	 * If the queue is filtered, potentially mark it as complete
+	 * and reschedule it as needed.
+	 *
+	 * This is required as there may be a subsequent TX descriptor
+	 * for this end-node that has CLRDMASK set, so it's quite possible
+	 * that a filtered frame will be followed by a non-filtered
+	 * (complete or otherwise) frame.
+	 *
+	 * XXX should we do this before we complete the frame?
+	 */
+	if (atid->isfiltered)
+		ath_tx_tid_filt_comp_complete(sc, atid);
+	ATH_TX_UNLOCK(sc);
+
+	/*
+	 * punt to rate control if we're not being cleaned up
+	 * during a hw queue drain and the frame wanted an ACK.
+	 */
+	if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0))
+		ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc,
+		    ts, bf->bf_state.bfs_pktlen,
+		    1, (ts->ts_status == 0) ? 0 : 1);
+
+	ath_tx_default_comp(sc, bf, fail);
+}
+
+/*
+ * Handle cleanup of aggregate session packets that aren't
+ * an A-MPDU.
+ *
+ * There's no need to update the BAW here - the session is being
+ * torn down.
+ */
+static void
+ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: incomp=%d\n",
+	    __func__, tid, atid->incomp);
+
+	ATH_TX_LOCK(sc);
+	atid->incomp--;
+	if (atid->incomp == 0) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		    "%s: TID %d: cleaned up! resume!\n",
+		    __func__, tid);
+		atid->cleanup_inprogress = 0;
+		ath_tx_tid_resume(sc, atid);
+	}
+	ATH_TX_UNLOCK(sc);
+
+	ath_tx_default_comp(sc, bf, 0);
+}
+
+/*
+ * Performs transmit side cleanup when TID changes from aggregated to
+ * unaggregated.
+ *
+ * - Discard all retry frames from the s/w queue.
+ * - Fix the tx completion function for all buffers in s/w queue.
+ * - Count the number of unacked frames, and let transmit completion
+ *   handle it later.
+ *
+ * The caller is responsible for pausing the TID and unpausing the
+ * TID if no cleanup was required. Otherwise the cleanup path will
+ * unpause the TID once the last hardware queued frame is completed.
+ */
+static void
+ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid,
+    ath_bufhead *bf_cq)
+{
+	struct ath_tid *atid = &an->an_tid[tid];
+	struct ieee80211_tx_ampdu *tap;
+	struct ath_buf *bf, *bf_next;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+	    "%s: TID %d: called\n", __func__, tid);
+
+	/*
+	 * Move the filtered frames to the TX queue, before
+	 * we run off and discard/process things.
+	 */
+	/* XXX this is really quite inefficient */
+	while ((bf = ATH_TID_FILT_LAST(atid, ath_bufhead_s)) != NULL) {
+		ATH_TID_FILT_REMOVE(atid, bf, bf_list);
+		ATH_TID_INSERT_HEAD(atid, bf, bf_list);
+	}
+
+	/*
+	 * Update the frames in the software TX queue:
+	 *
+	 * + Discard retry frames in the queue
+	 * + Fix the completion function to be non-aggregate
+	 */
+	bf = ATH_TID_FIRST(atid);
+	while (bf) {
+		if (bf->bf_state.bfs_isretried) {
+			bf_next = TAILQ_NEXT(bf, bf_list);
+			ATH_TID_REMOVE(atid, bf, bf_list);
+			if (bf->bf_state.bfs_dobaw) {
+				ath_tx_update_baw(sc, an, atid, bf);
+				if (!bf->bf_state.bfs_addedbaw)
+					DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+					    "%s: wasn't added: seqno %d\n",
+					    __func__,
+					    SEQNO(bf->bf_state.bfs_seqno));
+			}
+			bf->bf_state.bfs_dobaw = 0;
+			/*
+			 * Call the default completion handler with "fail" just
+			 * so upper levels are suitably notified about this.
+			 */
+			TAILQ_INSERT_TAIL(bf_cq, bf, bf_list);
+			bf = bf_next;
+			continue;
+		}
+		/* Give these the default completion handler */
+		bf->bf_comp = ath_tx_normal_comp;
+		bf = TAILQ_NEXT(bf, bf_list);
+	}
+
+	/*
+	 * Calculate what hardware-queued frames exist based
+	 * on the current BAW size. Ie, what frames have been
+	 * added to the TX hardware queue for this TID but
+	 * not yet ACKed.
+	 */
+	tap = ath_tx_get_tx_tid(an, tid);
+	/* Need the lock - fiddling with BAW */
+	while (atid->baw_head != atid->baw_tail) {
+		if (atid->tx_buf[atid->baw_head]) {
+			atid->incomp++;
+			atid->cleanup_inprogress = 1;
+			atid->tx_buf[atid->baw_head] = NULL;
+		}
+		INCR(atid->baw_head, ATH_TID_MAX_BUFS);
+		INCR(tap->txa_start, IEEE80211_SEQ_RANGE);
+	}
+
+	if (atid->cleanup_inprogress)
+		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		    "%s: TID %d: cleanup needed: %d packets\n",
+		    __func__, tid, atid->incomp);
+
+	/* Owner now must free completed frames */
+}
+
+static struct ath_buf *
+ath_tx_retry_clone(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid, struct ath_buf *bf)
+{
+	struct ath_buf *nbf;
+	int error;
+
+	/*
+	 * Clone the buffer.  This will handle the dma unmap and
+	 * copy the node reference to the new buffer.  If this
+	 * works out, 'bf' will have no DMA mapping, no mbuf
+	 * pointer and no node reference.
+	 */
+	nbf = ath_buf_clone(sc, bf);
+
+#if 0
+	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: ATH_BUF_BUSY; cloning\n",
+	    __func__);
+#endif
+
+	if (nbf == NULL) {
+		/* Failed to clone */
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: failed to clone a busy buffer\n",
+		    __func__);
+		return NULL;
+	}
+
+	/* Setup the dma for the new buffer */
+	error = ath_tx_dmasetup(sc, nbf, nbf->bf_m);
+	if (error != 0) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: failed to setup dma for clone\n",
+		    __func__);
+		/*
+		 * Put this at the head of the list, not tail;
+		 * that way it doesn't interfere with the
+		 * busy buffer logic (which uses the tail of
+		 * the list.)
+		 */
+		ATH_TXBUF_LOCK(sc);
+		ath_returnbuf_head(sc, nbf);
+		ATH_TXBUF_UNLOCK(sc);
+		return NULL;
+	}
+
+	/* Update BAW if required, before we free the original buf */
+	if (bf->bf_state.bfs_dobaw)
+		ath_tx_switch_baw_buf(sc, an, tid, bf, nbf);
+
+	/* Free original buffer; return new buffer */
+	ath_freebuf(sc, bf);
+
+	return nbf;
+}
+
+/*
+ * Handle retrying an unaggregate frame in an aggregate
+ * session.
+ *
+ * If too many retries occur, pause the TID, wait for
+ * any further retransmits (as there's no reason why
+ * non-aggregate frames in an aggregate session are
+ * transmitted in-order; they just have to be in-BAW)
+ * and then queue a BAR.
+ */
+static void
+ath_tx_aggr_retry_unaggr(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+	struct ieee80211_tx_ampdu *tap;
+
+	ATH_TX_LOCK(sc);
+
+	tap = ath_tx_get_tx_tid(an, tid);
+
+	/*
+	 * If the buffer is marked as busy, we can't directly
+	 * reuse it. Instead, try to clone the buffer.
+	 * If the clone is successful, recycle the old buffer.
+	 * If the clone is unsuccessful, set bfs_retries to max
+	 * to force the next bit of code to free the buffer
+	 * for us.
+	 */
+	if ((bf->bf_state.bfs_retries < SWMAX_RETRIES) &&
+	    (bf->bf_flags & ATH_BUF_BUSY)) {
+		struct ath_buf *nbf;
+		nbf = ath_tx_retry_clone(sc, an, atid, bf);
+		if (nbf)
+			/* bf has been freed at this point */
+			bf = nbf;
+		else
+			bf->bf_state.bfs_retries = SWMAX_RETRIES + 1;
+	}
+
+	if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_RETRIES,
+		    "%s: exceeded retries; seqno %d\n",
+		    __func__, SEQNO(bf->bf_state.bfs_seqno));
+		sc->sc_stats.ast_tx_swretrymax++;
+
+		/* Update BAW anyway */
+		if (bf->bf_state.bfs_dobaw) {
+			ath_tx_update_baw(sc, an, atid, bf);
+			if (! bf->bf_state.bfs_addedbaw)
+				DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+				    "%s: wasn't added: seqno %d\n",
+				    __func__, SEQNO(bf->bf_state.bfs_seqno));
+		}
+		bf->bf_state.bfs_dobaw = 0;
+
+		/* Suspend the TX queue and get ready to send the BAR */
+		ath_tx_tid_bar_suspend(sc, atid);
+
+		/* Send the BAR if there are no other frames waiting */
+		if (ath_tx_tid_bar_tx_ready(sc, atid))
+			ath_tx_tid_bar_tx(sc, atid);
+
+		ATH_TX_UNLOCK(sc);
+
+		/* Free buffer, bf is free after this call */
+		ath_tx_default_comp(sc, bf, 0);
+		return;
+	}
+
+	/*
+	 * This increments the retry counter as well as
+	 * sets the retry flag in the ath_buf and packet
+	 * body.
+	 */
+	ath_tx_set_retry(sc, bf);
+	sc->sc_stats.ast_tx_swretries++;
+
+	/*
+	 * Insert this at the head of the queue, so it's
+	 * retried before any current/subsequent frames.
+	 */
+	ATH_TID_INSERT_HEAD(atid, bf, bf_list);
+	ath_tx_tid_sched(sc, atid);
+	/* Send the BAR if there are no other frames waiting */
+	if (ath_tx_tid_bar_tx_ready(sc, atid))
+		ath_tx_tid_bar_tx(sc, atid);
+
+	ATH_TX_UNLOCK(sc);
+}
+
+/*
+ * Common code for aggregate excessive retry/subframe retry.
+ * If retrying, queues buffers to bf_q. If not, frees the
+ * buffers.
+ *
+ * XXX should unify this with ath_tx_aggr_retry_unaggr()
+ */
+static int
+ath_tx_retry_subframe(struct ath_softc *sc, struct ath_buf *bf,
+    ath_bufhead *bf_q)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/* XXX clr11naggr should be done for all subframes */
+	ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc);
+	ath_hal_set11nburstduration(sc->sc_ah, bf->bf_desc, 0);
+
+	/* ath_hal_set11n_virtualmorefrag(sc->sc_ah, bf->bf_desc, 0); */
+
+	/*
+	 * If the buffer is marked as busy, we can't directly
+	 * reuse it. Instead, try to clone the buffer.
+	 * If the clone is successful, recycle the old buffer.
+	 * If the clone is unsuccessful, set bfs_retries to max
+	 * to force the next bit of code to free the buffer
+	 * for us.
+	 */
+	if ((bf->bf_state.bfs_retries < SWMAX_RETRIES) &&
+	    (bf->bf_flags & ATH_BUF_BUSY)) {
+		struct ath_buf *nbf;
+		nbf = ath_tx_retry_clone(sc, an, atid, bf);
+		if (nbf)
+			/* bf has been freed at this point */
+			bf = nbf;
+		else
+			bf->bf_state.bfs_retries = SWMAX_RETRIES + 1;
+	}
+
+	if (bf->bf_state.bfs_retries >= SWMAX_RETRIES) {
+		sc->sc_stats.ast_tx_swretrymax++;
+		DPRINTF(sc, ATH_DEBUG_SW_TX_RETRIES,
+		    "%s: max retries: seqno %d\n",
+		    __func__, SEQNO(bf->bf_state.bfs_seqno));
+		ath_tx_update_baw(sc, an, atid, bf);
+		if (!bf->bf_state.bfs_addedbaw)
+			DPRINTF(sc, ATH_DEBUG_SW_TX_BAW,
+			    "%s: wasn't added: seqno %d\n",
+			    __func__, SEQNO(bf->bf_state.bfs_seqno));
+		bf->bf_state.bfs_dobaw = 0;
+		return 1;
+	}
+
+	ath_tx_set_retry(sc, bf);
+	sc->sc_stats.ast_tx_swretries++;
+	bf->bf_next = NULL;		/* Just to make sure */
+
+	/* Clear the aggregate state */
+	bf->bf_state.bfs_aggr = 0;
+	bf->bf_state.bfs_ndelim = 0;	/* ??? needed? */
+	bf->bf_state.bfs_nframes = 1;
+
+	TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
+	return 0;
+}
+
+/*
+ * error pkt completion for an aggregate destination
+ */
+static void
+ath_tx_comp_aggr_error(struct ath_softc *sc, struct ath_buf *bf_first,
+    struct ath_tid *tid)
+{
+	struct ieee80211_node *ni = bf_first->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_buf *bf_next, *bf;
+	ath_bufhead bf_q;
+	int drops = 0;
+	struct ieee80211_tx_ampdu *tap;
+	ath_bufhead bf_cq;
+
+	TAILQ_INIT(&bf_q);
+	TAILQ_INIT(&bf_cq);
+
+	/*
+	 * Update rate control - all frames have failed.
+	 *
+	 * XXX use the length in the first frame in the series;
+	 * XXX just so things are consistent for now.
+	 */
+	ath_tx_update_ratectrl(sc, ni, bf_first->bf_state.bfs_rc,
+	    &bf_first->bf_status.ds_txstat,
+	    bf_first->bf_state.bfs_pktlen,
+	    bf_first->bf_state.bfs_nframes, bf_first->bf_state.bfs_nframes);
+
+	ATH_TX_LOCK(sc);
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+	sc->sc_stats.ast_tx_aggr_failall++;
+
+	/* Retry all subframes */
+	bf = bf_first;
+	while (bf) {
+		bf_next = bf->bf_next;
+		bf->bf_next = NULL;	/* Remove it from the aggr list */
+		sc->sc_stats.ast_tx_aggr_fail++;
+		if (ath_tx_retry_subframe(sc, bf, &bf_q)) {
+			drops++;
+			bf->bf_next = NULL;
+			TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list);
+		}
+		bf = bf_next;
+	}
+
+	/* Prepend all frames to the beginning of the queue */
+	while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) {
+		TAILQ_REMOVE(&bf_q, bf, bf_list);
+		ATH_TID_INSERT_HEAD(tid, bf, bf_list);
+	}
+
+	/*
+	 * Schedule the TID to be re-tried.
+	 */
+	ath_tx_tid_sched(sc, tid);
+
+	/*
+	 * send bar if we dropped any frames
+	 *
+	 * Keep the txq lock held for now, as we need to ensure
+	 * that ni_txseqs[] is consistent (as it's being updated
+	 * in the ifnet TX context or raw TX context.)
+	 */
+	if (drops) {
+		/* Suspend the TX queue and get ready to send the BAR */
+		ath_tx_tid_bar_suspend(sc, tid);
+	}
+
+	/*
+	 * Send BAR if required
+	 */
+	if (ath_tx_tid_bar_tx_ready(sc, tid))
+		ath_tx_tid_bar_tx(sc, tid);
+
+	ATH_TX_UNLOCK(sc);
+
+	/* Complete frames which errored out */
+	while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
+		TAILQ_REMOVE(&bf_cq, bf, bf_list);
+		ath_tx_default_comp(sc, bf, 0);
+	}
+}
+
+/*
+ * Handle clean-up of packets from an aggregate list.
+ *
+ * There's no need to update the BAW here - the session is being
+ * torn down.
+ */
+static void
+ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf_first)
+{
+	struct ath_buf *bf, *bf_next;
+	struct ieee80211_node *ni = bf_first->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf_first->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+
+	ATH_TX_LOCK(sc);
+
+	/* update incomp */
+	bf = bf_first;
+	while (bf) {
+		atid->incomp--;
+		bf = bf->bf_next;
+	}
+
+	if (atid->incomp == 0) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+		    "%s: TID %d: cleaned up! resume!\n",
+		    __func__, tid);
+		atid->cleanup_inprogress = 0;
+		ath_tx_tid_resume(sc, atid);
+	}
+
+	/* Send BAR if required */
+	/* XXX why would we send a BAR when transitioning to non-aggregation? */
+	/*
+	 * XXX TODO: we should likely just tear down the BAR state here,
+	 * rather than sending a BAR.
+	 */
+	if (ath_tx_tid_bar_tx_ready(sc, atid))
+		ath_tx_tid_bar_tx(sc, atid);
+
+	ATH_TX_UNLOCK(sc);
+
+	/* Handle frame completion */
+	bf = bf_first;
+	while (bf) {
+		bf_next = bf->bf_next;
+		ath_tx_default_comp(sc, bf, 1);
+		bf = bf_next;
+	}
+}
+
+/*
+ * Handle completion of an set of aggregate frames.
+ *
+ * Note: the completion handler is the last descriptor in the aggregate,
+ * not the last descriptor in the first frame.
+ */
+static void
+ath_tx_aggr_comp_aggr(struct ath_softc *sc, struct ath_buf *bf_first,
+    int fail)
+{
+	//struct ath_desc *ds = bf->bf_lastds;
+	struct ieee80211_node *ni = bf_first->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf_first->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+	struct ath_tx_status ts;
+	struct ieee80211_tx_ampdu *tap;
+	ath_bufhead bf_q;
+	ath_bufhead bf_cq;
+	int seq_st, tx_ok;
+	int hasba, isaggr;
+	uint32_t ba[2];
+	struct ath_buf *bf, *bf_next;
+	int ba_index;
+	int drops = 0;
+	int nframes = 0, nbad = 0, nf;
+	int pktlen;
+	/* XXX there's too much on the stack? */
+	struct ath_rc_series rc[ATH_RC_NUM];
+	int txseq;
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: called; hwq_depth=%d\n",
+	    __func__, atid->hwq_depth);
+
+	/*
+	 * Take a copy; this may be needed -after- bf_first
+	 * has been completed and freed.
+	 */
+	ts = bf_first->bf_status.ds_txstat;
+
+	TAILQ_INIT(&bf_q);
+	TAILQ_INIT(&bf_cq);
+
+	/* The TID state is kept behind the TXQ lock */
+	ATH_TX_LOCK(sc);
+
+	atid->hwq_depth--;
+	if (atid->hwq_depth < 0)
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: hwq_depth < 0: %d\n",
+		    __func__, atid->hwq_depth);
+
+	/*
+	 * If the TID is filtered, handle completing the filter
+	 * transition before potentially kicking it to the cleanup
+	 * function.
+	 *
+	 * XXX this is duplicate work, ew.
+	 */
+	if (atid->isfiltered)
+		ath_tx_tid_filt_comp_complete(sc, atid);
+
+	/*
+	 * Punt cleanup to the relevant function, not our problem now
+	 */
+	if (atid->cleanup_inprogress) {
+		if (atid->isfiltered)
+			DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+			    "%s: isfiltered=1, normal_comp?\n",
+			    __func__);
+		ATH_TX_UNLOCK(sc);
+		ath_tx_comp_cleanup_aggr(sc, bf_first);
+		return;
+	}
+
+	/*
+	 * If the frame is filtered, transition to filtered frame
+	 * mode and add this to the filtered frame list.
+	 *
+	 * XXX TODO: figure out how this interoperates with
+	 * BAR, pause and cleanup states.
+	 */
+	if ((ts.ts_status & HAL_TXERR_FILT) ||
+	    (ts.ts_status != 0 && atid->isfiltered)) {
+		if (fail != 0)
+			DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+			    "%s: isfiltered=1, fail=%d\n", __func__, fail);
+		ath_tx_tid_filt_comp_aggr(sc, atid, bf_first, &bf_cq);
+
+		/* Remove from BAW */
+		TAILQ_FOREACH_SAFE(bf, &bf_cq, bf_list, bf_next) {
+			if (bf->bf_state.bfs_addedbaw)
+				drops++;
+			if (bf->bf_state.bfs_dobaw) {
+				ath_tx_update_baw(sc, an, atid, bf);
+				if (!bf->bf_state.bfs_addedbaw)
+					DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+					    "%s: wasn't added: seqno %d\n",
+					    __func__,
+					    SEQNO(bf->bf_state.bfs_seqno));
+			}
+			bf->bf_state.bfs_dobaw = 0;
+		}
+		/*
+		 * If any intermediate frames in the BAW were dropped when
+		 * handling filtering things, send a BAR.
+		 */
+		if (drops)
+			ath_tx_tid_bar_suspend(sc, atid);
+
+		/*
+		 * Finish up by sending a BAR if required and freeing
+		 * the frames outside of the TX lock.
+		 */
+		goto finish_send_bar;
+	}
+
+	/*
+	 * XXX for now, use the first frame in the aggregate for
+	 * XXX rate control completion; it's at least consistent.
+	 */
+	pktlen = bf_first->bf_state.bfs_pktlen;
+
+	/*
+	 * 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_TX_UNLOCK(sc);
+		ath_tx_comp_aggr_error(sc, bf_first, atid);
+		return;
+	}
+
+	tap = ath_tx_get_tx_tid(an, tid);
+
+	/*
+	 * extract starting sequence and block-ack bitmap
+	 */
+	/* XXX endian-ness of seq_st, ba? */
+	seq_st = ts.ts_seqnum;
+	hasba = !! (ts.ts_flags & HAL_TX_BA);
+	tx_ok = (ts.ts_status == 0);
+	isaggr = bf_first->bf_state.bfs_aggr;
+	ba[0] = ts.ts_ba_low;
+	ba[1] = ts.ts_ba_high;
+
+	/*
+	 * Copy the TX completion status and the rate control
+	 * series from the first descriptor, as it may be freed
+	 * before the rate control code can get its grubby fingers
+	 * into things.
+	 */
+	memcpy(rc, bf_first->bf_state.bfs_rc, sizeof(rc));
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+	    "%s: txa_start=%d, tx_ok=%d, status=%.8x, flags=%.8x, "
+	    "isaggr=%d, seq_st=%d, hasba=%d, ba=%.8x, %.8x\n",
+	    __func__, tap->txa_start, tx_ok, ts.ts_status, ts.ts_flags,
+	    isaggr, seq_st, hasba, ba[0], ba[1]);
+
+	/*
+	 * The reference driver doesn't do this; it simply ignores
+	 * this check in its entirety.
+	 *
+	 * I've seen this occur when using iperf to send traffic
+	 * out tid 1 - the aggregate frames are all marked as TID 1,
+	 * but the TXSTATUS has TID=0.  So, let's just ignore this
+	 * check.
+	 */
+#if 0
+	/* Occasionally, the MAC sends a tx status for the wrong TID. */
+	if (tid != ts.ts_tid) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: tid %d != hw tid %d\n",
+		    __func__, tid, ts.ts_tid);
+		tx_ok = 0;
+	}
+#endif
+
+	/* AR5416 BA bug; this requires an interface reset */
+	if (isaggr && tx_ok && (! hasba)) {
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+		    "%s: AR5416 bug: hasba=%d; txok=%d, isaggr=%d, "
+		    "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
+	}
+
+	/*
+	 * Walk the list of frames, figure out which ones were correctly
+	 * sent and which weren't.
+	 */
+	bf = bf_first;
+	nf = bf_first->bf_state.bfs_nframes;
+
+	/* bf_first is going to be invalid once this list is walked */
+	bf_first = NULL;
+
+	/*
+	 * Walk the list of completed frames and determine
+	 * which need to be completed and which need to be
+	 * retransmitted.
+	 *
+	 * For completed frames, the completion functions need
+	 * to be called at the end of this function as the last
+	 * node reference may free the node.
+	 *
+	 * Finally, since the TXQ lock can't be held during the
+	 * completion callback (to avoid lock recursion),
+	 * the completion calls have to be done outside of the
+	 * lock.
+	 */
+	while (bf) {
+		nframes++;
+		ba_index = ATH_BA_INDEX(seq_st,
+		    SEQNO(bf->bf_state.bfs_seqno));
+		bf_next = bf->bf_next;
+		bf->bf_next = NULL;	/* Remove it from the aggr list */
+
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+		    "%s: checking bf=%p seqno=%d; ack=%d\n",
+		    __func__, bf, SEQNO(bf->bf_state.bfs_seqno),
+		    ATH_BA_ISSET(ba, ba_index));
+
+		if (tx_ok && ATH_BA_ISSET(ba, ba_index)) {
+			sc->sc_stats.ast_tx_aggr_ok++;
+			ath_tx_update_baw(sc, an, atid, bf);
+			bf->bf_state.bfs_dobaw = 0;
+			if (!bf->bf_state.bfs_addedbaw)
+				DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+				    "%s: wasn't added: seqno %d\n",
+				    __func__, SEQNO(bf->bf_state.bfs_seqno));
+			bf->bf_next = NULL;
+			TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list);
+		} else {
+			sc->sc_stats.ast_tx_aggr_fail++;
+			if (ath_tx_retry_subframe(sc, bf, &bf_q)) {
+				drops++;
+				bf->bf_next = NULL;
+				TAILQ_INSERT_TAIL(&bf_cq, bf, bf_list);
+			}
+			nbad++;
+		}
+		bf = bf_next;
+	}
+
+	/*
+	 * Now that the BAW updates have been done, unlock
+	 *
+	 * txseq is grabbed before the lock is released so we
+	 * have a consistent view of what -was- in the BAW.
+	 * Anything after this point will not yet have been
+	 * TXed.
+	 */
+	txseq = tap->txa_start;
+	ATH_TX_UNLOCK(sc);
+
+	if (nframes != nf)
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+		    "%s: num frames seen=%d; bf nframes=%d\n",
+		    __func__, nframes, nf);
+
+	/*
+	 * Now we know how many frames were bad, call the rate
+	 * control code.
+	 */
+	if (fail == 0)
+		ath_tx_update_ratectrl(sc, ni, rc, &ts, pktlen, nframes,
+		    nbad);
+
+	/*
+	 * send bar if we dropped any frames
+	 */
+	if (drops) {
+		/* Suspend the TX queue and get ready to send the BAR */
+		ATH_TX_LOCK(sc);
+		ath_tx_tid_bar_suspend(sc, atid);
+		ATH_TX_UNLOCK(sc);
+	}
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+	    "%s: txa_start now %d\n", __func__, tap->txa_start);
+
+	ATH_TX_LOCK(sc);
+
+	/* Prepend all frames to the beginning of the queue */
+	while ((bf = TAILQ_LAST(&bf_q, ath_bufhead_s)) != NULL) {
+		TAILQ_REMOVE(&bf_q, bf, bf_list);
+		ATH_TID_INSERT_HEAD(atid, bf, bf_list);
+	}
+
+	/*
+	 * Reschedule to grab some further frames.
+	 */
+	ath_tx_tid_sched(sc, atid);
+
+	/*
+	 * If the queue is filtered, re-schedule as required.
+	 *
+	 * This is required as there may be a subsequent TX descriptor
+	 * for this end-node that has CLRDMASK set, so it's quite possible
+	 * that a filtered frame will be followed by a non-filtered
+	 * (complete or otherwise) frame.
+	 *
+	 * XXX should we do this before we complete the frame?
+	 */
+	if (atid->isfiltered)
+		ath_tx_tid_filt_comp_complete(sc, atid);
+
+finish_send_bar:
+
+	/*
+	 * Send BAR if required
+	 */
+	if (ath_tx_tid_bar_tx_ready(sc, atid))
+		ath_tx_tid_bar_tx(sc, atid);
+
+	ATH_TX_UNLOCK(sc);
+
+	/* Do deferred completion */
+	while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
+		TAILQ_REMOVE(&bf_cq, bf, bf_list);
+		ath_tx_default_comp(sc, bf, 0);
+	}
+}
+
+/*
+ * Handle completion of unaggregated frames in an ADDBA
+ * session.
+ *
+ * Fail is set to 1 if the entry is being freed via a call to
+ * ath_tx_draintxq().
+ */
+static void
+ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ath_node *an = ATH_NODE(ni);
+	int tid = bf->bf_state.bfs_tid;
+	struct ath_tid *atid = &an->an_tid[tid];
+	struct ath_tx_status ts;
+	int drops = 0;
+
+	/*
+	 * Take a copy of this; filtering/cloning the frame may free the
+	 * bf pointer.
+	 */
+	ts = bf->bf_status.ds_txstat;
+
+	/*
+	 * Update rate control status here, before we possibly
+	 * punt to retry or cleanup.
+	 *
+	 * Do it outside of the TXQ lock.
+	 */
+	if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0))
+		ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc,
+		    &bf->bf_status.ds_txstat,
+		    bf->bf_state.bfs_pktlen,
+		    1, (ts.ts_status == 0) ? 0 : 1);
+
+	/*
+	 * This is called early so atid->hwq_depth can be tracked.
+	 * This unfortunately means that it's released and regrabbed
+	 * during retry and cleanup. That's rather inefficient.
+	 */
+	ATH_TX_LOCK(sc);
+
+	if (tid == IEEE80211_NONQOS_TID)
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=16!\n", __func__);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX,
+	    "%s: bf=%p: tid=%d, hwq_depth=%d, seqno=%d\n",
+	    __func__, bf, bf->bf_state.bfs_tid, atid->hwq_depth,
+	    SEQNO(bf->bf_state.bfs_seqno));
+
+	atid->hwq_depth--;
+	if (atid->hwq_depth < 0)
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n",
+		    __func__, atid->hwq_depth);
+
+	/*
+	 * If the TID is filtered, handle completing the filter
+	 * transition before potentially kicking it to the cleanup
+	 * function.
+	 */
+	if (atid->isfiltered)
+		ath_tx_tid_filt_comp_complete(sc, atid);
+
+	/*
+	 * If a cleanup is in progress, punt to comp_cleanup;
+	 * rather than handling it here. It's thus their
+	 * responsibility to clean up, call the completion
+	 * function in net80211, etc.
+	 */
+	if (atid->cleanup_inprogress) {
+		if (atid->isfiltered)
+			DPRINTF(sc, ATH_DEBUG_SW_TX,
+			    "%s: isfiltered=1, normal_comp?\n",
+			    __func__);
+		ATH_TX_UNLOCK(sc);
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: cleanup_unaggr\n",
+		    __func__);
+		ath_tx_comp_cleanup_unaggr(sc, bf);
+		return;
+	}
+
+	/*
+	 * XXX TODO: how does cleanup, BAR and filtered frame handling
+	 * overlap?
+	 *
+	 * If the frame is filtered OR if it's any failure but
+	 * the TID is filtered, the frame must be added to the
+	 * filtered frame list.
+	 *
+	 * However - a busy buffer can't be added to the filtered
+	 * list as it will end up being recycled without having
+	 * been made available for the hardware.
+	 */
+	if ((ts.ts_status & HAL_TXERR_FILT) ||
+	    (ts.ts_status != 0 && atid->isfiltered)) {
+		int freeframe;
+
+		if (fail != 0)
+			DPRINTF(sc, ATH_DEBUG_SW_TX,
+			    "%s: isfiltered=1, fail=%d\n",
+			    __func__, fail);
+		freeframe = ath_tx_tid_filt_comp_single(sc, atid, bf);
+		if (freeframe) {
+			/* Remove from BAW */
+			if (bf->bf_state.bfs_addedbaw)
+				drops++;
+			if (bf->bf_state.bfs_dobaw) {
+				ath_tx_update_baw(sc, an, atid, bf);
+				if (!bf->bf_state.bfs_addedbaw)
+					DPRINTF(sc, ATH_DEBUG_SW_TX,
+					    "%s: wasn't added: seqno %d\n",
+					    __func__, SEQNO(bf->bf_state.bfs_seqno));
+			}
+			bf->bf_state.bfs_dobaw = 0;
+		}
+
+		/*
+		 * If the frame couldn't be filtered, treat it as a drop and
+		 * prepare to send a BAR.
+		 */
+		if (freeframe && drops)
+			ath_tx_tid_bar_suspend(sc, atid);
+
+		/*
+		 * Send BAR if required
+		 */
+		if (ath_tx_tid_bar_tx_ready(sc, atid))
+			ath_tx_tid_bar_tx(sc, atid);
+
+		ATH_TX_UNLOCK(sc);
+		/*
+		 * If freeframe is set, then the frame couldn't be
+		 * cloned and bf is still valid.  Just complete/free it.
+		 */
+		if (freeframe)
+			ath_tx_default_comp(sc, bf, fail);
+
+
+		return;
+	}
+	/*
+	 * 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_TX_UNLOCK(sc);
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: retry_unaggr\n",
+		    __func__);
+		ath_tx_aggr_retry_unaggr(sc, bf);
+		return;
+	}
+
+	/* Success? Complete */
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=%d, seqno %d\n",
+	    __func__, tid, SEQNO(bf->bf_state.bfs_seqno));
+	if (bf->bf_state.bfs_dobaw) {
+		ath_tx_update_baw(sc, an, atid, bf);
+		bf->bf_state.bfs_dobaw = 0;
+		if (!bf->bf_state.bfs_addedbaw)
+			DPRINTF(sc, ATH_DEBUG_SW_TX,
+			    "%s: wasn't added: seqno %d\n",
+			    __func__, SEQNO(bf->bf_state.bfs_seqno));
+	}
+
+	/*
+	 * If the queue is filtered, re-schedule as required.
+	 *
+	 * This is required as there may be a subsequent TX descriptor
+	 * for this end-node that has CLRDMASK set, so it's quite possible
+	 * that a filtered frame will be followed by a non-filtered
+	 * (complete or otherwise) frame.
+	 *
+	 * XXX should we do this before we complete the frame?
+	 */
+	if (atid->isfiltered)
+		ath_tx_tid_filt_comp_complete(sc, atid);
+
+	/*
+	 * Send BAR if required
+	 */
+	if (ath_tx_tid_bar_tx_ready(sc, atid))
+		ath_tx_tid_bar_tx(sc, atid);
+
+	ATH_TX_UNLOCK(sc);
+
+	ath_tx_default_comp(sc, bf, fail);
+	/* bf is freed at this point */
+}
+
+void
+ath_tx_aggr_comp(struct ath_softc *sc, struct ath_buf *bf, int fail)
+{
+	if (bf->bf_state.bfs_aggr)
+		ath_tx_aggr_comp_aggr(sc, bf, fail);
+	else
+		ath_tx_aggr_comp_unaggr(sc, bf, fail);
+}
+
+/*
+ * Schedule some packets from the given node/TID to the hardware.
+ *
+ * This is the aggregate version.
+ */
+void
+ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid)
+{
+	struct ath_buf *bf;
+	struct ath_txq *txq = sc->sc_ac2q[tid->ac];
+	struct ieee80211_tx_ampdu *tap;
+	ATH_AGGR_STATUS status;
+	ath_bufhead bf_q;
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d\n", __func__, tid->tid);
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * XXX TODO: If we're called for a queue that we're leaking frames to,
+	 * ensure we only leak one.
+	 */
+
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+
+	if (tid->tid == IEEE80211_NONQOS_TID)
+		DPRINTF(sc, ATH_DEBUG_SW_TX, 
+		    "%s: called for TID=NONQOS_TID?\n", __func__);
+
+	for (;;) {
+		status = ATH_AGGR_DONE;
+
+		/*
+		 * If the upper layer has paused the TID, don't
+		 * queue any further packets.
+		 *
+		 * This can also occur from the completion task because
+		 * of packet loss; but as its serialised with this code,
+		 * it won't "appear" half way through queuing packets.
+		 */
+		if (! ath_tx_tid_can_tx_or_sched(sc, tid))
+			break;
+
+		bf = ATH_TID_FIRST(tid);
+		if (bf == NULL) {
+			break;
+		}
+
+		/*
+		 * If the packet doesn't fall within the BAW (eg a NULL
+		 * data frame), schedule it directly; continue.
+		 */
+		if (! bf->bf_state.bfs_dobaw) {
+			DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+			    "%s: non-baw packet\n",
+			    __func__);
+			ATH_TID_REMOVE(tid, bf, bf_list);
+
+			if (bf->bf_state.bfs_nframes > 1)
+				DPRINTF(sc, ATH_DEBUG_SW_TX, 
+				    "%s: aggr=%d, nframes=%d\n",
+				    __func__,
+				    bf->bf_state.bfs_aggr,
+				    bf->bf_state.bfs_nframes);
+
+			/*
+			 * This shouldn't happen - such frames shouldn't
+			 * ever have been queued as an aggregate in the
+			 * first place.  However, make sure the fields
+			 * are correctly setup just to be totally sure.
+			 */
+			bf->bf_state.bfs_aggr = 0;
+			bf->bf_state.bfs_nframes = 1;
+
+			/* Update CLRDMASK just before this frame is queued */
+			ath_tx_update_clrdmask(sc, tid, bf);
+
+			ath_tx_do_ratelookup(sc, bf);
+			ath_tx_calc_duration(sc, bf);
+			ath_tx_calc_protection(sc, bf);
+			ath_tx_set_rtscts(sc, bf);
+			ath_tx_rate_fill_rcflags(sc, bf);
+			ath_tx_setds(sc, bf);
+			ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc);
+
+			sc->sc_aggr_stats.aggr_nonbaw_pkt++;
+
+			/* Queue the packet; continue */
+			goto queuepkt;
+		}
+
+		TAILQ_INIT(&bf_q);
+
+		/*
+		 * Do a rate control lookup on the first frame in the
+		 * list. The rate control code needs that to occur
+		 * before it can determine whether to TX.
+		 * It's inaccurate because the rate control code doesn't
+		 * really "do" aggregate lookups, so it only considers
+		 * the size of the first frame.
+		 */
+		ath_tx_do_ratelookup(sc, bf);
+		bf->bf_state.bfs_rc[3].rix = 0;
+		bf->bf_state.bfs_rc[3].tries = 0;
+
+		ath_tx_calc_duration(sc, bf);
+		ath_tx_calc_protection(sc, bf);
+
+		ath_tx_set_rtscts(sc, bf);
+		ath_tx_rate_fill_rcflags(sc, bf);
+
+		status = ath_tx_form_aggr(sc, an, tid, &bf_q);
+
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+		    "%s: ath_tx_form_aggr() status=%d\n", __func__, status);
+
+		/*
+		 * No frames to be picked up - out of BAW
+		 */
+		if (TAILQ_EMPTY(&bf_q))
+			break;
+
+		/*
+		 * This assumes that the descriptor list in the ath_bufhead
+		 * are already linked together via bf_next pointers.
+		 */
+		bf = TAILQ_FIRST(&bf_q);
+
+		if (status == ATH_AGGR_8K_LIMITED)
+			sc->sc_aggr_stats.aggr_rts_aggr_limited++;
+
+		/*
+		 * If it's the only frame send as non-aggregate
+		 * assume that ath_tx_form_aggr() has checked
+		 * whether it's in the BAW and added it appropriately.
+		 */
+		if (bf->bf_state.bfs_nframes == 1) {
+			DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+			    "%s: single-frame aggregate\n", __func__);
+
+			/* Update CLRDMASK just before this frame is queued */
+			ath_tx_update_clrdmask(sc, tid, bf);
+
+			bf->bf_state.bfs_aggr = 0;
+			bf->bf_state.bfs_ndelim = 0;
+			ath_tx_setds(sc, bf);
+			ath_hal_clr11n_aggr(sc->sc_ah, bf->bf_desc);
+			if (status == ATH_AGGR_BAW_CLOSED)
+				sc->sc_aggr_stats.aggr_baw_closed_single_pkt++;
+			else
+				sc->sc_aggr_stats.aggr_single_pkt++;
+		} else {
+			DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+			    "%s: multi-frame aggregate: %d frames, "
+			    "length %d\n",
+			     __func__, bf->bf_state.bfs_nframes,
+			    bf->bf_state.bfs_al);
+			bf->bf_state.bfs_aggr = 1;
+			sc->sc_aggr_stats.aggr_pkts[bf->bf_state.bfs_nframes]++;
+			sc->sc_aggr_stats.aggr_aggr_pkt++;
+
+			/* Update CLRDMASK just before this frame is queued */
+			ath_tx_update_clrdmask(sc, tid, bf);
+
+			/*
+			 * Calculate the duration/protection as required.
+			 */
+			ath_tx_calc_duration(sc, bf);
+			ath_tx_calc_protection(sc, bf);
+
+			/*
+			 * Update the rate and rtscts information based on the
+			 * rate decision made by the rate control code;
+			 * the first frame in the aggregate needs it.
+			 */
+			ath_tx_set_rtscts(sc, bf);
+
+			/*
+			 * Setup the relevant descriptor fields
+			 * for aggregation. The first descriptor
+			 * already points to the rest in the chain.
+			 */
+			ath_tx_setds_11n(sc, bf);
+
+		}
+	queuepkt:
+		/* Set completion handler, multi-frame aggregate or not */
+		bf->bf_comp = ath_tx_aggr_comp;
+
+		if (bf->bf_state.bfs_tid == IEEE80211_NONQOS_TID)
+			DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: TID=16?\n", __func__);
+
+		/*
+		 * Update leak count and frame config if were leaking frames.
+		 *
+		 * XXX TODO: it should update all frames in an aggregate
+		 * correctly!
+		 */
+		ath_tx_leak_count_update(sc, tid, bf);
+
+		/* Punt to txq */
+		ath_tx_handoff(sc, txq, bf);
+
+		/* Track outstanding buffer count to hardware */
+		/* aggregates are "one" buffer */
+		tid->hwq_depth++;
+
+		/*
+		 * Break out if ath_tx_form_aggr() indicated
+		 * there can't be any further progress (eg BAW is full.)
+		 * Checking for an empty txq is done above.
+		 *
+		 * XXX locking on txq here?
+		 */
+		/* XXX TXQ locking */
+		if (txq->axq_aggr_depth >= sc->sc_hwq_limit_aggr ||
+		    (status == ATH_AGGR_BAW_CLOSED ||
+		     status == ATH_AGGR_LEAK_CLOSED))
+			break;
+	}
+}
+
+/*
+ * Schedule some packets from the given node/TID to the hardware.
+ *
+ * XXX TODO: this routine doesn't enforce the maximum TXQ depth.
+ * It just dumps frames into the TXQ.  We should limit how deep
+ * the transmit queue can grow for frames dispatched to the given
+ * TXQ.
+ *
+ * To avoid locking issues, either we need to own the TXQ lock
+ * at this point, or we need to pass in the maximum frame count
+ * from the caller.
+ */
+void
+ath_tx_tid_hw_queue_norm(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid)
+{
+	struct ath_buf *bf;
+	struct ath_txq *txq = sc->sc_ac2q[tid->ac];
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: node %p: TID %d: called\n",
+	    __func__, an, tid->tid);
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/* Check - is AMPDU pending or running? then print out something */
+	if (ath_tx_ampdu_pending(sc, an, tid->tid))
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ampdu pending?\n",
+		    __func__, tid->tid);
+	if (ath_tx_ampdu_running(sc, an, tid->tid))
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, ampdu running?\n",
+		    __func__, tid->tid);
+
+	for (;;) {
+
+		/*
+		 * If the upper layers have paused the TID, don't
+		 * queue any further packets.
+		 *
+		 * XXX if we are leaking frames, make sure we decrement
+		 * that counter _and_ we continue here.
+		 */
+		if (! ath_tx_tid_can_tx_or_sched(sc, tid))
+			break;
+
+		bf = ATH_TID_FIRST(tid);
+		if (bf == NULL) {
+			break;
+		}
+
+		ATH_TID_REMOVE(tid, bf, bf_list);
+
+		/* Sanity check! */
+		if (tid->tid != bf->bf_state.bfs_tid) {
+			DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: bfs_tid %d !="
+			    " tid %d\n", __func__, bf->bf_state.bfs_tid,
+			    tid->tid);
+		}
+		/* Normal completion handler */
+		bf->bf_comp = ath_tx_normal_comp;
+
+		/*
+		 * Override this for now, until the non-aggregate
+		 * completion handler correctly handles software retransmits.
+		 */
+		bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+
+		/* Update CLRDMASK just before this frame is queued */
+		ath_tx_update_clrdmask(sc, tid, bf);
+
+		/* Program descriptors + rate control */
+		ath_tx_do_ratelookup(sc, bf);
+		ath_tx_calc_duration(sc, bf);
+		ath_tx_calc_protection(sc, bf);
+		ath_tx_set_rtscts(sc, bf);
+		ath_tx_rate_fill_rcflags(sc, bf);
+		ath_tx_setds(sc, bf);
+
+		/*
+		 * Update the current leak count if
+		 * we're leaking frames; and set the
+		 * MORE flag as appropriate.
+		 */
+		ath_tx_leak_count_update(sc, tid, bf);
+
+		/* Track outstanding buffer count to hardware */
+		/* aggregates are "one" buffer */
+		tid->hwq_depth++;
+
+		/* Punt to hardware or software txq */
+		ath_tx_handoff(sc, txq, bf);
+	}
+}
+
+/*
+ * Schedule some packets to the given hardware queue.
+ *
+ * This function walks the list of TIDs (ie, ath_node TIDs
+ * with queued traffic) and attempts to schedule traffic
+ * from them.
+ *
+ * TID scheduling is implemented as a FIFO, with TIDs being
+ * added to the end of the queue after some frames have been
+ * scheduled.
+ */
+void
+ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_tid *tid, *next, *last;
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	/*
+	 * Don't schedule if the hardware queue is busy.
+	 * This (hopefully) gives some more time to aggregate
+	 * some packets in the aggregation queue.
+	 *
+	 * XXX It doesn't stop a parallel sender from sneaking
+	 * in transmitting a frame!
+	 */
+	/* XXX TXQ locking */
+	if (txq->axq_aggr_depth + txq->fifo.axq_depth >= sc->sc_hwq_limit_aggr) {
+		sc->sc_aggr_stats.aggr_sched_nopkt++;
+		return;
+	}
+	if (txq->axq_depth >= sc->sc_hwq_limit_nonaggr) {
+		sc->sc_aggr_stats.aggr_sched_nopkt++;
+		return;
+	}
+
+	last = TAILQ_LAST(&txq->axq_tidq, axq_t_s);
+
+	TAILQ_FOREACH_SAFE(tid, &txq->axq_tidq, axq_qelem, next) {
+		/*
+		 * Suspend paused queues here; they'll be resumed
+		 * once the addba completes or times out.
+		 */
+		DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: tid=%d, paused=%d\n",
+		    __func__, tid->tid, tid->paused);
+		ath_tx_tid_unsched(sc, tid);
+		/*
+		 * This node may be in power-save and we're leaking
+		 * a frame; be careful.
+		 */
+		if (! ath_tx_tid_can_tx_or_sched(sc, tid)) {
+			continue;
+		}
+		if (ath_tx_ampdu_running(sc, tid->an, tid->tid))
+			ath_tx_tid_hw_queue_aggr(sc, tid->an, tid);
+		else
+			ath_tx_tid_hw_queue_norm(sc, tid->an, tid);
+
+		/* Not empty? Re-schedule */
+		if (tid->axq_depth != 0)
+			ath_tx_tid_sched(sc, tid);
+
+		/*
+		 * Give the software queue time to aggregate more
+		 * packets.  If we aren't running aggregation then
+		 * we should still limit the hardware queue depth.
+		 */
+		/* XXX TXQ locking */
+		if (txq->axq_aggr_depth + txq->fifo.axq_depth >= sc->sc_hwq_limit_aggr) {
+			break;
+		}
+		if (txq->axq_depth >= sc->sc_hwq_limit_nonaggr) {
+			break;
+		}
+
+		/*
+		 * If this was the last entry on the original list, stop.
+		 * Otherwise nodes that have been rescheduled onto the end
+		 * of the TID FIFO list will just keep being rescheduled.
+		 *
+		 * XXX What should we do about nodes that were paused
+		 * but are pending a leaking frame in response to a ps-poll?
+		 * They'll be put at the front of the list; so they'll
+		 * prematurely trigger this condition! Ew.
+		 */
+		if (tid == last)
+			break;
+	}
+}
+
+/*
+ * TX addba handling
+ */
+
+/*
+ * Return net80211 TID struct pointer, or NULL for none
+ */
+struct ieee80211_tx_ampdu *
+ath_tx_get_tx_tid(struct ath_node *an, int tid)
+{
+	struct ieee80211_node *ni = &an->an_node;
+	struct ieee80211_tx_ampdu *tap;
+
+	if (tid == IEEE80211_NONQOS_TID)
+		return NULL;
+
+	tap = &ni->ni_tx_ampdu[tid];
+	return tap;
+}
+
+/*
+ * Is AMPDU-TX running?
+ */
+static int
+ath_tx_ampdu_running(struct ath_softc *sc, struct ath_node *an, int tid)
+{
+	struct ieee80211_tx_ampdu *tap;
+
+	if (tid == IEEE80211_NONQOS_TID)
+		return 0;
+
+	tap = ath_tx_get_tx_tid(an, tid);
+	if (tap == NULL)
+		return 0;	/* Not valid; default to not running */
+
+	return !! (tap->txa_flags & IEEE80211_AGGR_RUNNING);
+}
+
+/*
+ * Is AMPDU-TX negotiation pending?
+ */
+static int
+ath_tx_ampdu_pending(struct ath_softc *sc, struct ath_node *an, int tid)
+{
+	struct ieee80211_tx_ampdu *tap;
+
+	if (tid == IEEE80211_NONQOS_TID)
+		return 0;
+
+	tap = ath_tx_get_tx_tid(an, tid);
+	if (tap == NULL)
+		return 0;	/* Not valid; default to not pending */
+
+	return !! (tap->txa_flags & IEEE80211_AGGR_XCHGPEND);
+}
+
+/*
+ * Is AMPDU-TX pending for the given TID?
+ */
+
+
+/*
+ * Method to handle sending an ADDBA request.
+ *
+ * We tap this so the relevant flags can be set to pause the TID
+ * whilst waiting for the response.
+ *
+ * XXX there's no timeout handler we can override?
+ */
+int
+ath_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+    int dialogtoken, int baparamset, int batimeout)
+{
+	struct ath_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+	int tid = tap->txa_tid;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_tid *atid = &an->an_tid[tid];
+
+	/*
+	 * XXX danger Will Robinson!
+	 *
+	 * Although the taskqueue may be running and scheduling some more
+	 * packets, these should all be _before_ the addba sequence number.
+	 * However, net80211 will keep self-assigning sequence numbers
+	 * until addba has been negotiated.
+	 *
+	 * In the past, these packets would be "paused" (which still works
+	 * fine, as they're being scheduled to the driver in the same
+	 * serialised method which is calling the addba request routine)
+	 * and when the aggregation session begins, they'll be dequeued
+	 * as aggregate packets and added to the BAW. However, now there's
+	 * a "bf->bf_state.bfs_dobaw" flag, and this isn't set for these
+	 * packets. Thus they never get included in the BAW tracking and
+	 * this can cause the initial burst of packets after the addba
+	 * negotiation to "hang", as they quickly fall outside the BAW.
+	 *
+	 * The "eventual" solution should be to tag these packets with
+	 * dobaw. Although net80211 has given us a sequence number,
+	 * it'll be "after" the left edge of the BAW and thus it'll
+	 * fall within it.
+	 */
+	ATH_TX_LOCK(sc);
+	/*
+	 * 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_TX_UNLOCK(sc);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+	    "%s: %6D: called; dialogtoken=%d, baparamset=%d, batimeout=%d\n",
+	    __func__,
+	    ni->ni_macaddr,
+	    ":",
+	    dialogtoken, baparamset, batimeout);
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+	    "%s: txa_start=%d, ni_txseqs=%d\n",
+	    __func__, tap->txa_start, ni->ni_txseqs[tid]);
+
+	return sc->sc_addba_request(ni, tap, dialogtoken, baparamset,
+	    batimeout);
+}
+
+/*
+ * Handle an ADDBA response.
+ *
+ * We unpause the queue so TX'ing can resume.
+ *
+ * Any packets TX'ed from this point should be "aggregate" (whether
+ * aggregate or not) so the BAW is updated.
+ *
+ * Note! net80211 keeps self-assigning sequence numbers until
+ * ampdu is negotiated. This means the initially-negotiated BAW left
+ * edge won't match the ni->ni_txseq.
+ *
+ * So, being very dirty, the BAW left edge is "slid" here to match
+ * ni->ni_txseq.
+ *
+ * What likely SHOULD happen is that all packets subsequent to the
+ * addba request should be tagged as aggregate and queued as non-aggregate
+ * frames; thus updating the BAW. For now though, I'll just slide the
+ * window.
+ */
+int
+ath_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+    int status, int code, int batimeout)
+{
+	struct ath_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+	int tid = tap->txa_tid;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_tid *atid = &an->an_tid[tid];
+	int r;
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+	    "%s: %6D: called; status=%d, code=%d, batimeout=%d\n", __func__,
+	    ni->ni_macaddr,
+	    ":",
+	    status, code, batimeout);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+	    "%s: txa_start=%d, ni_txseqs=%d\n",
+	    __func__, tap->txa_start, ni->ni_txseqs[tid]);
+
+	/*
+	 * Call this first, so the interface flags get updated
+	 * before the TID is unpaused. Otherwise a race condition
+	 * exists where the unpaused TID still doesn't yet have
+	 * IEEE80211_AGGR_RUNNING set.
+	 */
+	r = sc->sc_addba_response(ni, tap, status, code, batimeout);
+
+	ATH_TX_LOCK(sc);
+	atid->addba_tx_pending = 0;
+	/*
+	 * XXX dirty!
+	 * Slide the BAW left edge to wherever net80211 left it for us.
+	 * Read above for more information.
+	 */
+	tap->txa_start = ni->ni_txseqs[tid];
+	ath_tx_tid_resume(sc, atid);
+	ATH_TX_UNLOCK(sc);
+	return r;
+}
+
+
+/*
+ * 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)
+{
+	struct ath_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+	int tid = tap->txa_tid;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_tid *atid = &an->an_tid[tid];
+	ath_bufhead bf_cq;
+	struct ath_buf *bf;
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: %6D: called\n",
+	    __func__,
+	    ni->ni_macaddr,
+	    ":");
+
+	/*
+	 * Pause TID traffic early, so there aren't any races
+	 * Unblock the pending BAR held traffic, if it's currently paused.
+	 */
+	ATH_TX_LOCK(sc);
+	ath_tx_tid_pause(sc, atid);
+	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_TX_UNLOCK(sc);
+
+	/* There's no need to hold the TXQ lock here */
+	sc->sc_addba_stop(ni, tap);
+
+	/*
+	 * 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.
+	 */
+	TAILQ_INIT(&bf_cq);
+	ATH_TX_LOCK(sc);
+	ath_tx_tid_cleanup(sc, an, tid, &bf_cq);
+	/*
+	 * Unpause the TID if no cleanup is required.
+	 */
+	if (! atid->cleanup_inprogress)
+		ath_tx_tid_resume(sc, atid);
+	ATH_TX_UNLOCK(sc);
+
+	/* Handle completing frames and fail them */
+	while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
+		TAILQ_REMOVE(&bf_cq, bf, bf_list);
+		ath_tx_default_comp(sc, bf, 1);
+	}
+
+}
+
+/*
+ * Handle a node reassociation.
+ *
+ * We may have a bunch of frames queued to the hardware; those need
+ * to be marked as cleanup.
+ */
+void
+ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an)
+{
+	struct ath_tid *tid;
+	int i;
+	ath_bufhead bf_cq;
+	struct ath_buf *bf;
+
+	TAILQ_INIT(&bf_cq);
+
+	ATH_TX_UNLOCK_ASSERT(sc);
+
+	ATH_TX_LOCK(sc);
+	for (i = 0; i < IEEE80211_TID_SIZE; i++) {
+		tid = &an->an_tid[i];
+		if (tid->hwq_depth == 0)
+			continue;
+		ath_tx_tid_pause(sc, tid);
+		DPRINTF(sc, ATH_DEBUG_NODE,
+		    "%s: %6D: TID %d: cleaning up TID\n",
+		    __func__,
+		    an->an_node.ni_macaddr,
+		    ":",
+		    i);
+		ath_tx_tid_cleanup(sc, an, i, &bf_cq);
+		/*
+		 * Unpause the TID if no cleanup is required.
+		 */
+		if (! tid->cleanup_inprogress)
+			ath_tx_tid_resume(sc, tid);
+	}
+	ATH_TX_UNLOCK(sc);
+
+	/* Handle completing frames and fail them */
+	while ((bf = TAILQ_FIRST(&bf_cq)) != NULL) {
+		TAILQ_REMOVE(&bf_cq, bf, bf_list);
+		ath_tx_default_comp(sc, bf, 1);
+	}
+}
+
+/*
+ * Note: net80211 bar_timeout() doesn't call this function on BAR failure;
+ * it simply tears down the aggregation session. Ew.
+ *
+ * It however will call ieee80211_ampdu_stop() which will call
+ * ic->ic_addba_stop().
+ *
+ * XXX This uses a hard-coded max BAR count value; the whole
+ * XXX BAR TX success or failure should be better handled!
+ */
+void
+ath_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+    int status)
+{
+	struct ath_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+	int tid = tap->txa_tid;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_tid *atid = &an->an_tid[tid];
+	int attempts = tap->txa_attempts;
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+	    "%s: %6D: called; txa_tid=%d, atid->tid=%d, status=%d, attempts=%d\n",
+	    __func__,
+	    ni->ni_macaddr,
+	    ":",
+	    tap->txa_tid,
+	    atid->tid,
+	    status,
+	    attempts);
+
+	/* Note: This may update the BAW details */
+	sc->sc_bar_response(ni, tap, status);
+
+	/* Unpause the TID */
+	/*
+	 * XXX if this is attempt=50, the TID will be downgraded
+	 * XXX to a non-aggregate session. So we must unpause the
+	 * XXX TID here or it'll never be done.
+	 *
+	 * Also, don't call it if bar_tx/bar_wait are 0; something
+	 * has beaten us to the punch? (XXX figure out what?)
+	 */
+	if (status == 0 || attempts == 50) {
+		ATH_TX_LOCK(sc);
+		if (atid->bar_tx == 0 || atid->bar_wait == 0)
+			DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+			    "%s: huh? bar_tx=%d, bar_wait=%d\n",
+			    __func__,
+			    atid->bar_tx, atid->bar_wait);
+		else
+			ath_tx_tid_bar_unsuspend(sc, atid);
+		ATH_TX_UNLOCK(sc);
+	}
+}
+
+/*
+ * This is called whenever the pending ADDBA request times out.
+ * Unpause and reschedule the TID.
+ */
+void
+ath_addba_response_timeout(struct ieee80211_node *ni,
+    struct ieee80211_tx_ampdu *tap)
+{
+	struct ath_softc *sc = ni->ni_ic->ic_ifp->if_softc;
+	int tid = tap->txa_tid;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_tid *atid = &an->an_tid[tid];
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
+	    "%s: %6D: TID=%d, called; resuming\n",
+	    __func__,
+	    ni->ni_macaddr,
+	    ":",
+	    tid);
+
+	ATH_TX_LOCK(sc);
+	atid->addba_tx_pending = 0;
+	ATH_TX_UNLOCK(sc);
+
+	/* Note: This updates the aggregate state to (again) pending */
+	sc->sc_addba_response_timeout(ni, tap);
+
+	/* Unpause the TID; which reschedules it */
+	ATH_TX_LOCK(sc);
+	ath_tx_tid_resume(sc, atid);
+	ATH_TX_UNLOCK(sc);
+}
+
+/*
+ * Check if a node is asleep or not.
+ */
+int
+ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	return (an->an_is_powersave);
+}
+
+/*
+ * Mark a node as currently "in powersaving."
+ * This suspends all traffic on the node.
+ *
+ * This must be called with the node/tx locks free.
+ *
+ * XXX TODO: the locking silliness below is due to how the node
+ * locking currently works.  Right now, the node lock is grabbed
+ * to do rate control lookups and these are done with the TX
+ * queue lock held.  This means the node lock can't be grabbed
+ * first here or a LOR will occur.
+ *
+ * Eventually (hopefully!) the TX path code will only grab
+ * the TXQ lock when transmitting and the ath_node lock when
+ * doing node/TID operations.  There are other complications -
+ * the sched/unsched operations involve walking the per-txq
+ * 'active tid' list and this requires both locks to be held.
+ */
+void
+ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an)
+{
+	struct ath_tid *atid;
+	struct ath_txq *txq;
+	int tid;
+
+	ATH_TX_UNLOCK_ASSERT(sc);
+
+	/* Suspend all traffic on the node */
+	ATH_TX_LOCK(sc);
+
+	if (an->an_is_powersave) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: %6D: node was already asleep!\n",
+		    __func__, an->an_node.ni_macaddr, ":");
+		ATH_TX_UNLOCK(sc);
+		return;
+	}
+
+	for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) {
+		atid = &an->an_tid[tid];
+		txq = sc->sc_ac2q[atid->ac];
+
+		ath_tx_tid_pause(sc, atid);
+	}
+
+	/* Mark node as in powersaving */
+	an->an_is_powersave = 1;
+
+	ATH_TX_UNLOCK(sc);
+}
+
+/*
+ * Mark a node as currently "awake."
+ * This resumes all traffic to the node.
+ */
+void
+ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an)
+{
+	struct ath_tid *atid;
+	struct ath_txq *txq;
+	int tid;
+
+	ATH_TX_UNLOCK_ASSERT(sc);
+
+	ATH_TX_LOCK(sc);
+
+	/* !? */
+	if (an->an_is_powersave == 0) {
+		ATH_TX_UNLOCK(sc);
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+		    "%s: an=%p: node was already awake\n",
+		    __func__, an);
+		return;
+	}
+
+	/* Mark node as awake */
+	an->an_is_powersave = 0;
+	/*
+	 * Clear any pending leaked frame requests
+	 */
+	an->an_leak_count = 0;
+
+	for (tid = 0; tid < IEEE80211_TID_SIZE; tid++) {
+		atid = &an->an_tid[tid];
+		txq = sc->sc_ac2q[atid->ac];
+
+		ath_tx_tid_resume(sc, atid);
+	}
+	ATH_TX_UNLOCK(sc);
+}
+
+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 = sizeof(struct ath_desc);
+	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;
+	sc->sc_tx.xmit_attach_comp_func = ath_legacy_attach_comp_func;
+
+	sc->sc_tx.xmit_dma_restart = ath_legacy_tx_dma_restart;
+	sc->sc_tx.xmit_handoff = ath_legacy_xmit_handoff;
+
+	sc->sc_tx.xmit_drain = ath_legacy_tx_drain;
+}

Modified: trunk/sys/dev/ath/if_ath_tx.h
===================================================================
--- trunk/sys/dev/ath/if_ath_tx.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_tx.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,12 +27,68 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_ath_tx.h 250866 2013-05-21 18:13:57Z adrian $
  */
 #ifndef	__IF_ATH_TX_H__
 #define	__IF_ATH_TX_H__
 
+/*
+ * some general macros
+ */
+#define	INCR(_l, _sz)		(_l) ++; (_l) &= ((_sz) - 1)
+/*
+ * return block-ack bitmap index given sequence and starting sequence
+ */
+#define	ATH_BA_INDEX(_st, _seq)	(((_seq) - (_st)) & (IEEE80211_SEQ_RANGE - 1))
+
+#define	WME_BA_BMP_SIZE	64
+#define	WME_MAX_BA	WME_BA_BMP_SIZE
+
+/*
+ * How 'busy' to try and keep the hardware txq
+ */
+#define	ATH_AGGR_MIN_QDEPTH		2
+#define	ATH_NONAGGR_MIN_QDEPTH		32
+
+/*
+ * Watermark for scheduling TIDs in order to maximise aggregation.
+ *
+ * If hwq_depth is greater than this, don't schedule the TID
+ * for packet scheduling - the hardware is already busy servicing
+ * this TID.
+ *
+ * If hwq_depth is less than this, schedule the TID for packet
+ * scheduling in the completion handler.
+ */
+#define	ATH_AGGR_SCHED_HIGH		4
+#define	ATH_AGGR_SCHED_LOW		2
+
+/*
+ * return whether a bit at index _n in bitmap _bm is set
+ * _sz is the size of the bitmap
+ */
+#define	ATH_BA_ISSET(_bm, _n)	(((_n) < (WME_BA_BMP_SIZE)) &&		\
+	    ((_bm)[(_n) >> 5] & (1 << ((_n) & 31))))
+
+
+/* extracting the seqno from buffer seqno */
+#define	SEQNO(_a)	((_a) >> IEEE80211_SEQ_SEQ_SHIFT)
+
+/*
+ * Whether the current sequence number is within the
+ * BAW.
+ */
+#define	BAW_WITHIN(_start, _bawsz, _seqno)	\
+	    ((((_seqno) - (_start)) & 4095) < (_bawsz))
+
+/*
+ * Maximum aggregate size
+ */
+#define	ATH_AGGR_MAXSIZE	65530
+
 extern void ath_freetx(struct mbuf *m);
+extern void ath_tx_node_flush(struct ath_softc *sc, struct ath_node *an);
+extern void ath_tx_txq_drain(struct ath_softc *sc, struct ath_txq *txq);
 extern void ath_txfrag_cleanup(struct ath_softc *sc, ath_bufhead *frags,
     struct ieee80211_node *ni);
 extern int ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags,
@@ -41,4 +98,73 @@
 extern int ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
     const struct ieee80211_bpf_params *params);
 
+/* software queue stuff */
+extern void ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni,
+    struct ath_txq *txq, int queue_to_head, struct ath_buf *bf);
+extern void ath_tx_tid_init(struct ath_softc *sc, struct ath_node *an);
+extern void ath_tx_tid_hw_queue_aggr(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid);
+extern void ath_tx_tid_hw_queue_norm(struct ath_softc *sc, struct ath_node *an,
+    struct ath_tid *tid);
+extern void ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq);
+extern void ath_tx_normal_comp(struct ath_softc *sc, struct ath_buf *bf,
+    int fail);
+extern void ath_tx_aggr_comp(struct ath_softc *sc, struct ath_buf *bf,
+    int fail);
+extern void ath_tx_addto_baw(struct ath_softc *sc, struct ath_node *an,
+    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 void ath_tx_tid_sched(struct ath_softc *sc, struct ath_tid *tid);
+
+/* TX addba handling */
+extern	int ath_addba_request(struct ieee80211_node *ni,
+    struct ieee80211_tx_ampdu *tap, int dialogtoken,
+    int baparamset, int batimeout);
+extern	int ath_addba_response(struct ieee80211_node *ni,
+    struct ieee80211_tx_ampdu *tap, int dialogtoken,
+    int code, int batimeout);
+extern	void ath_addba_stop(struct ieee80211_node *ni,
+    struct ieee80211_tx_ampdu *tap);
+extern	void ath_bar_response(struct ieee80211_node *ni,
+     struct ieee80211_tx_ampdu *tap, int status);
+extern	void ath_addba_response_timeout(struct ieee80211_node *ni,
+    struct ieee80211_tx_ampdu *tap);
+
+/*
+ * AP mode power save handling (of stations)
+ */
+extern	void ath_tx_node_sleep(struct ath_softc *sc, struct ath_node *an);
+extern	void ath_tx_node_wakeup(struct ath_softc *sc, struct ath_node *an);
+extern	int ath_tx_node_is_asleep(struct ath_softc *sc, struct ath_node *an);
+extern	void ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an);
+
+/*
+ * Hardware queue stuff
+ */
+extern	void ath_tx_push_pending(struct ath_softc *sc, struct ath_txq *txq);
+
+/*
+ * Misc debugging stuff
+ */
+#ifdef	ATH_DEBUG_ALQ
+extern	void ath_tx_alq_post(struct ath_softc *sc, struct ath_buf *bf_first);
+#endif	/* ATH_DEBUG_ALQ */
+
+/*
+ * Setup path
+ */
+#define	ath_txdma_setup(_sc)			\
+	(_sc)->sc_tx.xmit_setup(_sc)
+#define	ath_txdma_teardown(_sc)			\
+	(_sc)->sc_tx.xmit_teardown(_sc)
+#define	ath_txq_restart_dma(_sc, _txq)		\
+	(_sc)->sc_tx.xmit_dma_restart((_sc), (_txq))
+#define	ath_tx_handoff(_sc, _txq, _bf)		\
+	(_sc)->sc_tx.xmit_handoff((_sc), (_txq), (_bf))
+#define	ath_draintxq(_sc, _rtype)		\
+	(_sc)->sc_tx.xmit_drain((_sc), (_rtype))
+
+extern	void ath_xmit_setup_legacy(struct ath_softc *sc);
+
 #endif

Added: trunk/sys/dev/ath/if_ath_tx_edma.c
===================================================================
--- trunk/sys/dev/ath/if_ath_tx_edma.c	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_tx_edma.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,883 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/sys/dev/ath/if_ath_tx_edma.c 250444 2013-05-10 10:06:45Z 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>
+
+#ifdef	ATH_DEBUG_ALQ
+#include <dev/ath/if_ath_alq.h>
+#endif
+
+/*
+ * some general macros
+ */
+#define	INCR(_l, _sz)		(_l) ++; (_l) &= ((_sz) - 1)
+#define	DECR(_l, _sz)		(_l) --; (_l) &= ((_sz) - 1)
+
+/*
+ * XXX doesn't belong here, and should be tunable
+ */
+#define	ATH_TXSTATUS_RING_SIZE	512
+
+MALLOC_DECLARE(M_ATHDEV);
+
+static void ath_edma_tx_processq(struct ath_softc *sc, int dosched);
+
+/*
+ * Push some frames into the TX FIFO if we have space.
+ */
+static void
+ath_edma_tx_fifo_fill(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_buf *bf, *bf_last;
+	int i = 0;
+
+	ATH_TXQ_LOCK_ASSERT(txq);
+
+	DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: Q%d: called\n",
+	    __func__,
+	    txq->axq_qnum);
+
+	TAILQ_FOREACH(bf, &txq->axq_q, bf_list) {
+		if (txq->axq_fifo_depth >= HAL_TXFIFO_DEPTH)
+			break;
+
+		/*
+		 * We have space in the FIFO - so let's push a frame
+		 * into it.
+		 */
+
+		/*
+		 * Remove it from the normal list
+		 */
+		ATH_TXQ_REMOVE(txq, bf, bf_list);
+
+		/*
+		 * XXX for now, we only dequeue a frame at a time, so
+		 * that's only one buffer.  Later on when we just
+		 * push this staging _list_ into the queue, we'll
+		 * set bf_last to the end pointer in the list.
+		 */
+		bf_last = bf;
+		DPRINTF(sc, ATH_DEBUG_TX_PROC,
+		    "%s: Q%d: depth=%d; pushing %p->%p\n",
+		    __func__,
+		    txq->axq_qnum,
+		    txq->axq_fifo_depth,
+		    bf,
+		    bf_last);
+
+		/*
+		 * Append it to the FIFO staging list
+		 */
+		ATH_TXQ_INSERT_TAIL(&txq->fifo, bf, bf_list);
+
+		/*
+		 * Set fifo start / fifo end flags appropriately
+		 *
+		 */
+		bf->bf_flags |= ATH_BUF_FIFOPTR;
+		bf_last->bf_flags |= ATH_BUF_FIFOEND;
+
+		/*
+		 * Push _into_ the FIFO.
+		 */
+		ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr);
+#ifdef	ATH_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_XMIT_DESC)
+			ath_printtxbuf(sc, bf, txq->axq_qnum, i, 0);
+#endif/* ATH_DEBUG */
+#ifdef	ATH_DEBUG_ALQ
+		if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC))
+			ath_tx_alq_post(sc, bf);
+#endif /* ATH_DEBUG_ALQ */
+		txq->axq_fifo_depth++;
+		i++;
+	}
+	if (i > 0)
+		ath_hal_txstart(sc->sc_ah, txq->axq_qnum);
+}
+
+/*
+ * Re-initialise the DMA FIFO with the current contents of
+ * said TXQ.
+ *
+ * This should only be called as part of the chip reset path, as it
+ * assumes the FIFO is currently empty.
+ */
+static void
+ath_edma_dma_restart(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_buf *bf;
+	int i = 0;
+	int fifostart = 1;
+	int old_fifo_depth;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%d: called\n",
+	    __func__,
+	    txq->axq_qnum);
+
+	ATH_TXQ_LOCK_ASSERT(txq);
+
+	/*
+	 * Let's log if the tracked FIFO depth doesn't match
+	 * what we actually push in.
+	 */
+	old_fifo_depth = txq->axq_fifo_depth;
+	txq->axq_fifo_depth = 0;
+
+	/*
+	 * Walk the FIFO staging list, looking for "head" entries.
+	 * Since we may have a partially completed list of frames,
+	 * we push the first frame we see into the FIFO and re-mark
+	 * it as the head entry.  We then skip entries until we see
+	 * FIFO end, at which point we get ready to push another
+	 * entry into the FIFO.
+	 */
+	TAILQ_FOREACH(bf, &txq->fifo.axq_q, bf_list) {
+		/*
+		 * If we're looking for FIFOEND and we haven't found
+		 * it, skip.
+		 *
+		 * If we're looking for FIFOEND and we've found it,
+		 * reset for another descriptor.
+		 */
+#ifdef	ATH_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_XMIT_DESC)
+			ath_printtxbuf(sc, bf, txq->axq_qnum, i, 0);
+#endif/* ATH_DEBUG */
+#ifdef	ATH_DEBUG_ALQ
+		if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC))
+			ath_tx_alq_post(sc, bf);
+#endif /* ATH_DEBUG_ALQ */
+
+		if (fifostart == 0) {
+			if (bf->bf_flags & ATH_BUF_FIFOEND)
+				fifostart = 1;
+			continue;
+		}
+
+		/* Make sure we're not overflowing the FIFO! */
+		if (txq->axq_fifo_depth >= HAL_TXFIFO_DEPTH) {
+			device_printf(sc->sc_dev,
+			    "%s: Q%d: more frames in the queue; FIFO depth=%d?!\n",
+			    __func__,
+			    txq->axq_qnum,
+			    txq->axq_fifo_depth);
+		}
+
+#if 0
+		DPRINTF(sc, ATH_DEBUG_RESET,
+		    "%s: Q%d: depth=%d: pushing bf=%p; start=%d, end=%d\n",
+		    __func__,
+		    txq->axq_qnum,
+		    txq->axq_fifo_depth,
+		    bf,
+		    !! (bf->bf_flags & ATH_BUF_FIFOPTR),
+		    !! (bf->bf_flags & ATH_BUF_FIFOEND));
+#endif
+
+		/*
+		 * Set this to be the first buffer in the FIFO
+		 * list - even if it's also the last buffer in
+		 * a FIFO list!
+		 */
+		bf->bf_flags |= ATH_BUF_FIFOPTR;
+
+		/* Push it into the FIFO and bump the FIFO count */
+		ath_hal_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr);
+		txq->axq_fifo_depth++;
+
+		/*
+		 * If this isn't the last entry either, let's
+		 * clear fifostart so we continue looking for
+		 * said last entry.
+		 */
+		if (! (bf->bf_flags & ATH_BUF_FIFOEND))
+			fifostart = 0;
+		i++;
+	}
+
+	/* Only bother starting the queue if there's something in it */
+	if (i > 0)
+		ath_hal_txstart(sc->sc_ah, txq->axq_qnum);
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: Q%d: FIFO depth was %d, is %d\n",
+	    __func__,
+	    txq->axq_qnum,
+	    old_fifo_depth,
+	    txq->axq_fifo_depth);
+
+	/* And now, let's check! */
+	if (txq->axq_fifo_depth != old_fifo_depth) {
+		device_printf(sc->sc_dev,
+		    "%s: Q%d: FIFO depth should be %d, is %d\n",
+		    __func__,
+		    txq->axq_qnum,
+		    old_fifo_depth,
+		    txq->axq_fifo_depth);
+	}
+}
+
+/*
+ * Hand off this frame to a hardware queue.
+ *
+ * Things are a bit hairy in the EDMA world.  The TX FIFO is only
+ * 8 entries deep, so we need to keep track of exactly what we've
+ * pushed into the FIFO and what's just sitting in the TX queue,
+ * waiting to go out.
+ *
+ * So this is split into two halves - frames get appended to the
+ * TXQ; then a scheduler is called to push some frames into the
+ * actual TX FIFO.
+ */
+static void
+ath_edma_xmit_handoff_hw(struct ath_softc *sc, struct ath_txq *txq,
+    struct ath_buf *bf)
+{
+
+	ATH_TXQ_LOCK(txq);
+
+	KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
+	    ("%s: busy status 0x%x", __func__, bf->bf_flags));
+
+	/*
+	 * XXX TODO: write a hard-coded check to ensure that
+	 * the queue id in the TX descriptor matches txq->axq_qnum.
+	 */
+
+	/* Update aggr stats */
+	if (bf->bf_state.bfs_aggr)
+		txq->axq_aggr_depth++;
+
+	/* Push and update frame stats */
+	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+
+	/* For now, set the link pointer in the last descriptor
+	 * to be NULL.
+	 *
+	 * Later on, when it comes time to handling multiple descriptors
+	 * in one FIFO push, we can link descriptors together this way.
+	 */
+
+	/*
+	 * Finally, call the FIFO schedule routine to schedule some
+	 * frames to the FIFO.
+	 */
+	ath_edma_tx_fifo_fill(sc, txq);
+	ATH_TXQ_UNLOCK(txq);
+}
+
+/*
+ * Hand off this frame to a multicast software queue.
+ *
+ * The EDMA TX CABQ will get a list of chained frames, chained
+ * together using the next pointer.  The single head of that
+ * particular queue is pushed to the hardware CABQ.
+ */
+static void
+ath_edma_xmit_handoff_mcast(struct ath_softc *sc, struct ath_txq *txq,
+    struct ath_buf *bf)
+{
+
+	ATH_TX_LOCK_ASSERT(sc);
+	KASSERT((bf->bf_flags & ATH_BUF_BUSY) == 0,
+	    ("%s: busy status 0x%x", __func__, bf->bf_flags));
+
+	ATH_TXQ_LOCK(txq);
+	/*
+	 * XXX this is mostly duplicated in ath_tx_handoff_mcast().
+	 */
+	if (ATH_TXQ_LAST(txq, axq_q_s) != NULL) {
+		struct ath_buf *bf_last = ATH_TXQ_LAST(txq, axq_q_s);
+		struct ieee80211_frame *wh;
+
+		/* mark previous frame */
+		wh = mtod(bf_last->bf_m, struct ieee80211_frame *);
+		wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+
+		/* re-sync buffer to memory */
+		bus_dmamap_sync(sc->sc_dmat, bf_last->bf_dmamap,
+		   BUS_DMASYNC_PREWRITE);
+
+		/* link descriptor */
+		ath_hal_settxdesclink(sc->sc_ah,
+		    bf_last->bf_lastds,
+		    bf->bf_daddr);
+	}
+#ifdef	ATH_DEBUG_ALQ
+	if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXDESC))
+		ath_tx_alq_post(sc, bf);
+#endif	/* ATH_DEBUG_ALQ */
+	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+	ATH_TXQ_UNLOCK(txq);
+}
+
+/*
+ * Handoff this frame to the hardware.
+ *
+ * For the multicast queue, this will treat it as a software queue
+ * and append it to the list, after updating the MORE_DATA flag
+ * in the previous frame.  The cabq processing code will ensure
+ * that the queue contents gets transferred over.
+ *
+ * For the hardware queues, this will queue a frame to the queue
+ * like before, then populate the FIFO from that.  Since the
+ * EDMA hardware has 8 FIFO slots per TXQ, this ensures that
+ * frames such as management frames don't get prematurely dropped.
+ *
+ * This does imply that a similar flush-hwq-to-fifoq method will
+ * need to be called from the processq function, before the
+ * per-node software scheduler is called.
+ */
+static void
+ath_edma_xmit_handoff(struct ath_softc *sc, struct ath_txq *txq,
+    struct ath_buf *bf)
+{
+
+	DPRINTF(sc, ATH_DEBUG_XMIT_DESC,
+	    "%s: called; bf=%p, txq=%p, qnum=%d\n",
+	    __func__,
+	    bf,
+	    txq,
+	    txq->axq_qnum);
+
+	if (txq->axq_qnum == ATH_TXQ_SWQ)
+		ath_edma_xmit_handoff_mcast(sc, txq, bf);
+	else
+		ath_edma_xmit_handoff_hw(sc, txq, bf);
+}
+
+static int
+ath_edma_setup_txfifo(struct ath_softc *sc, int qnum)
+{
+	struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum];
+
+	te->m_fifo = malloc(sizeof(struct ath_buf *) * HAL_TXFIFO_DEPTH,
+	    M_ATHDEV,
+	    M_NOWAIT | M_ZERO);
+	if (te->m_fifo == NULL) {
+		device_printf(sc->sc_dev, "%s: malloc failed\n",
+		    __func__);
+		return (-ENOMEM);
+	}
+
+	/*
+	 * Set initial "empty" state.
+	 */
+	te->m_fifo_head = te->m_fifo_tail = te->m_fifo_depth = 0;
+	
+	return (0);
+}
+
+static int
+ath_edma_free_txfifo(struct ath_softc *sc, int qnum)
+{
+	struct ath_tx_edma_fifo *te = &sc->sc_txedma[qnum];
+
+	/* XXX TODO: actually deref the ath_buf entries? */
+	free(te->m_fifo, M_ATHDEV);
+	return (0);
+}
+
+static int
+ath_edma_dma_txsetup(struct ath_softc *sc)
+{
+	int error;
+	int i;
+
+	error = ath_descdma_alloc_desc(sc, &sc->sc_txsdma,
+	    NULL, "txcomp", sc->sc_tx_statuslen, ATH_TXSTATUS_RING_SIZE);
+	if (error != 0)
+		return (error);
+
+	ath_hal_setuptxstatusring(sc->sc_ah,
+	    (void *) sc->sc_txsdma.dd_desc,
+	    sc->sc_txsdma.dd_desc_paddr,
+	    ATH_TXSTATUS_RING_SIZE);
+
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+		ath_edma_setup_txfifo(sc, i);
+	}
+
+	return (0);
+}
+
+static int
+ath_edma_dma_txteardown(struct ath_softc *sc)
+{
+	int i;
+
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+		ath_edma_free_txfifo(sc, i);
+	}
+
+	ath_descdma_cleanup(sc, &sc->sc_txsdma, NULL);
+	return (0);
+}
+
+/*
+ * Drain all TXQs, potentially after completing the existing completed
+ * frames.
+ */
+static void
+ath_edma_tx_drain(struct ath_softc *sc, ATH_RESET_TYPE reset_type)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+	int i;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__);
+
+	(void) ath_stoptxdma(sc);
+
+	/*
+	 * If reset type is noloss, the TX FIFO needs to be serviced
+	 * and those frames need to be handled.
+	 *
+	 * Otherwise, just toss everything in each TX queue.
+	 */
+	if (reset_type == ATH_RESET_NOLOSS) {
+		ath_edma_tx_processq(sc, 0);
+		for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+			if (ATH_TXQ_SETUP(sc, i)) {
+				ATH_TXQ_LOCK(&sc->sc_txq[i]);
+				/*
+				 * Free the holding buffer; DMA is now
+				 * stopped.
+				 */
+				ath_txq_freeholdingbuf(sc, &sc->sc_txq[i]);
+				/*
+				 * Reset the link pointer to NULL; there's
+				 * no frames to chain DMA to.
+				 */
+				sc->sc_txq[i].axq_link = NULL;
+				ATH_TXQ_UNLOCK(&sc->sc_txq[i]);
+			}
+		}
+	} else {
+		for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
+			if (ATH_TXQ_SETUP(sc, i))
+				ath_tx_draintxq(sc, &sc->sc_txq[i]);
+		}
+	}
+
+	/* XXX dump out the TX completion FIFO contents */
+
+	/* XXX dump out the frames */
+
+	IF_LOCK(&ifp->if_snd);
+	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	IF_UNLOCK(&ifp->if_snd);
+	sc->sc_wd_timer = 0;
+}
+
+/*
+ * TX completion tasklet.
+ */
+
+static void
+ath_edma_tx_proc(void *arg, int npending)
+{
+	struct ath_softc *sc = (struct ath_softc *) arg;
+
+#if 0
+	DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: called, npending=%d\n",
+	    __func__, npending);
+#endif
+	ath_edma_tx_processq(sc, 1);
+}
+
+/*
+ * Process the TX status queue.
+ */
+static void
+ath_edma_tx_processq(struct ath_softc *sc, int dosched)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_STATUS status;
+	struct ath_tx_status ts;
+	struct ath_txq *txq;
+	struct ath_buf *bf;
+	struct ieee80211_node *ni;
+	int nacked = 0;
+	int idx;
+
+#ifdef	ATH_DEBUG
+	/* XXX */
+	uint32_t txstatus[32];
+#endif
+
+	for (idx = 0; ; idx++) {
+		bzero(&ts, sizeof(ts));
+
+		ATH_TXSTATUS_LOCK(sc);
+#ifdef	ATH_DEBUG
+		ath_hal_gettxrawtxdesc(ah, txstatus);
+#endif
+		status = ath_hal_txprocdesc(ah, NULL, (void *) &ts);
+		ATH_TXSTATUS_UNLOCK(sc);
+
+		if (status == HAL_EINPROGRESS)
+			break;
+
+#ifdef	ATH_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_TX_PROC)
+			if (ts.ts_queue_id != sc->sc_bhalq)
+			ath_printtxstatbuf(sc, NULL, txstatus, ts.ts_queue_id,
+			    idx, (status == HAL_OK));
+#endif
+
+		/*
+		 * If there is an error with this descriptor, continue
+		 * processing.
+		 *
+		 * XXX TBD: log some statistics?
+		 */
+		if (status == HAL_EIO) {
+			device_printf(sc->sc_dev, "%s: invalid TX status?\n",
+			    __func__);
+			break;
+		}
+
+#if defined(ATH_DEBUG_ALQ) && defined(ATH_DEBUG)
+		if (if_ath_alq_checkdebug(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS))
+			if_ath_alq_post(&sc->sc_alq, ATH_ALQ_EDMA_TXSTATUS,
+			    sc->sc_tx_statuslen,
+			    (char *) txstatus);
+#endif /* ATH_DEBUG_ALQ */
+
+		/*
+		 * At this point we have a valid status descriptor.
+		 * The QID and descriptor ID (which currently isn't set)
+		 * is part of the status.
+		 *
+		 * We then assume that the descriptor in question is the
+		 * -head- of the given QID.  Eventually we should verify
+		 * this by using the descriptor ID.
+		 */
+
+		/*
+		 * The beacon queue is not currently a "real" queue.
+		 * Frames aren't pushed onto it and the lock isn't setup.
+		 * So skip it for now; the beacon handling code will
+		 * free and alloc more beacon buffers as appropriate.
+		 */
+		if (ts.ts_queue_id == sc->sc_bhalq)
+			continue;
+
+		txq = &sc->sc_txq[ts.ts_queue_id];
+
+		ATH_TXQ_LOCK(txq);
+		bf = ATH_TXQ_FIRST(&txq->fifo);
+
+		/*
+		 * Work around the situation where I'm seeing notifications
+		 * for Q1 when no frames are available.  That needs to be
+		 * debugged but not by crashing _here_.
+		 */
+		if (bf == NULL) {
+			device_printf(sc->sc_dev, "%s: Q%d: empty?\n",
+			    __func__,
+			    ts.ts_queue_id);
+			ATH_TXQ_UNLOCK(txq);
+			continue;
+		}
+
+		DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: Q%d, bf=%p, start=%d, end=%d\n",
+		    __func__,
+		    ts.ts_queue_id, bf,
+		    !! (bf->bf_flags & ATH_BUF_FIFOPTR),
+		    !! (bf->bf_flags & ATH_BUF_FIFOEND));
+
+		/* XXX TODO: actually output debugging info about this */
+
+#if 0
+		/* XXX assert the buffer/descriptor matches the status descid */
+		if (ts.ts_desc_id != bf->bf_descid) {
+			device_printf(sc->sc_dev,
+			    "%s: mismatched descid (qid=%d, tsdescid=%d, "
+			    "bfdescid=%d\n",
+			    __func__,
+			    ts.ts_queue_id,
+			    ts.ts_desc_id,
+			    bf->bf_descid);
+		}
+#endif
+
+		/* This removes the buffer and decrements the queue depth */
+		ATH_TXQ_REMOVE(&txq->fifo, bf, bf_list);
+		if (bf->bf_state.bfs_aggr)
+			txq->axq_aggr_depth--;
+
+		/*
+		 * If this was the end of a FIFO set, decrement FIFO depth
+		 */
+		if (bf->bf_flags & ATH_BUF_FIFOEND)
+			txq->axq_fifo_depth--;
+
+		/*
+		 * If this isn't the final buffer in a FIFO set, mark
+		 * the buffer as busy so it goes onto the holding queue.
+		 */
+		if (! (bf->bf_flags & ATH_BUF_FIFOEND))
+			bf->bf_flags |= ATH_BUF_BUSY;
+
+		DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: Q%d: FIFO depth is now %d (%d)\n",
+		    __func__,
+		    txq->axq_qnum,
+		    txq->axq_fifo_depth,
+		    txq->fifo.axq_depth);
+
+		/* XXX assert FIFO depth >= 0 */
+		ATH_TXQ_UNLOCK(txq);
+
+		/*
+		 * Outside of the TX lock - if the buffer is end
+		 * end buffer in this FIFO, we don't need a holding
+		 * buffer any longer.
+		 */
+		if (bf->bf_flags & ATH_BUF_FIFOEND) {
+			ATH_TXQ_LOCK(txq);
+			ath_txq_freeholdingbuf(sc, txq);
+			ATH_TXQ_UNLOCK(txq);
+		}
+
+		/*
+		 * First we need to make sure ts_rate is valid.
+		 *
+		 * Pre-EDMA chips pass the whole TX descriptor to
+		 * the proctxdesc function which will then fill out
+		 * ts_rate based on the ts_finaltsi (final TX index)
+		 * in the TX descriptor.  However the TX completion
+		 * FIFO doesn't have this information.  So here we
+		 * do a separate HAL call to populate that information.
+		 *
+		 * The same problem exists with ts_longretry.
+		 * The FreeBSD HAL corrects ts_longretry in the HAL layer;
+		 * the AR9380 HAL currently doesn't.  So until the HAL
+		 * is imported and this can be added, we correct for it
+		 * here.
+		 */
+		/* XXX TODO */
+		/* XXX faked for now. Ew. */
+		if (ts.ts_finaltsi < 4) {
+			ts.ts_rate =
+			    bf->bf_state.bfs_rc[ts.ts_finaltsi].ratecode;
+			switch (ts.ts_finaltsi) {
+			case 3: ts.ts_longretry +=
+			    bf->bf_state.bfs_rc[2].tries;
+			case 2: ts.ts_longretry +=
+			    bf->bf_state.bfs_rc[1].tries;
+			case 1: ts.ts_longretry +=
+			    bf->bf_state.bfs_rc[0].tries;
+			}
+		} else {
+			device_printf(sc->sc_dev, "%s: finaltsi=%d\n",
+			    __func__,
+			    ts.ts_finaltsi);
+			ts.ts_rate = bf->bf_state.bfs_rc[0].ratecode;
+		}
+
+		/*
+		 * XXX This is terrible.
+		 *
+		 * Right now, some code uses the TX status that is
+		 * passed in here, but the completion handlers in the
+		 * software TX path also use bf_status.ds_txstat.
+		 * Ew.  That should all go away.
+		 *
+		 * XXX It's also possible the rate control completion
+		 * routine is called twice.
+		 */
+		memcpy(&bf->bf_status, &ts, sizeof(ts));
+
+		ni = bf->bf_node;
+
+		/* Update RSSI */
+		/* XXX duplicate from ath_tx_processq */
+		if (ni != NULL && ts.ts_status == 0 &&
+		    ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0)) {
+			nacked++;
+			sc->sc_stats.ast_tx_rssi = ts.ts_rssi;
+			ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi,
+			    ts.ts_rssi);
+		}
+
+		/* Handle frame completion and rate control update */
+		ath_tx_process_buf_completion(sc, txq, &ts, bf);
+
+		/* bf is invalid at this point */
+
+		/*
+		 * Now that there's space in the FIFO, let's push some
+		 * more frames into it.
+		 */
+		ATH_TXQ_LOCK(txq);
+		if (dosched)
+			ath_edma_tx_fifo_fill(sc, txq);
+		ATH_TXQ_UNLOCK(txq);
+	}
+
+	sc->sc_wd_timer = 0;
+
+	if (idx > 0) {
+		IF_LOCK(&sc->sc_ifp->if_snd);
+		sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+		IF_UNLOCK(&sc->sc_ifp->if_snd);
+	}
+
+	/* Kick software scheduler */
+	/*
+	 * XXX It's inefficient to do this if the FIFO queue is full,
+	 * but there's no easy way right now to only populate
+	 * the txq task for _one_ TXQ.  This should be fixed.
+	 */
+	if (dosched)
+		ath_tx_swq_kick(sc);
+}
+
+static void
+ath_edma_attach_comp_func(struct ath_softc *sc)
+{
+
+	TASK_INIT(&sc->sc_txtask, 0, ath_edma_tx_proc, sc);
+}
+
+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;
+	sc->sc_tx.xmit_attach_comp_func = ath_edma_attach_comp_func;
+
+	sc->sc_tx.xmit_dma_restart = ath_edma_dma_restart;
+	sc->sc_tx.xmit_handoff = ath_edma_xmit_handoff;
+	sc->sc_tx.xmit_drain = ath_edma_tx_drain;
+}


Property changes on: trunk/sys/dev/ath/if_ath_tx_edma.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/ath/if_ath_tx_edma.h
===================================================================
--- trunk/sys/dev/ath/if_ath_tx_edma.h	                        (rev 0)
+++ trunk/sys/dev/ath/if_ath_tx_edma.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -0,0 +1,37 @@
+/* $MidnightBSD$ */
+/*-
+ * 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: stable/10/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


Property changes on: trunk/sys/dev/ath/if_ath_tx_edma.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/ath/if_ath_tx_ht.c
===================================================================
--- trunk/sys/dev/ath/if_ath_tx_ht.c	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_tx_ht.c	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
  * All rights reserved.
@@ -28,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_tx_ht.c 250665 2013-05-15 18:33:05Z adrian $");
 
 #include "opt_inet.h"
 #include "opt_ath.h"
@@ -86,78 +87,489 @@
 #include <dev/ath/ath_tx99/ath_tx99.h>
 #endif
 
+#include <dev/ath/if_ath_tx.h>		/* XXX for some support functions */
 #include <dev/ath/if_ath_tx_ht.h>
+#include <dev/ath/if_athrate.h>
+#include <dev/ath/if_ath_debug.h>
 
 /*
+ * XXX net80211?
+ */
+#define	IEEE80211_AMPDU_SUBFRAME_DEFAULT		32
+
+#define	ATH_AGGR_DELIM_SZ	4	/* delimiter size */
+#define	ATH_AGGR_MINPLEN	256	/* in bytes, minimum packet length */
+/* number of delimiters for encryption padding */
+#define	ATH_AGGR_ENCRYPTDELIM	10
+
+/*
+ * returns delimiter padding required given the packet length
+ */
+#define	ATH_AGGR_GET_NDELIM(_len)					\
+	    (((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ?	\
+	    (ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2)
+
+#define	PADBYTES(_len)		((4 - ((_len) % 4)) % 4)
+
+int ath_max_4ms_framelen[4][32] = {
+	[MCS_HT20] = {
+		3212,  6432,  9648,  12864,  19300,  25736,  28952,  32172,
+		6424,  12852, 19280, 25708,  38568,  51424,  57852,  64280,
+		9628,  19260, 28896, 38528,  57792,  65532,  65532,  65532,
+		12828, 25656, 38488, 51320,  65532,  65532,  65532,  65532,
+	},
+	[MCS_HT20_SGI] = {
+		3572,  7144,  10720,  14296,  21444,  28596,  32172,  35744,
+		7140,  14284, 21428,  28568,  42856,  57144,  64288,  65532,
+		10700, 21408, 32112,  42816,  64228,  65532,  65532,  65532,
+		14256, 28516, 42780,  57040,  65532,  65532,  65532,  65532,
+	},
+	[MCS_HT40] = {
+		6680,  13360,  20044,  26724,  40092,  53456,  60140,  65532,
+		13348, 26700,  40052,  53400,  65532,  65532,  65532,  65532,
+		20004, 40008,  60016,  65532,  65532,  65532,  65532,  65532,
+		26644, 53292,  65532,  65532,  65532,  65532,  65532,  65532,
+	},
+	[MCS_HT40_SGI] = {
+		7420,  14844,  22272,  29696,  44544,  59396,  65532,  65532,
+		14832, 29668,  44504,  59340,  65532,  65532,  65532,  65532,
+		22232, 44464,  65532,  65532,  65532,  65532,  65532,  65532,
+		29616, 59232,  65532,  65532,  65532,  65532,  65532,  65532,
+	}
+};
+
+/*
+ * XXX should be in net80211
+ */
+static int ieee80211_mpdudensity_map[] = {
+	0,		/* IEEE80211_HTCAP_MPDUDENSITY_NA */
+	25,		/* IEEE80211_HTCAP_MPDUDENSITY_025 */
+	50,		/* IEEE80211_HTCAP_MPDUDENSITY_05 */
+	100,		/* IEEE80211_HTCAP_MPDUDENSITY_1 */
+	200,		/* IEEE80211_HTCAP_MPDUDENSITY_2 */
+	400,		/* IEEE80211_HTCAP_MPDUDENSITY_4 */
+	800,		/* IEEE80211_HTCAP_MPDUDENSITY_8 */
+	1600,		/* IEEE80211_HTCAP_MPDUDENSITY_16 */
+};
+
+/*
+ * XXX should be in the HAL/net80211 ?
+ */
+#define	BITS_PER_BYTE		8
+#define	OFDM_PLCP_BITS		22
+#define	HT_RC_2_MCS(_rc)	((_rc) & 0x7f)
+#define	HT_RC_2_STREAMS(_rc)	((((_rc) & 0x78) >> 3) + 1)
+#define	L_STF			8
+#define	L_LTF			8
+#define	L_SIG			4
+#define	HT_SIG			8
+#define	HT_STF			4
+#define	HT_LTF(_ns)		(4 * (_ns))
+#define	SYMBOL_TIME(_ns)	((_ns) << 2)		// ns * 4 us
+#define	SYMBOL_TIME_HALFGI(_ns)	(((_ns) * 18 + 4) / 5)	// ns * 3.6 us
+#define	NUM_SYMBOLS_PER_USEC(_usec)	(_usec >> 2)
+#define	NUM_SYMBOLS_PER_USEC_HALFGI(_usec)	(((_usec*5)-4)/18)
+#define	IS_HT_RATE(_rate)	((_rate) & 0x80)
+
+const uint32_t bits_per_symbol[][2] = {
+    /* 20MHz 40MHz */
+    {    26,   54 },     //  0: BPSK
+    {    52,  108 },     //  1: QPSK 1/2
+    {    78,  162 },     //  2: QPSK 3/4
+    {   104,  216 },     //  3: 16-QAM 1/2
+    {   156,  324 },     //  4: 16-QAM 3/4
+    {   208,  432 },     //  5: 64-QAM 2/3
+    {   234,  486 },     //  6: 64-QAM 3/4
+    {   260,  540 },     //  7: 64-QAM 5/6
+    {    52,  108 },     //  8: BPSK
+    {   104,  216 },     //  9: QPSK 1/2
+    {   156,  324 },     // 10: QPSK 3/4
+    {   208,  432 },     // 11: 16-QAM 1/2
+    {   312,  648 },     // 12: 16-QAM 3/4
+    {   416,  864 },     // 13: 64-QAM 2/3
+    {   468,  972 },     // 14: 64-QAM 3/4
+    {   520, 1080 },     // 15: 64-QAM 5/6
+    {    78,  162 },     // 16: BPSK
+    {   156,  324 },     // 17: QPSK 1/2
+    {   234,  486 },     // 18: QPSK 3/4
+    {   312,  648 },     // 19: 16-QAM 1/2
+    {   468,  972 },     // 20: 16-QAM 3/4
+    {   624, 1296 },     // 21: 64-QAM 2/3
+    {   702, 1458 },     // 22: 64-QAM 3/4
+    {   780, 1620 },     // 23: 64-QAM 5/6
+    {   104,  216 },     // 24: BPSK
+    {   208,  432 },     // 25: QPSK 1/2
+    {   312,  648 },     // 26: QPSK 3/4
+    {   416,  864 },     // 27: 16-QAM 1/2
+    {   624, 1296 },     // 28: 16-QAM 3/4
+    {   832, 1728 },     // 29: 64-QAM 2/3
+    {   936, 1944 },     // 30: 64-QAM 3/4
+    {  1040, 2160 },     // 31: 64-QAM 5/6
+};
+
+/*
+ * Fill in the rate array information based on the current
+ * node configuration and the choices made by the rate
+ * selection code and ath_buf setup code.
+ *
+ * Later on, this may end up also being made by the
+ * rate control code, but for now it can live here.
+ *
+ * This needs to be called just before the packet is
+ * queued to the software queue or hardware queue,
+ * so all of the needed fields in bf_state are setup.
+ */
+void
+ath_tx_rate_fill_rcflags(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ieee80211com *ic = ni->ni_ic;
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	struct ath_rc_series *rc = bf->bf_state.bfs_rc;
+	uint8_t rate;
+	int i;
+
+	for (i = 0; i < ATH_RC_NUM; i++) {
+		rc[i].flags = 0;
+		if (rc[i].tries == 0)
+			continue;
+
+		rate = rt->info[rc[i].rix].rateCode;
+
+		/*
+		 * Only enable short preamble for legacy rates
+		 */
+		if ((! IS_HT_RATE(rate)) && bf->bf_state.bfs_shpream)
+			rate |= rt->info[rc[i].rix].shortPreamble;
+
+		/*
+		 * Save this, used by the TX and completion code
+		 */
+		rc[i].ratecode = rate;
+
+		if (bf->bf_state.bfs_txflags &
+		    (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA))
+			rc[i].flags |= ATH_RC_RTSCTS_FLAG;
+
+		/* Only enable shortgi, 2040, dual-stream if HT is set */
+		if (IS_HT_RATE(rate)) {
+			rc[i].flags |= ATH_RC_HT_FLAG;
+
+			if (ni->ni_chw == 40)
+				rc[i].flags |= ATH_RC_CW40_FLAG;
+
+			if (ni->ni_chw == 40 &&
+			    ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40 &&
+			    ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
+				rc[i].flags |= ATH_RC_SGI_FLAG;
+
+			if (ni->ni_chw == 20 &&
+			    ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20 &&
+			    ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
+				rc[i].flags |= ATH_RC_SGI_FLAG;
+
+			/*
+			 * If we have STBC TX enabled and the receiver
+			 * can receive (at least) 1 stream STBC, AND it's
+			 * MCS 0-7, AND we have at least two chains enabled,
+			 * enable STBC.
+			 */
+			if (ic->ic_htcaps & IEEE80211_HTCAP_TXSTBC &&
+			    ni->ni_htcap & IEEE80211_HTCAP_RXSTBC_1STREAM &&
+			    (sc->sc_cur_txchainmask > 1) &&
+			    HT_RC_2_STREAMS(rate) == 1) {
+				rc[i].flags |= ATH_RC_STBC_FLAG;
+			}
+
+			/*
+			 * XXX TODO: LDPC
+			 */
+
+			/*
+			 * Dual / Triple stream rate?
+			 */
+			if (HT_RC_2_STREAMS(rate) == 2)
+				rc[i].flags |= ATH_RC_DS_FLAG;
+			else if (HT_RC_2_STREAMS(rate) == 3)
+				rc[i].flags |= ATH_RC_TS_FLAG;
+		}
+
+		/*
+		 * Calculate the maximum TX power cap for the current
+		 * node.
+		 */
+		rc[i].tx_power_cap = ieee80211_get_node_txpower(ni);
+
+		/*
+		 * Calculate the maximum 4ms frame length based
+		 * on the MCS rate, SGI and channel width flags.
+		 */
+		if ((rc[i].flags & ATH_RC_HT_FLAG) &&
+		    (HT_RC_2_MCS(rate) < 32)) {
+			int j;
+			if (rc[i].flags & ATH_RC_CW40_FLAG) {
+				if (rc[i].flags & ATH_RC_SGI_FLAG)
+					j = MCS_HT40_SGI;
+				else
+					j = MCS_HT40;
+			} else {
+				if (rc[i].flags & ATH_RC_SGI_FLAG)
+					j = MCS_HT20_SGI;
+				else
+					j = MCS_HT20;
+			}
+			rc[i].max4msframelen =
+			    ath_max_4ms_framelen[j][HT_RC_2_MCS(rate)];
+		} else
+			rc[i].max4msframelen = 0;
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+		    "%s: i=%d, rate=0x%x, flags=0x%x, max4ms=%d\n",
+		    __func__, i, rate, rc[i].flags, rc[i].max4msframelen);
+	}
+}
+
+/*
+ * Return the number of delimiters to be added to
+ * meet the minimum required mpdudensity.
+ *
+ * Caller should make sure that the rate is HT.
+ *
+ * TODO: is this delimiter calculation supposed to be the
+ * total frame length, the hdr length, the data length (including
+ * delimiters, padding, CRC, etc) or ?
+ *
+ * TODO: this should ensure that the rate control information
+ * HAS been setup for the first rate.
+ *
+ * TODO: ensure this is only called for MCS rates.
+ *
+ * TODO: enforce MCS < 31
+ */
+static int
+ath_compute_num_delims(struct ath_softc *sc, struct ath_buf *first_bf,
+    uint16_t pktlen)
+{
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	struct ieee80211_node *ni = first_bf->bf_node;
+	struct ieee80211vap *vap = ni->ni_vap;
+	int ndelim, mindelim = 0;
+	int mpdudensity;	 /* in 1/100'th of a microsecond */
+	uint8_t rc, rix, flags;
+	int width, half_gi;
+	uint32_t nsymbits, nsymbols;
+	uint16_t minlen;
+
+	/*
+	 * vap->iv_ampdu_density is a value, rather than the actual
+	 * density.
+	 */
+	if (vap->iv_ampdu_density > IEEE80211_HTCAP_MPDUDENSITY_16)
+		mpdudensity = 1600;		/* maximum density */
+	else
+		mpdudensity = ieee80211_mpdudensity_map[vap->iv_ampdu_density];
+
+	/* Select standard number of delimiters based on frame length */
+	ndelim = ATH_AGGR_GET_NDELIM(pktlen);
+
+	/*
+	 * If encryption is enabled, add extra delimiters to let the
+	 * crypto hardware catch up. This could be tuned per-MAC and
+	 * per-rate, but for now we'll simply assume encryption is
+	 * always enabled.
+	 *
+	 * Also note that the Atheros reference driver inserts two
+	 * delimiters by default for pre-AR9380 peers.  This will
+	 * include "that" required delimiter.
+	 */
+	ndelim += ATH_AGGR_ENCRYPTDELIM;
+
+	/*
+	 * For AR9380, there's a minimum number of delimeters
+	 * required when doing RTS.
+	 *
+	 * XXX TODO: this is only needed if (a) RTS/CTS is enabled, and
+	 * XXX (b) this is the first sub-frame in the aggregate.
+	 */
+	if (sc->sc_use_ent && (sc->sc_ent_cfg & AH_ENT_RTSCTS_DELIM_WAR)
+	    && ndelim < AH_FIRST_DESC_NDELIMS)
+		ndelim = AH_FIRST_DESC_NDELIMS;
+
+	/*
+	 * If sc_delim_min_pad is non-zero, enforce it as the minimum
+	 * pad delimiter count.
+	 */
+	if (sc->sc_delim_min_pad != 0)
+		ndelim = MAX(ndelim, sc->sc_delim_min_pad);
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+	    "%s: pktlen=%d, ndelim=%d, mpdudensity=%d\n",
+	    __func__, pktlen, ndelim, mpdudensity);
+
+	/*
+	 * If the MPDU density is 0, we can return here.
+	 * Otherwise, we need to convert the desired mpdudensity
+	 * into a byte length, based on the rate in the subframe.
+	 */
+	if (mpdudensity == 0)
+		return ndelim;
+
+	/*
+	 * Convert desired mpdu density from microeconds to bytes based
+	 * on highest rate in rate series (i.e. first rate) to determine
+	 * required minimum length for subframe. Take into account
+	 * whether high rate is 20 or 40Mhz and half or full GI.
+	 */
+	rix = first_bf->bf_state.bfs_rc[0].rix;
+	rc = rt->info[rix].rateCode;
+	flags = first_bf->bf_state.bfs_rc[0].flags;
+	width = !! (flags & ATH_RC_CW40_FLAG);
+	half_gi = !! (flags & ATH_RC_SGI_FLAG);
+
+	/*
+	 * mpdudensity is in 1/100th of a usec, so divide by 100
+	 */
+	if (half_gi)
+		nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity);
+	else
+		nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity);
+	nsymbols /= 100;
+
+	if (nsymbols == 0)
+		nsymbols = 1;
+
+	nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
+	minlen = (nsymbols * nsymbits) / BITS_PER_BYTE;
+
+	/*
+	 * Min length is the minimum frame length for the
+	 * required MPDU density.
+	 */
+	if (pktlen < minlen) {
+		mindelim = (minlen - pktlen) / ATH_AGGR_DELIM_SZ;
+		ndelim = MAX(mindelim, ndelim);
+	}
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+	    "%s: pktlen=%d, minlen=%d, rix=%x, rc=%x, width=%d, hgi=%d, ndelim=%d\n",
+	    __func__, pktlen, minlen, rix, rc, width, half_gi, ndelim);
+
+	return ndelim;
+}
+
+/*
+ * Fetch the aggregation limit.
+ *
+ * It's the lowest of the four rate series 4ms frame length.
+ */
+static int
+ath_get_aggr_limit(struct ath_softc *sc, struct ath_buf *bf)
+{
+	int amin = ATH_AGGR_MAXSIZE;
+	int i;
+
+	if (sc->sc_aggr_limit > 0 && sc->sc_aggr_limit < ATH_AGGR_MAXSIZE)
+		amin = sc->sc_aggr_limit;
+
+	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);
+	}
+
+	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: max frame len= %d\n",
+	    __func__, amin);
+
+	return amin;
+}
+
+/*
  * Setup a 11n rate series structure
  *
  * This should be called for both legacy and MCS rates.
+ *
+ * This uses the rate series stuf from ath_tx_rate_fill_rcflags().
+ *
+ * It, along with ath_buf_set_rate, must be called -after- a burst
+ * or aggregate is setup.
  */
 static void
 ath_rateseries_setup(struct ath_softc *sc, struct ieee80211_node *ni,
-    HAL_11N_RATE_SERIES *series, unsigned int pktlen, uint8_t *rix,
-    uint8_t *try, int flags)
+    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;
 	const HAL_RATE_TABLE *rt = sc->sc_currates;
 	int i;
+	int pktlen;
+	struct ath_rc_series *rc = bf->bf_state.bfs_rc;
 
 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
 	    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE))
 		shortPreamble = AH_TRUE;
 
+	/*
+	 * If this is the first frame in an aggregate series,
+	 * use the aggregate length.
+	 */
+	if (bf->bf_state.bfs_aggr)
+		pktlen = bf->bf_state.bfs_al;
+	else
+		pktlen = bf->bf_state.bfs_pktlen;
+
+	/*
+	 * XXX TODO: modify this routine to use the bfs_rc[x].flags
+	 * 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 (try[i] == 0)
+		if (rc[i].tries == 0)
 			continue;
 
-		series[i].Tries = try[i];
+		series[i].Tries = rc[i].tries;
 
 		/*
-		 * XXX this isn't strictly correct - sc_txchainmask
-		 * XXX isn't the currently active chainmask;
-		 * XXX it's the interface chainmask at startup.
-		 * XXX It's overridden in the HAL rate scenario function
-		 * XXX for now.
+		 * XXX TODO: When the NIC is capable of three stream TX,
+		 * transmit 1/2 stream rates on two streams.
+		 *
+		 * This reduces the power consumption of the NIC and
+		 * keeps it within the PCIe slot power limits.
 		 */
-		series[i].ChSel = sc->sc_txchainmask;
+		series[i].ChSel = sc->sc_cur_txchainmask;
 
-		if (flags & (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA))
-			series[i].RateFlags |= HAL_RATESERIES_RTS_CTS;
+		/*
+		 * Setup rate and TX power cap for this series.
+		 */
+		series[i].Rate = rt->info[rc[i].rix].rateCode;
+		series[i].RateIndex = rc[i].rix;
+		series[i].tx_power_cap = rc[i].tx_power_cap;
 
 		/*
-		 * Transmit 40MHz frames only if the node has negotiated
-		 * it rather than whether the node is capable of it or not.
-	 	 * It's subtly different in the hostap case.
-	 	 */
-		if (ni->ni_chw == 40)
-			series[i].RateFlags |= HAL_RATESERIES_2040;
+		 * Enable RTS/CTS as appropriate.
+		 */
+		if (rc[i].flags & ATH_RC_RTSCTS_FLAG)
+			series[i].RateFlags |= HAL_RATESERIES_RTS_CTS;
 
 		/*
-		 * Set short-GI only if the node has advertised it
-		 * the channel width is suitable, and we support it.
-		 * We don't currently have a "negotiated" set of bits -
-		 * ni_htcap is what the remote end sends, not what this
-		 * node is capable of.
+		 * 11n rate? Update 11n flags.
 		 */
-		if (ni->ni_chw == 40 &&
-		    ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40 &&
-		    ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
-			series[i].RateFlags |= HAL_RATESERIES_HALFGI;
+		if (rc[i].flags & ATH_RC_HT_FLAG) {
+			if (rc[i].flags & ATH_RC_CW40_FLAG)
+				series[i].RateFlags |= HAL_RATESERIES_2040;
 
-		if (ni->ni_chw == 20 &&
-		    ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20 &&
-		    ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
-			series[i].RateFlags |= HAL_RATESERIES_HALFGI;
+			if (rc[i].flags & ATH_RC_SGI_FLAG)
+				series[i].RateFlags |= HAL_RATESERIES_HALFGI;
 
-		series[i].Rate = rt->info[rix[i]].rateCode;
+			if (rc[i].flags & ATH_RC_STBC_FLAG)
+				series[i].RateFlags |= HAL_RATESERIES_STBC;
+		}
 
-		/* PktDuration doesn't include slot, ACK, RTS, etc timing - it's just the packet duration */
-		if (series[i].Rate & IEEE80211_RATE_MCS) {
+		/*
+		 * PktDuration doesn't include slot, ACK, RTS, etc timing -
+		 * it's just the packet duration
+		 */
+		if (rc[i].flags & ATH_RC_HT_FLAG) {
 			series[i].PktDuration =
 			    ath_computedur_ht(pktlen
 				, series[i].Rate
@@ -166,26 +578,28 @@
 				, series[i].RateFlags & HAL_RATESERIES_HALFGI);
 		} else {
 			if (shortPreamble)
-				series[i].Rate |= rt->info[rix[i]].shortPreamble;
+				series[i].Rate |=
+				    rt->info[rc[i].rix].shortPreamble;
 			series[i].PktDuration = ath_hal_computetxtime(ah,
-			    rt, pktlen, rix[i], shortPreamble);
+			    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; txpowcap %d, rateFlags %x\n",
 		    i,
 		    series[i].Rate,
 		    series[i].Tries,
 		    series[i].PktDuration,
 		    series[i].ChSel,
+		    series[i].tx_power_cap,
 		    series[i].RateFlags);
 	}
 }
@@ -198,46 +612,286 @@
  * This isn't useful for sending beacon frames, which has different needs
  * wrt what's passed into the rate scenario function.
  */
-
 void
-ath_buf_set_rate(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf,
-    int pktlen, int flags, uint8_t ctsrate, int is_pspoll, uint8_t *rix, uint8_t *try)
+ath_buf_set_rate(struct ath_softc *sc, struct ieee80211_node *ni,
+    struct ath_buf *bf)
 {
 	HAL_11N_RATE_SERIES series[4];
 	struct ath_desc *ds = bf->bf_desc;
-	struct ath_desc *lastds = NULL;
 	struct ath_hal *ah = sc->sc_ah;
+	int is_pspoll = (bf->bf_state.bfs_atype == HAL_PKT_TYPE_PSPOLL);
+	int ctsrate = bf->bf_state.bfs_ctsrate;
+	int flags = bf->bf_state.bfs_txflags;
 
 	/* Setup rate scenario */
 	memset(&series, 0, sizeof(series));
 
-	ath_rateseries_setup(sc, ni, series, pktlen, rix, try, flags);
+	ath_rateseries_setup(sc, ni, bf, series);
 
-	/* Enforce AR5416 aggregate limit - can't do RTS w/ an agg frame > 8k */
-
-	/* Enforce RTS and CTS are mutually exclusive */
-
-	/* Get a pointer to the last tx descriptor in the list */
-	lastds = &bf->bf_desc[bf->bf_nseg - 1];
-
 #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 */
-	ath_hal_setuplasttxdesc(ah, lastds, ds);
-
 	/* Set burst duration */
-	/* This should only be done if aggregate protection is enabled */
+	/*
+	 * This is only required when doing 11n burst, not aggregation
+	 * ie, if there's a second frame in a RIFS or A-MPDU burst
+	 * w/ >1 A-MPDU frame bursting back to back.
+	 * Normal A-MPDU doesn't do bursting -between- aggregates.
+	 *
+	 * .. and it's highly likely this won't ever be implemented
+	 */
 	//ath_hal_set11nburstduration(ah, ds, 8192);
 }
+
+/*
+ * Form an aggregate packet list.
+ *
+ * This function enforces the aggregate restrictions/requirements.
+ *
+ * These are:
+ *
+ * + The aggregate size maximum (64k for AR9160 and later, 8K for
+ *   AR5416 when doing RTS frame protection.)
+ * + Maximum number of sub-frames for an aggregate
+ * + The aggregate delimiter size, giving MACs time to do whatever is
+ *   needed before each frame
+ * + Enforce the BAW limit
+ *
+ * Each descriptor queued should have the DMA setup.
+ * The rate series, descriptor setup, linking, etc is all done
+ * externally. This routine simply chains them together.
+ * ath_tx_setds_11n() will take care of configuring the per-
+ * descriptor setup, and ath_buf_set_rate() will configure the
+ * rate control.
+ *
+ * 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
+ * task.
+ *
+ * So if you want to call this from an upper layer context (eg, to direct-
+ * 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)
+{
+	//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;
+	struct ieee80211_tx_ampdu *tap;
+	int status = ATH_AGGR_DONE;
+	int prev_frames = 0;	/* XXX for AR5416 burst, not done here */
+	int prev_al = 0;	/* XXX also for AR5416 burst */
+
+	ATH_TX_LOCK_ASSERT(sc);
+
+	tap = ath_tx_get_tx_tid(an, tid->tid);
+	if (tap == NULL) {
+		status = ATH_AGGR_ERROR;
+		goto finish;
+	}
+
+	h_baw = tap->txa_wnd / 2;
+
+	for (;;) {
+		bf = ATH_TID_FIRST(tid);
+		if (bf_first == NULL)
+			bf_first = bf;
+		if (bf == NULL) {
+			status = ATH_AGGR_DONE;
+			break;
+		} else {
+			/*
+			 * It's the first frame;
+			 * set the aggregation limit based on the
+			 * rate control decision that has been made.
+			 */
+			aggr_limit = ath_get_aggr_limit(sc, bf_first);
+		}
+
+		/* Set this early just so things don't get confused */
+		bf->bf_next = NULL;
+
+		/*
+		 * If the frame doesn't have a sequence number that we're
+		 * tracking in the BAW (eg NULL QOS data frame), we can't
+		 * aggregate it. Stop the aggregation process; the sender
+		 * can then TX what's in the list thus far and then
+		 * TX the frame individually.
+		 */
+		if (! bf->bf_state.bfs_dobaw) {
+			status = ATH_AGGR_NONAGGR;
+			break;
+		}
+
+		/*
+		 * If any of the rates are non-HT, this packet
+		 * can't be aggregated.
+		 * XXX TODO: add a bf_state flag which gets marked
+		 * if any active rate is non-HT.
+		 */
+
+		/*
+		 * do not exceed aggregation limit
+		 */
+		al_delta = ATH_AGGR_DELIM_SZ + bf->bf_state.bfs_pktlen;
+		if (nframes &&
+		    (aggr_limit < (al + bpad + al_delta + prev_al))) {
+			status = ATH_AGGR_LIMITED;
+			break;
+		}
+
+		/*
+		 * If RTS/CTS is set on the first frame, enforce
+		 * the RTS aggregate limit.
+		 */
+		if (bf_first->bf_state.bfs_txflags &
+		    (HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA)) {
+			if (nframes &&
+			   (sc->sc_rts_aggr_limit <
+			     (al + bpad + al_delta + prev_al))) {
+				status = ATH_AGGR_8K_LIMITED;
+				break;
+			}
+		}
+
+		/*
+		 * Do not exceed subframe limit.
+		 */
+		if ((nframes + prev_frames) >= MIN((h_baw),
+		    IEEE80211_AMPDU_SUBFRAME_DEFAULT)) {
+			status = ATH_AGGR_LIMITED;
+			break;
+		}
+
+		/*
+		 * If the current frame has an RTS/CTS configuration
+		 * that differs from the first frame, override the
+		 * subsequent frame with this config.
+		 */
+		if (bf != bf_first) {
+			bf->bf_state.bfs_txflags &=
+			    ~ (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA);
+			bf->bf_state.bfs_txflags |=
+			    bf_first->bf_state.bfs_txflags &
+			    (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA);
+		}
+
+		/*
+		 * 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))) {
+			status = ATH_AGGR_BAW_CLOSED;
+			break;
+		}
+
+		/*
+		 * this packet is part of an aggregate.
+		 */
+		ATH_TID_REMOVE(tid, bf, bf_list);
+
+		/* The TID lock is required for the BAW update */
+		ath_tx_addto_baw(sc, an, tid, bf);
+		bf->bf_state.bfs_addedbaw = 1;
+
+		/*
+		 * XXX enforce ACK for aggregate frames (this needs to be
+		 * XXX handled more gracefully?
+		 */
+		if (bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) {
+			device_printf(sc->sc_dev,
+			    "%s: HAL_TXDESC_NOACK set for an aggregate frame?\n",
+			    __func__);
+			bf->bf_state.bfs_txflags &= (~HAL_TXDESC_NOACK);
+		}
+
+		/*
+		 * Add the now owned buffer (which isn't
+		 * on the software TXQ any longer) to our
+		 * aggregate frame list.
+		 */
+		TAILQ_INSERT_TAIL(bf_q, bf, bf_list);
+		nframes ++;
+
+		/* Completion handler */
+		bf->bf_comp = ath_tx_aggr_comp;
+
+		/*
+		 * add padding for previous frame to aggregation length
+		 */
+		al += bpad + al_delta;
+
+		/*
+		 * Calculate delimiters needed for the current frame
+		 */
+		bf->bf_state.bfs_ndelim =
+		    ath_compute_num_delims(sc, bf_first,
+		    bf->bf_state.bfs_pktlen);
+
+		/*
+		 * Calculate the padding needed from this set of delimiters,
+		 * used when calculating if the next frame will fit in
+		 * the aggregate.
+		 */
+		bpad = PADBYTES(al_delta) + (bf->bf_state.bfs_ndelim << 2);
+
+		/*
+		 * Chain the buffers together
+		 */
+		if (bf_prev)
+			bf_prev->bf_next = bf;
+		bf_prev = bf;
+
+		/*
+		 * If we're leaking frames, just return at this point;
+		 * we've queued a single frame and we don't want to add
+		 * any more.
+		 */
+		if (tid->an->an_leak_count) {
+			status = ATH_AGGR_LEAK_CLOSED;
+			break;
+		}
+
+#if 0
+		/*
+		 * terminate aggregation on a small packet boundary
+		 */
+		if (bf->bf_state.bfs_pktlen < ATH_AGGR_MINPLEN) {
+			status = ATH_AGGR_SHORTPKT;
+			break;
+		}
+#endif
+
+	}
+
+finish:
+	/*
+	 * Just in case the list was empty when we tried to
+	 * dequeue a packet ..
+	 */
+	if (bf_first) {
+		bf_first->bf_state.bfs_al = al;
+		bf_first->bf_state.bfs_nframes = nframes;
+	}
+	return status;
+}

Modified: trunk/sys/dev/ath/if_ath_tx_ht.h
===================================================================
--- trunk/sys/dev/ath/if_ath_tx_ht.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_ath_tx_ht.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
  * All rights reserved.
@@ -26,14 +27,38 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_ath_tx_ht.h 250665 2013-05-15 18:33:05Z adrian $
  */
 #ifndef	__IF_ATH_TX_HT_H__
 #define	__IF_ATH_TX_HT_H__
 
+enum {
+	MCS_HT20,
+	MCS_HT20_SGI,
+	MCS_HT40,
+	MCS_HT40_SGI,
+};
+
+typedef enum {
+	ATH_AGGR_DONE,
+	ATH_AGGR_BAW_CLOSED,
+	ATH_AGGR_LIMITED,
+	ATH_AGGR_SHORTPKT,
+	ATH_AGGR_8K_LIMITED,
+	ATH_AGGR_ERROR,
+	ATH_AGGR_NONAGGR,
+	ATH_AGGR_LEAK_CLOSED,
+} ATH_AGGR_STATUS;
+
+extern int	ath_max_4ms_framelen[4][32];
+
+extern void ath_tx_rate_fill_rcflags(struct ath_softc *sc, struct ath_buf *bf);
+
 extern void	ath_buf_set_rate(struct ath_softc *sc,
-		struct ieee80211_node *ni, struct ath_buf *bf,
-		int pktlen, int flags, uint8_t ctsrate, int is_pspoll,
-		uint8_t *rix, uint8_t *try);
+		struct ieee80211_node *ni, struct ath_buf *bf);
 
+extern ATH_AGGR_STATUS
+	    ath_tx_form_aggr(struct ath_softc *sc, struct ath_node *an,
+	    struct ath_tid *tid, ath_bufhead *bf_q);
+
 #endif

Modified: trunk/sys/dev/ath/if_athdfs.h
===================================================================
--- trunk/sys/dev/ath/if_athdfs.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_athdfs.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_athdfs.h 244946 2013-01-02 01:36:10Z adrian $
  */
 #ifndef	__IF_ATHDFS_H__
 #define	__IF_ATHDFS_H__
@@ -33,9 +34,10 @@
 
 extern	int ath_dfs_attach(struct ath_softc *sc);
 extern	int ath_dfs_detach(struct ath_softc *sc);
-extern	void ath_dfs_radar_enable(struct ath_softc *,
+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	int ath_dfs_radar_disable(struct ath_softc *sc);
+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);

Modified: trunk/sys/dev/ath/if_athioctl.h
===================================================================
--- trunk/sys/dev/ath/if_athioctl.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_athioctl.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_athioctl.h 250665 2013-05-15 18:33:05Z adrian $
  */
 
 /*
@@ -35,6 +36,21 @@
 #ifndef _DEV_ATH_ATHIOCTL_H
 #define _DEV_ATH_ATHIOCTL_H
 
+struct ath_tx_aggr_stats {
+	u_int32_t	aggr_pkts[64];
+	u_int32_t	aggr_single_pkt;
+	u_int32_t	aggr_nonbaw_pkt;
+	u_int32_t	aggr_aggr_pkt;
+	u_int32_t	aggr_baw_closed_single_pkt;
+	u_int32_t	aggr_low_hwq_single_pkt;
+	u_int32_t	aggr_sched_nopkt;
+	u_int32_t	aggr_rts_aggr_limited;
+};
+
+struct ath_intr_stats {
+	u_int32_t	sync_intr[32];
+};
+
 struct ath_stats {
 	u_int32_t	ast_watchdog;	/* device reset by watchdog */
 	u_int32_t	ast_hardware;	/* fatal hardware error interrupts */
@@ -134,11 +150,29 @@
 	u_int32_t	ast_tx_xtxop;	/* tx exceeded TXOP */
 	u_int32_t	ast_tx_timerexpired;	/* tx exceeded TX_TIMER */
 	u_int32_t	ast_tx_desccfgerr;	/* tx desc cfg error */
-	u_int32_t	ast_pad[13];
+	u_int32_t	ast_tx_swretries;	/* software TX retries */
+	u_int32_t	ast_tx_swretrymax;	/* software TX retry max limit reach */
+	u_int32_t	ast_tx_data_underrun;
+	u_int32_t	ast_tx_delim_underrun;
+	u_int32_t	ast_tx_aggr_failall;	/* aggregate TX failed in its entirety */
+	u_int32_t	ast_tx_getnobuf;
+	u_int32_t	ast_tx_getbusybuf;
+	u_int32_t	ast_tx_intr;
+	u_int32_t	ast_rx_intr;
+	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_rx_keymiss;
+	u_int32_t	ast_tx_swfiltered;
+	u_int32_t	ast_tx_node_psq_overflow;
+	u_int32_t	ast_rx_stbc;		/* RX STBC frame */
+	u_int32_t	ast_tx_nodeq_overflow;	/* node sw queue overflow */
+	u_int32_t	ast_pad[12];
 };
 
 #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" */
@@ -156,10 +190,57 @@
 #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)
+#define	SIOCGATHRATESTATS	_IOWR('i', 150, 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)		| \
@@ -169,8 +250,95 @@
 	(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
+
+/*
+ * AR9380 and later chips are 3x3, which requires
+ * 5 EVM DWORDs in HT40 mode.
+ */
+#define	ATH_RADIOTAP_MAX_EVM		5
+
+/*
+ * 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_EVM];	/* 5 * 4 = 20 */
+
+	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_flags;	/* General flags */
+#define	ATH_VENDOR_PKT_RX	0x01
+#define	ATH_VENDOR_PKT_TX	0x02
+#define	ATH_VENDOR_PKT_RXPHYERR	0x04
+#define	ATH_VENDOR_PKT_ISAGGR	0x08
+#define	ATH_VENDOR_PKT_MOREAGGR	0x10
+
+	uint8_t		vh_rx_hwrate;	/* hardware RX ratecode */
+	uint8_t		vh_rs_flags;	/* RX HAL flags */
+	uint8_t		vh_pad[2];	/* pad to DWORD 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;
@@ -182,6 +350,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 (		\
@@ -236,4 +424,28 @@
 #define	DFS_PARAM_ENABLE	32
 #define	DFS_PARAM_EN_EXTCH	33
 
+/*
+ * Spectral ioctl parameter types
+ */
+#define	SPECTRAL_PARAM_FFT_PERIOD	1
+#define	SPECTRAL_PARAM_SS_PERIOD	2
+#define	SPECTRAL_PARAM_SS_COUNT		3
+#define	SPECTRAL_PARAM_SS_SHORT_RPT	4
+#define	SPECTRAL_PARAM_ENABLED		5
+#define	SPECTRAL_PARAM_ACTIVE		6
+
+/*
+ * Spectral control parameters
+ */
+#define	SIOCGATHSPECTRAL	_IOWR('i', 151, struct ath_diag)
+
+#define	SPECTRAL_CONTROL_ENABLE		2
+#define	SPECTRAL_CONTROL_DISABLE	3
+#define	SPECTRAL_CONTROL_START		4
+#define	SPECTRAL_CONTROL_STOP		5
+#define	SPECTRAL_CONTROL_GET_PARAMS	6
+#define	SPECTRAL_CONTROL_SET_PARAMS	7
+#define	SPECTRAL_CONTROL_ENABLE_AT_RESET	8
+#define	SPECTRAL_CONTROL_DISABLE_AT_RESET	9
+
 #endif /* _DEV_ATH_ATHIOCTL_H */

Modified: trunk/sys/dev/ath/if_athrate.h
===================================================================
--- trunk/sys/dev/ath/if_athrate.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_athrate.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
  * Copyright (c) 2004 Video54 Technologies, Inc.
@@ -27,7 +28,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_athrate.h 249578 2013-04-17 07:21:30Z adrian $
  */
 #ifndef _ATH_RATECTRL_H_
 #define _ATH_RATECTRL_H_
@@ -77,7 +78,25 @@
 struct ath_ratectrl *ath_rate_attach(struct ath_softc *);
 void	ath_rate_detach(struct ath_ratectrl *);
 
+#define	ATH_RC_NUM		4
 
+#define	ATH_RC_DS_FLAG		0x01	/* dual-stream rate */
+#define	ATH_RC_CW40_FLAG	0x02	/* use HT40 */
+#define	ATH_RC_SGI_FLAG		0x04	/* use short-GI */
+#define	ATH_RC_HT_FLAG		0x08	/* use HT */
+#define	ATH_RC_RTSCTS_FLAG	0x10	/* enable RTS/CTS protection */
+#define	ATH_RC_STBC_FLAG	0x20	/* enable STBC */
+#define	ATH_RC_TS_FLAG		0x40	/* triple-stream rate */
+
+struct ath_rc_series {
+	uint8_t rix;		/* ratetable index, not rate code */
+	uint8_t ratecode;	/* hardware rate code */
+	uint8_t tries;
+	uint8_t tx_power_cap;
+	uint16_t flags;
+	uint16_t max4msframelen;
+};
+
 /*
  * State storage handling.
  */
@@ -105,7 +124,7 @@
  * Return the four TX rate index and try counts for the current data packet.
  */
 void	ath_rate_getxtxrates(struct ath_softc *sc, struct ath_node *an,
-		uint8_t rix0, uint8_t *rix, uint8_t *try);
+		uint8_t rix0, struct ath_rc_series *rc);
 
 /*
  * Return the transmit info for a data packet.  If multi-rate state
@@ -127,8 +146,24 @@
  * supplied transmit descriptor.  The routine is invoked both
  * for packets that were successfully sent and for those that
  * failed (consult the descriptor for details).
+ *
+ * For A-MPDU frames, nframes and nbad indicate how many frames
+ * were in the aggregate, and how many failed.
  */
 struct ath_buf;
 void	ath_rate_tx_complete(struct ath_softc *, struct ath_node *,
-		const struct ath_buf *);
+		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_ */

Modified: trunk/sys/dev/ath/if_athvar.h
===================================================================
--- trunk/sys/dev/ath/if_athvar.h	2018-05-28 00:22:50 UTC (rev 10126)
+++ trunk/sys/dev/ath/if_athvar.h	2018-05-28 00:25:20 UTC (rev 10127)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
  * All rights reserved.
@@ -26,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/ath/if_athvar.h 251655 2013-06-12 14:52:57Z adrian $
  */
 
 /*
@@ -35,15 +36,28 @@
 #ifndef _DEV_ATH_ATHVAR_H
 #define _DEV_ATH_ATHVAR_H
 
+#include <machine/atomic.h>
+
 #include <dev/ath/ath_hal/ah.h>
 #include <dev/ath/ath_hal/ah_desc.h>
 #include <net80211/ieee80211_radiotap.h>
 #include <dev/ath/if_athioctl.h>
 #include <dev/ath/if_athrate.h>
+#ifdef	ATH_DEBUG_ALQ
+#include <dev/ath/if_ath_alq.h>
+#endif
 
 #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
@@ -64,7 +78,7 @@
 #define	ATH_TXMGTTRY	4		/* xmit attempts for mgt/ctl frames */
 #define	ATH_TXINTR_PERIOD 5		/* max number of batched tx descriptors */
 
-#define	ATH_BEACON_AIFS_DEFAULT	 0	/* default aifs for ap beacon q */
+#define	ATH_BEACON_AIFS_DEFAULT	 1	/* default aifs for ap beacon q */
 #define	ATH_BEACON_CWMIN_DEFAULT 0	/* default cwmin for ap beacon q */
 #define	ATH_BEACON_CWMAX_DEFAULT 0	/* default cwmax for ap beacon q */
 
@@ -83,12 +97,93 @@
 struct kthread;
 struct ath_buf;
 
+#define	ATH_TID_MAX_BUFS	(2 * IEEE80211_AGGR_BAWMAX)
+
+/*
+ * Per-TID state
+ *
+ * Note that TID 16 (WME_NUM_TID+1) is for handling non-QoS frames.
+ */
+struct ath_tid {
+	TAILQ_HEAD(,ath_buf)	tid_q;		/* pending buffers */
+	struct ath_node		*an;		/* pointer to parent */
+	int			tid;		/* tid */
+	int			ac;		/* which AC gets this trafic */
+	int			hwq_depth;	/* how many buffers are on HW */
+	u_int			axq_depth;	/* SW queue depth */
+
+	struct {
+		TAILQ_HEAD(,ath_buf)	tid_q;		/* filtered queue */
+		u_int			axq_depth;	/* SW queue depth */
+	} filtq;
+
+	/*
+	 * Entry on the ath_txq; when there's traffic
+	 * to send
+	 */
+	TAILQ_ENTRY(ath_tid)	axq_qelem;
+	int			sched;
+	int			paused;	/* >0 if the TID has been paused */
+
+	/*
+	 * These are flags - perhaps later collapse
+	 * down to a single uint32_t ?
+	 */
+	int			addba_tx_pending;	/* TX ADDBA pending */
+	int			bar_wait;	/* waiting for BAR */
+	int			bar_tx;		/* BAR TXed */
+	int			isfiltered;	/* is this node currently filtered */
+
+	/*
+	 * Is the TID being cleaned up after a transition
+	 * from aggregation to non-aggregation?
+	 * When this is set to 1, this TID will be paused
+	 * and no further traffic will be queued until all
+	 * the hardware packets pending for this TID have been
+	 * TXed/completed; at which point (non-aggregation)
+	 * traffic will resume being TXed.
+	 */
+	int			cleanup_inprogress;
+	/*
+	 * How many hardware-queued packets are
+	 * waiting to be cleaned up.
+	 * This is only valid if cleanup_inprogress is 1.
+	 */
+	int			incomp;
+
+	/*
+	 * The following implements a ring representing
+	 * the frames in the current BAW.
+	 * To avoid copying the array content each time
+	 * the BAW is moved, the baw_head/baw_tail point
+	 * to the current BAW begin/end; when the BAW is
+	 * shifted the head/tail of the array are also
+	 * appropriately shifted.
+	 */
+	/* active tx buffers, beginning at current BAW */
+	struct ath_buf		*tx_buf[ATH_TID_MAX_BUFS];
+	/* where the baw head is in the array */
+	int			baw_head;
+	/* where the BAW tail is in the array */
+	int			baw_tail;
+};
+
 /* driver-specific node state */
 struct ath_node {
 	struct ieee80211_node an_node;	/* base class */
 	u_int8_t	an_mgmtrix;	/* min h/w rate index */
 	u_int8_t	an_mcastrix;	/* mcast h/w rate index */
+	uint32_t	an_is_powersave;	/* node is sleeping */
+	uint32_t	an_stack_psq;		/* net80211 psq isn't empty */
+	uint32_t	an_tim_set;		/* TIM has been set */
 	struct ath_buf	*an_ff_buf[WME_NUM_AC]; /* ff staging area */
+	struct ath_tid	an_tid[IEEE80211_TID_SIZE];	/* per-TID state */
+	char		an_name[32];	/* eg "wlan0_a1" */
+	struct mtx	an_mtx;		/* protecting the rate control state */
+	uint32_t	an_swq_depth;	/* how many SWQ packets for this
+					   node */
+	int			clrdmask;	/* has clrdmask been set */
+	uint32_t	an_leak_count;	/* How many frames to leak during pause */
 	/* variable-length rate control state follows */
 };
 #define	ATH_NODE(ni)	((struct ath_node *)(ni))
@@ -108,11 +203,18 @@
 	((((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 {
-	STAILQ_ENTRY(ath_buf)	bf_list;
+	TAILQ_ENTRY(ath_buf)	bf_list;
+	struct ath_buf *	bf_next;	/* next buffer in the aggregate */
 	int			bf_nseg;
-	uint16_t		bf_txflags;	/* tx descriptor flags */
+	HAL_STATUS		bf_rxstatus;
 	uint16_t		bf_flags;	/* status flags (below) */
+	uint16_t		bf_descid;	/* 16 bit descriptor ID */
 	struct ath_desc		*bf_desc;	/* virtual addr of desc */
 	struct ath_desc_status	bf_status;	/* tx/rx status */
 	bus_addr_t		bf_daddr;	/* physical addr of desc */
@@ -119,14 +221,84 @@
 	bus_dmamap_t		bf_dmamap;	/* DMA map for mbuf chain */
 	struct mbuf		*bf_m;		/* mbuf for buf */
 	struct ieee80211_node	*bf_node;	/* pointer to the node */
+	struct ath_desc		*bf_lastds;	/* last descriptor for comp status */
+	struct ath_buf		*bf_last;	/* last buffer in aggregate, or self for non-aggregate */
 	bus_size_t		bf_mapsize;
 #define	ATH_MAX_SCATTER		ATH_TXDESC	/* max(tx,rx,beacon) desc's */
 	bus_dma_segment_t	bf_segs[ATH_MAX_SCATTER];
+	uint32_t		bf_nextfraglen;	/* length of next fragment */
+
+	/* Completion function to call on TX complete (fail or not) */
+	/*
+	 * "fail" here is set to 1 if the queue entries were removed
+	 * through a call to ath_tx_draintxq().
+	 */
+	void(* bf_comp) (struct ath_softc *sc, struct ath_buf *bf, int fail);
+
+	/* This state is kept to support software retries and aggregation */
+	struct {
+		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 */
+		uint8_t bfs_tx_queue;	/* destination hardware TX queue */
+
+		u_int32_t bfs_aggr:1,		/* part of aggregate? */
+		    bfs_aggrburst:1,	/* part of aggregate burst? */
+		    bfs_isretried:1,	/* retried frame? */
+		    bfs_dobaw:1,	/* actually check against BAW? */
+		    bfs_addedbaw:1,	/* has been added to the BAW */
+		    bfs_shpream:1,	/* use short preamble */
+		    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 */
+
+		/*
+		 * These fields are passed into the
+		 * descriptor setup functions.
+		 */
+
+		/* Make this an 8 bit value? */
+		HAL_PKT_TYPE bfs_atype;	/* packet type */
+
+		uint32_t bfs_pktlen;	/* length of this packet */
+
+		uint16_t bfs_hdrlen;	/* length of this packet header */
+		uint16_t bfs_al;	/* length of aggregate */
+
+		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 */
+		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;
+
+		/* 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 STAILQ_HEAD(, ath_buf) ath_bufhead;
+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 */
+#define	ATH_BUF_FIFOEND	0x00000004
+#define	ATH_BUF_FIFOPTR	0x00000008
 
+#define	ATH_BUF_FLAGS_CLONE	(ATH_BUF_MGMT)
+
 /*
  * DMA state for tx/rx descriptors.
  */
@@ -133,6 +305,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;
@@ -151,42 +324,137 @@
  * hardware queue).
  */
 struct ath_txq {
+	struct ath_softc	*axq_softc;	/* Needed for scheduling */
 	u_int			axq_qnum;	/* hardware q number */
 #define	ATH_TXQ_SWQ	(HAL_NUM_TX_QUEUES+1)	/* qnum for s/w only queue */
 	u_int			axq_ac;		/* WME AC */
 	u_int			axq_flags;
-#define	ATH_TXQ_PUTPENDING	0x0001		/* ath_hal_puttxbuf pending */
+//#define	ATH_TXQ_PUTPENDING	0x0001		/* ath_hal_puttxbuf pending */
+#define	ATH_TXQ_PUTRUNNING	0x0002		/* ath_hal_puttxbuf has been called */
 	u_int			axq_depth;	/* queue depth (stat only) */
+	u_int			axq_aggr_depth;	/* how many aggregates are queued */
 	u_int			axq_intrcnt;	/* interrupt count */
 	u_int32_t		*axq_link;	/* link ptr in last TX desc */
-	STAILQ_HEAD(, ath_buf)	axq_q;		/* transmit queue */
+	TAILQ_HEAD(axq_q_s, ath_buf)	axq_q;		/* transmit queue */
 	struct mtx		axq_lock;	/* lock on q and link */
+
+	/*
+	 * This is the FIFO staging buffer when doing EDMA.
+	 *
+	 * For legacy chips, we just push the head pointer to
+	 * the hardware and we ignore this list.
+	 *
+	 * For EDMA, the staging buffer is treated as normal;
+	 * when it's time to push a list of frames to the hardware
+	 * we move that list here and we stamp buffers with
+	 * flags to identify the beginning/end of that particular
+	 * FIFO entry.
+	 */
+	struct {
+		TAILQ_HEAD(axq_q_f_s, ath_buf)	axq_q;
+		u_int				axq_depth;
+	} fifo;
+	u_int			axq_fifo_depth;	/* depth of FIFO frames */
+
+	/*
+	 * XXX the holdingbf field is protected by the TXBUF lock
+	 * for now, NOT the TXQ lock.
+	 *
+	 * Architecturally, it would likely be better to move
+	 * the holdingbf field to a separate array in ath_softc
+	 * just to highlight that it's not protected by the normal
+	 * TX path lock.
+	 */
+	struct ath_buf		*axq_holdingbf;	/* holding TX buffer */
 	char			axq_name[12];	/* e.g. "ath0_txq4" */
+
+	/* Per-TID traffic queue for software -> hardware TX */
+	/*
+	 * This is protected by the general TX path lock, not (for now)
+	 * by the TXQ lock.
+	 */
+	TAILQ_HEAD(axq_t_s,ath_tid)	axq_tidq;
 };
 
 #define	ATH_TXQ_LOCK_INIT(_sc, _tq) do { \
-	snprintf((_tq)->axq_name, sizeof((_tq)->axq_name), "%s_txq%u", \
-		device_get_nameunit((_sc)->sc_dev), (_tq)->axq_qnum); \
-	mtx_init(&(_tq)->axq_lock, (_tq)->axq_name, NULL, MTX_DEF); \
-} while (0)
+	    snprintf((_tq)->axq_name, sizeof((_tq)->axq_name), "%s_txq%u", \
+	      device_get_nameunit((_sc)->sc_dev), (_tq)->axq_qnum); \
+	    mtx_init(&(_tq)->axq_lock, (_tq)->axq_name, NULL, MTX_DEF); \
+	} while (0)
 #define	ATH_TXQ_LOCK_DESTROY(_tq)	mtx_destroy(&(_tq)->axq_lock)
 #define	ATH_TXQ_LOCK(_tq)		mtx_lock(&(_tq)->axq_lock)
 #define	ATH_TXQ_UNLOCK(_tq)		mtx_unlock(&(_tq)->axq_lock)
 #define	ATH_TXQ_LOCK_ASSERT(_tq)	mtx_assert(&(_tq)->axq_lock, MA_OWNED)
+#define	ATH_TXQ_UNLOCK_ASSERT(_tq)	mtx_assert(&(_tq)->axq_lock,	\
+					    MA_NOTOWNED)
 
+
+#define	ATH_NODE_LOCK(_an)		mtx_lock(&(_an)->an_mtx)
+#define	ATH_NODE_UNLOCK(_an)		mtx_unlock(&(_an)->an_mtx)
+#define	ATH_NODE_LOCK_ASSERT(_an)	mtx_assert(&(_an)->an_mtx, MA_OWNED)
+#define	ATH_NODE_UNLOCK_ASSERT(_an)	mtx_assert(&(_an)->an_mtx,	\
+					    MA_NOTOWNED)
+
+/*
+ * These are for the hardware queue.
+ */
+#define ATH_TXQ_INSERT_HEAD(_tq, _elm, _field) do { \
+	TAILQ_INSERT_HEAD(&(_tq)->axq_q, (_elm), _field); \
+	(_tq)->axq_depth++; \
+} while (0)
 #define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \
-	STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \
+	TAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \
 	(_tq)->axq_depth++; \
 } while (0)
-#define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \
-	STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \
+#define ATH_TXQ_REMOVE(_tq, _elm, _field) do { \
+	TAILQ_REMOVE(&(_tq)->axq_q, _elm, _field); \
 	(_tq)->axq_depth--; \
 } while (0)
-/* NB: this does not do the "head empty check" that STAILQ_LAST does */
-#define	ATH_TXQ_LAST(_tq) \
-	((struct ath_buf *)(void *) \
-	 ((char *)((_tq)->axq_q.stqh_last) - __offsetof(struct ath_buf, bf_list)))
+#define	ATH_TXQ_FIRST(_tq)		TAILQ_FIRST(&(_tq)->axq_q)
+#define	ATH_TXQ_LAST(_tq, _field)	TAILQ_LAST(&(_tq)->axq_q, _field)
 
+/*
+ * These are for the TID software queue.
+ */
+#define ATH_TID_INSERT_HEAD(_tq, _elm, _field) do { \
+	TAILQ_INSERT_HEAD(&(_tq)->tid_q, (_elm), _field); \
+	(_tq)->axq_depth++; \
+	(_tq)->an->an_swq_depth++; \
+} while (0)
+#define ATH_TID_INSERT_TAIL(_tq, _elm, _field) do { \
+	TAILQ_INSERT_TAIL(&(_tq)->tid_q, (_elm), _field); \
+	(_tq)->axq_depth++; \
+	(_tq)->an->an_swq_depth++; \
+} while (0)
+#define ATH_TID_REMOVE(_tq, _elm, _field) do { \
+	TAILQ_REMOVE(&(_tq)->tid_q, _elm, _field); \
+	(_tq)->axq_depth--; \
+	(_tq)->an->an_swq_depth--; \
+} while (0)
+#define	ATH_TID_FIRST(_tq)		TAILQ_FIRST(&(_tq)->tid_q)
+#define	ATH_TID_LAST(_tq, _field)	TAILQ_LAST(&(_tq)->tid_q, _field)
+
+/*
+ * These are for the TID filtered frame queue
+ */
+#define ATH_TID_FILT_INSERT_HEAD(_tq, _elm, _field) do { \
+	TAILQ_INSERT_HEAD(&(_tq)->filtq.tid_q, (_elm), _field); \
+	(_tq)->axq_depth++; \
+	(_tq)->an->an_swq_depth++; \
+} while (0)
+#define ATH_TID_FILT_INSERT_TAIL(_tq, _elm, _field) do { \
+	TAILQ_INSERT_TAIL(&(_tq)->filtq.tid_q, (_elm), _field); \
+	(_tq)->axq_depth++; \
+	(_tq)->an->an_swq_depth++; \
+} while (0)
+#define ATH_TID_FILT_REMOVE(_tq, _elm, _field) do { \
+	TAILQ_REMOVE(&(_tq)->filtq.tid_q, _elm, _field); \
+	(_tq)->axq_depth--; \
+	(_tq)->an->an_swq_depth--; \
+} while (0)
+#define	ATH_TID_FILT_FIRST(_tq)		TAILQ_FIRST(&(_tq)->filtq.tid_q)
+#define	ATH_TID_FILT_LAST(_tq, _field)	TAILQ_LAST(&(_tq)->filtq.tid_q,_field)
+
 struct ath_vap {
 	struct ieee80211vap av_vap;	/* base class */
 	int		av_bslot;	/* beacon slot index */
@@ -199,6 +467,10 @@
 	int		(*av_newstate)(struct ieee80211vap *,
 				enum ieee80211_state, int);
 	void		(*av_bmiss)(struct ieee80211vap *);
+	void		(*av_node_ps)(struct ieee80211_node *, int);
+	int		(*av_set_tim)(struct ieee80211_node *, int);
+	void		(*av_recv_pspoll)(struct ieee80211_node *,
+				struct mbuf *);
 };
 #define	ATH_VAP(vap)	((struct ath_vap *)(vap))
 
@@ -205,10 +477,70 @@
 struct taskqueue;
 struct ath_tx99;
 
+/*
+ * Whether to reset the TX/RX queue with or without
+ * a queue flush.
+ */
+typedef enum {
+	ATH_RESET_DEFAULT = 0,
+	ATH_RESET_NOLOSS = 1,
+	ATH_RESET_FULL = 2,
+} ATH_RESET_TYPE;
+
+struct ath_rx_methods {
+	void		(*recv_sched_queue)(struct ath_softc *sc,
+			    HAL_RX_QUEUE q, int dosched);
+	void		(*recv_sched)(struct ath_softc *sc, int dosched);
+	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_edma_fifo {
+	struct ath_buf	**m_fifo;
+	int		m_fifolen;
+	int		m_fifo_head;
+	int		m_fifo_tail;
+	int		m_fifo_depth;
+};
+
+struct ath_tx_methods {
+	int		(*xmit_setup)(struct ath_softc *sc);
+	int		(*xmit_teardown)(struct ath_softc *sc);
+	void		(*xmit_attach_comp_func)(struct ath_softc *sc);
+
+	void		(*xmit_dma_restart)(struct ath_softc *sc,
+			    struct ath_txq *txq);
+	void		(*xmit_handoff)(struct ath_softc *sc,
+			    struct ath_txq *txq, struct ath_buf *bf);
+	void		(*xmit_drain)(struct ath_softc *sc,
+			    ATH_RESET_TYPE reset_type);
+};
+
 struct ath_softc {
 	struct ifnet		*sc_ifp;	/* interface common */
 	struct ath_stats	sc_stats;	/* interface statistics */
-	int			sc_debug;
+	struct ath_tx_aggr_stats	sc_aggr_stats;
+	struct ath_intr_stats	sc_intr_stats;
+	uint64_t		sc_debug;
+	uint64_t		sc_ktrdebug;
 	int			sc_nvaps;	/* # vaps */
 	int			sc_nstavaps;	/* # station vaps */
 	int			sc_nmeshvaps;	/* # mbss vaps */
@@ -216,6 +548,26 @@
 	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 */
+	ath_bufhead		sc_rx_rxlist[HAL_NUM_RX_QUEUES];	/* deferred RX completion */
+	struct ath_tx_methods	sc_tx;
+	struct ath_tx_edma_fifo	sc_txedma[HAL_NUM_TX_QUEUES];
+
+	/*
+	 * This is (currently) protected by the TX queue lock;
+	 * it should migrate to a separate lock later
+	 * so as to minimise contention.
+	 */
+	ath_bufhead		sc_txbuf_list;
+
+	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;
 	HAL_BUS_TAG		sc_st;		/* bus space tag */
@@ -222,14 +574,28 @@
 	HAL_BUS_HANDLE		sc_sh;		/* bus space handle */
 	bus_dma_tag_t		sc_dmat;	/* bus DMA tag */
 	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 mtx		sc_tx_mtx;	/* TX handling/comp mutex */
+	char			sc_tx_mtx_name[32];
+	struct mtx		sc_tx_ic_mtx;	/* TX queue mutex */
+	char			sc_tx_ic_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 */
 	struct ath_tx99		*sc_tx99;	/* tx99 adjunct state */
 	void			(*sc_setdefantenna)(struct ath_softc *, u_int);
-	unsigned int		sc_invalid  : 1,/* disable hardware accesses */
+
+	/*
+	 * First set of flags.
+	 */
+	uint32_t		sc_invalid  : 1,/* disable hardware accesses */
 				sc_mrretry  : 1,/* multi-rate retry support */
+				sc_mrrprot  : 1,/* MRR + protection support */
 				sc_softled  : 1,/* enable LED gpio status */
+				sc_hardled  : 1,/* enable MAC LED status */
 				sc_splitmic : 1,/* split TKIP MIC keys */
 				sc_needmib  : 1,/* enable MIB stats intr */
 				sc_diversity: 1,/* enable rx diversity */
@@ -255,8 +621,26 @@
 				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_kickpcu  : 1,/* kick PCU RX on next RX proc */
-				sc_rxtsf32  : 1;/* RX dec TSF is 32 bits */
+				sc_rxtsf32  : 1,/* RX dec TSF is 32 bits */
+				sc_isedma   : 1;/* supports EDMA */
+
+	/*
+	 * Second set of flags.
+	 */
+	u_int32_t		sc_use_ent  : 1,
+				sc_rx_stbc  : 1,
+				sc_tx_stbc  : 1,
+				sc_hasenforcetxop : 1, /* support enforce TxOP */
+				sc_hasdivcomb : 1,     /* RX diversity combining */
+				sc_rx_lnamixer : 1;    /* RX using LNA mixing */
+
+	int			sc_cabq_enable;	/* Enable cabq transmission */
+
+	/*
+	 * Enterprise mode configuration for AR9380 and later chipsets.
+	 */
+	uint32_t		sc_ent_cfg;
+
 	uint32_t		sc_eerd;	/* regdomain from EEPROM */
 	uint32_t		sc_eecc;	/* country code from EEPROM */
 						/* rate tables */
@@ -282,10 +666,32 @@
 	u_int			sc_fftxqmin;	/* min frames before staging */
 	u_int			sc_fftxqmax;	/* max frames before drop */
 	u_int			sc_txantenna;	/* tx antenna (fixed or auto) */
+
 	HAL_INT			sc_imask;	/* interrupt mask copy */
+
+	/*
+	 * These are modified in the interrupt handler as well as
+	 * the task queues and other contexts. Thus these must be
+	 * protected by a mutex, or they could clash.
+	 *
+	 * For now, access to these is behind the ATH_LOCK,
+	 * just to save time.
+	 */
+	uint32_t		sc_txq_active;	/* bitmap of active TXQs */
+	uint32_t		sc_kickpcu;	/* whether to kick the PCU */
+	uint32_t		sc_rxproc_cnt;	/* In RX processing */
+	uint32_t		sc_txproc_cnt;	/* In TX processing */
+	uint32_t		sc_txstart_cnt;	/* In TX output (raw/start) */
+	uint32_t		sc_inreset_cnt;	/* In active reset/chanchange */
+	uint32_t		sc_txrx_cnt;	/* refcount on stop/start'ing TX */
+	uint32_t		sc_intr_cnt;	/* refcount on interrupt handling */
+
 	u_int			sc_keymax;	/* size of key cache */
 	u_int8_t		sc_keymap[ATH_KEYBYTES];/* key use bit map */
 
+	/*
+	 * Software based LED blinking
+	 */
 	u_int			sc_ledpin;	/* GPIO pin for driving LED */
 	u_int			sc_ledon;	/* pin setting for LED on */
 	u_int			sc_ledidle;	/* idle polling interval */
@@ -294,12 +700,17 @@
 	u_int16_t		sc_ledoff;	/* off time for current blink */
 	struct callout		sc_ledtimer;	/* led off timer */
 
+	/*
+	 * Hardware based LED blinking
+	 */
+	int			sc_led_pwr_pin;	/* MAC power LED GPIO pin */
+	int			sc_led_net_pin;	/* MAC network LED GPIO pin */
+
 	u_int			sc_rfsilentpin;	/* GPIO pin for rfkill int */
 	u_int			sc_rfsilentpol;	/* pin setting for rfkill on */
 
 	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 */
@@ -311,7 +722,12 @@
 	u_int			sc_monpass;	/* frames to pass in mon.mode */
 
 	struct ath_descdma	sc_txdma;	/* TX descriptors */
+	uint16_t		sc_txbuf_descid;
 	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 ath_descdma	sc_txsdma;	/* EDMA TX status desc's */
 	struct mtx		sc_txbuflock;	/* txbuf lock */
 	char			sc_txname[12];	/* e.g. "ath0_buf" */
 	u_int			sc_txqsetup;	/* h/w queues setup */
@@ -319,6 +735,12 @@
 	struct ath_txq		sc_txq[HAL_NUM_TX_QUEUES];
 	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;
@@ -332,6 +754,8 @@
 	struct ath_txq		*sc_cabq;	/* tx q for cab frames */
 	struct task		sc_bmisstask;	/* bmiss int processing */
 	struct task		sc_bstucktask;	/* stuck beacon processing */
+	struct task		sc_resettask;	/* interface reset task */
+	struct task		sc_fataltask;	/* fatal task */
 	enum {
 		OK,				/* no change needed */
 		UPDATE,				/* update pending */
@@ -357,13 +781,90 @@
 	u_int32_t		sc_avgtsfdeltap;/* TDMA slot adjust (+) */
 	u_int32_t		sc_avgtsfdeltam;/* TDMA slot adjust (-) */
 	uint16_t		*sc_eepromdata;	/* Local eeprom data, if AR9100 */
-	int			sc_txchainmask;	/* currently configured TX chainmask */
-	int			sc_rxchainmask;	/* currently configured RX chainmask */
+	uint32_t		sc_txchainmask;	/* hardware TX chainmask */
+	uint32_t		sc_rxchainmask;	/* hardware RX chainmask */
+	uint32_t		sc_cur_txchainmask;	/* currently configured TX chainmask */
+	uint32_t		sc_cur_rxchainmask;	/* currently configured RX chainmask */
+	uint32_t		sc_rts_aggr_limit;	/* TX limit on RTS aggregates */
+	int			sc_aggr_limit;	/* TX limit on all aggregates */
+	int			sc_delim_min_pad;	/* Minimum delimiter count */
 
+	/* Queue limits */
+
+	/*
+	 * To avoid queue starvation in congested conditions,
+	 * these parameters tune the maximum number of frames
+	 * queued to the data/mcastq before they're dropped.
+	 *
+	 * This is to prevent:
+	 * + a single destination overwhelming everything, including
+	 *   management/multicast frames;
+	 * + multicast frames overwhelming everything (when the
+	 *   air is sufficiently busy that cabq can't drain.)
+	 * + A node in powersave shouldn't be allowed to exhaust
+	 *   all available mbufs;
+	 *
+	 * These implement:
+	 * + data_minfree is the maximum number of free buffers
+	 *   overall to successfully allow a data frame.
+	 *
+	 * + mcastq_maxdepth is the maximum depth allowed of the cabq.
+	 */
+	int			sc_txq_node_maxdepth;
+	int			sc_txq_data_minfree;
+	int			sc_txq_mcastq_maxdepth;
+	int			sc_txq_node_psq_maxdepth;
+
+	/*
+	 * Software queue twiddles
+	 *
+	 * hwq_limit_nonaggr:
+	 *		when to begin limiting non-aggregate frames to the
+	 *		hardware queue, regardless of the TID.
+	 * hwq_limit_aggr:
+	 *		when to begin limiting A-MPDU frames to the
+	 *		hardware queue, regardless of the TID.
+	 * tid_hwq_lo:	how low the per-TID hwq count has to be before the
+	 *		TID will be scheduled again
+	 * tid_hwq_hi:	how many frames to queue to the HWQ before the TID
+	 *		stops being scheduled.
+	 */
+	int			sc_hwq_limit_nonaggr;
+	int			sc_hwq_limit_aggr;
+	int			sc_tid_hwq_lo;
+	int			sc_tid_hwq_hi;
+
 	/* DFS related state */
 	void			*sc_dfs;	/* Used by an optional DFS module */
 	int			sc_dodfs;	/* Whether to enable DFS rx filter bits */
 	struct task		sc_dfstask;	/* DFS processing task */
+
+	/* Spectral related state */
+	void			*sc_spectral;
+	int			sc_dospectral;
+
+	/* LNA diversity related state */
+	void			*sc_lna_div;
+	int			sc_dolnadiv;
+
+	/* ALQ */
+#ifdef	ATH_DEBUG_ALQ
+	struct if_ath_alq sc_alq;
+#endif
+
+	/* TX AMPDU handling */
+	int			(*sc_addba_request)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *, int, int, int);
+	int			(*sc_addba_response)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *, int, int, int);
+	void			(*sc_addba_stop)(struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *);
+	void			(*sc_addba_response_timeout)
+				    (struct ieee80211_node *,
+				    struct ieee80211_tx_ampdu *);
+	void			(*sc_bar_response)(struct ieee80211_node *ni,
+				    struct ieee80211_tx_ampdu *tap,
+				    int status);
 };
 
 #define	ATH_LOCK_INIT(_sc) \
@@ -373,7 +874,105 @@
 #define	ATH_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
 #define	ATH_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
 #define	ATH_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+#define	ATH_UNLOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
 
+/*
+ * The TX lock is non-reentrant and serialises the TX frame send
+ * and completion operations.
+ */
+#define	ATH_TX_LOCK_INIT(_sc) do {\
+	snprintf((_sc)->sc_tx_mtx_name,				\
+	    sizeof((_sc)->sc_tx_mtx_name),				\
+	    "%s TX lock",						\
+	    device_get_nameunit((_sc)->sc_dev));			\
+	mtx_init(&(_sc)->sc_tx_mtx, (_sc)->sc_tx_mtx_name,		\
+		 NULL, MTX_DEF);					\
+	} while (0)
+#define	ATH_TX_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_tx_mtx)
+#define	ATH_TX_LOCK(_sc)		mtx_lock(&(_sc)->sc_tx_mtx)
+#define	ATH_TX_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_tx_mtx)
+#define	ATH_TX_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_tx_mtx,	\
+		MA_OWNED)
+#define	ATH_TX_UNLOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_tx_mtx,	\
+		MA_NOTOWNED)
+#define	ATH_TX_TRYLOCK(_sc)	(mtx_owned(&(_sc)->sc_tx_mtx) != 0 &&	\
+					mtx_trylock(&(_sc)->sc_tx_mtx))
+
+/*
+ * The IC TX lock is non-reentrant and serialises packet queuing from
+ * the upper layers.
+ */
+#define	ATH_TX_IC_LOCK_INIT(_sc) do {\
+	snprintf((_sc)->sc_tx_ic_mtx_name,				\
+	    sizeof((_sc)->sc_tx_ic_mtx_name),				\
+	    "%s IC TX lock",						\
+	    device_get_nameunit((_sc)->sc_dev));			\
+	mtx_init(&(_sc)->sc_tx_ic_mtx, (_sc)->sc_tx_ic_mtx_name,	\
+		 NULL, MTX_DEF);					\
+	} while (0)
+#define	ATH_TX_IC_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_tx_ic_mtx)
+#define	ATH_TX_IC_LOCK(_sc)		mtx_lock(&(_sc)->sc_tx_ic_mtx)
+#define	ATH_TX_IC_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_tx_ic_mtx)
+#define	ATH_TX_IC_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_tx_ic_mtx,	\
+		MA_OWNED)
+#define	ATH_TX_IC_UNLOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_tx_ic_mtx,	\
+		MA_NOTOWNED)
+
+/*
+ * The PCU lock is non-recursive and should be treated as a spinlock.
+ * Although currently the interrupt code is run in netisr context and
+ * doesn't require this, this may change in the future.
+ * Please keep this in mind when protecting certain code paths
+ * with the PCU lock.
+ *
+ * The PCU lock is used to serialise access to the PCU so things such
+ * as TX, RX, state change (eg channel change), channel reset and updates
+ * from interrupt context (eg kickpcu, txqactive bits) do not clash.
+ *
+ * Although the current single-thread taskqueue mechanism protects the
+ * majority of these situations by simply serialising them, there are
+ * a few others which occur at the same time. These include the TX path
+ * (which only acquires ATH_LOCK when recycling buffers to the free list),
+ * ath_set_channel, the channel scanning API and perhaps quite a bit more.
+ */
+#define	ATH_PCU_LOCK_INIT(_sc) do {\
+	snprintf((_sc)->sc_pcu_mtx_name,				\
+	    sizeof((_sc)->sc_pcu_mtx_name),				\
+	    "%s PCU lock",						\
+	    device_get_nameunit((_sc)->sc_dev));			\
+	mtx_init(&(_sc)->sc_pcu_mtx, (_sc)->sc_pcu_mtx_name,		\
+		 NULL, MTX_DEF);					\
+	} while (0)
+#define	ATH_PCU_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_pcu_mtx)
+#define	ATH_PCU_LOCK(_sc)		mtx_lock(&(_sc)->sc_pcu_mtx)
+#define	ATH_PCU_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_pcu_mtx)
+#define	ATH_PCU_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->sc_pcu_mtx,	\
+		MA_OWNED)
+#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 { \
@@ -386,7 +985,22 @@
 #define	ATH_TXBUF_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_txbuflock)
 #define	ATH_TXBUF_LOCK_ASSERT(_sc) \
 	mtx_assert(&(_sc)->sc_txbuflock, MA_OWNED)
+#define	ATH_TXBUF_UNLOCK_ASSERT(_sc) \
+	mtx_assert(&(_sc)->sc_txbuflock, MA_NOTOWNED)
 
+#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 *);
@@ -443,8 +1057,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) \
@@ -451,6 +1065,8 @@
 	OS_REG_READ(_ah, AR_TSF_L32)
 #define	ath_hal_gettsf64(_ah) \
 	((*(_ah)->ah_getTsf64)((_ah)))
+#define	ath_hal_settsf64(_ah, _val) \
+	((*(_ah)->ah_setTsf64)((_ah), (_val)))
 #define	ath_hal_resettsf(_ah) \
 	((*(_ah)->ah_resetTsf)((_ah)))
 #define	ath_hal_rxena(_ah) \
@@ -461,8 +1077,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) \
@@ -643,26 +1259,74 @@
 #define	ath_hal_settpcts(_ah, _tpcts) \
 	ath_hal_setcapability(_ah, HAL_CAP_TPC_CTS, 0, _tpcts, NULL)
 #define	ath_hal_hasintmit(_ah) \
-	(ath_hal_getcapability(_ah, HAL_CAP_INTMIT, HAL_CAP_INTMIT_PRESENT, NULL) == HAL_OK)
+	(ath_hal_getcapability(_ah, HAL_CAP_INTMIT, \
+	HAL_CAP_INTMIT_PRESENT, NULL) == HAL_OK)
 #define	ath_hal_getintmit(_ah) \
-	(ath_hal_getcapability(_ah, HAL_CAP_INTMIT, HAL_CAP_INTMIT_ENABLE, NULL) == HAL_OK)
+	(ath_hal_getcapability(_ah, HAL_CAP_INTMIT, \
+	HAL_CAP_INTMIT_ENABLE, NULL) == HAL_OK)
 #define	ath_hal_setintmit(_ah, _v) \
-	ath_hal_setcapability(_ah, HAL_CAP_INTMIT, HAL_CAP_INTMIT_ENABLE, _v, NULL)
+	ath_hal_setcapability(_ah, HAL_CAP_INTMIT, \
+	HAL_CAP_INTMIT_ENABLE, _v, NULL)
+
+#define	ath_hal_hasenforcetxop(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_ENFORCE_TXOP, 0, NULL) == HAL_OK)
+#define	ath_hal_getenforcetxop(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_ENFORCE_TXOP, 1, NULL) == HAL_OK)
+#define	ath_hal_setenforcetxop(_ah, _v) \
+	ath_hal_setcapability(_ah, HAL_CAP_ENFORCE_TXOP, 1, _v, NULL)
+
+#define	ath_hal_hasrxlnamixer(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_RX_LNA_MIXING, 0, NULL) == HAL_OK)
+
+#define	ath_hal_hasdivantcomb(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_ANT_DIV_COMB, 0, NULL) == HAL_OK)
+
+/* 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) \
 	(ath_hal_getcapability(_ah, HAL_CAP_TX_CHAINMASK, 0, _ptxchainmask))
+#define	ath_hal_setrxchainmask(_ah, _rx) \
+	(ath_hal_setcapability(_ah, HAL_CAP_RX_CHAINMASK, 1, _rx, NULL))
+#define	ath_hal_settxchainmask(_ah, _tx) \
+	(ath_hal_setcapability(_ah, HAL_CAP_TX_CHAINMASK, 1, _tx, NULL))
 #define	ath_hal_split4ktrans(_ah) \
-	(ath_hal_getcapability(_ah, HAL_CAP_SPLIT_4KB_TRANS, 0, NULL) == HAL_OK)
+	(ath_hal_getcapability(_ah, HAL_CAP_SPLIT_4KB_TRANS, \
+	0, NULL) == HAL_OK)
 #define	ath_hal_self_linked_final_rxdesc(_ah) \
-	(ath_hal_getcapability(_ah, HAL_CAP_RXDESC_SELFLINK, 0, NULL) == HAL_OK)
+	(ath_hal_getcapability(_ah, HAL_CAP_RXDESC_SELFLINK, \
+	0, NULL) == HAL_OK)
 #define	ath_hal_gtxto_supported(_ah) \
 	(ath_hal_getcapability(_ah, HAL_CAP_GTXTO, 0, NULL) == HAL_OK)
 #define	ath_hal_has_long_rxdesc_tsf(_ah) \
-	(ath_hal_getcapability(_ah, HAL_CAP_LONG_RXDESC_TSF, 0, NULL) == HAL_OK)
-
+	(ath_hal_getcapability(_ah, HAL_CAP_LONG_RXDESC_TSF, \
+	0, NULL) == HAL_OK)
 #define	ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \
 	((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq)))
 #define	ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext, _rs) \
@@ -677,8 +1341,9 @@
 		_txr1, _txtr1, _txr2, _txtr2, _txr3, _txtr3) \
 	((*(_ah)->ah_setupXTxDesc)((_ah), (_ds), \
 		(_txr1), (_txtr1), (_txr2), (_txtr2), (_txr3), (_txtr3)))
-#define	ath_hal_filltxdesc(_ah, _ds, _l, _first, _last, _ds0) \
-	((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_l), (_first), (_last), (_ds0)))
+#define	ath_hal_filltxdesc(_ah, _ds, _b, _l, _did, _qid, _first, _last, _ds0) \
+	((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_b), (_l), (_did), (_qid), \
+		(_first), (_last), (_ds0)))
 #define	ath_hal_txprocdesc(_ah, _ds, _ts) \
 	((*(_ah)->ah_procTxDesc)((_ah), (_ds), (_ts)))
 #define	ath_hal_gettxintrtxqs(_ah, _txqs) \
@@ -685,27 +1350,66 @@
 	((*(_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_gettxrawtxdesc(_ah, _txstatus) \
+	((*(_ah)->ah_getTxRawTxDesc)((_ah), (_txstatus)))
 
-#define	ath_hal_chaintxdesc(_ah, _ds, _pktlen, _hdrlen, _type, _keyix, \
-	_cipher, _delims, _seglen, _first, _last) \
-	((*(_ah)->ah_chainTxDesc((_ah), (_ds), (_pktlen), (_hdrlen), \
-	(_type), (_keyix), (_cipher), (_delims), (_seglen), \
-	(_first), (_last)))) 
 #define	ath_hal_setupfirsttxdesc(_ah, _ds, _aggrlen, _flags, _txpower, \
 		_txr0, _txtr0, _antm, _rcr, _rcd) \
 	((*(_ah)->ah_setupFirstTxDesc)((_ah), (_ds), (_aggrlen), (_flags), \
 	(_txpower), (_txr0), (_txtr0), (_antm), (_rcr), (_rcd)))
+#define	ath_hal_chaintxdesc(_ah, _ds, _bl, _sl, _pktlen, _hdrlen, _type, \
+	_keyix, _cipher, _delims, _first, _last, _lastaggr) \
+	((*(_ah)->ah_chainTxDesc)((_ah), (_ds), (_bl), (_sl), \
+	(_pktlen), (_hdrlen), (_type), (_keyix), (_cipher), (_delims), \
+	(_first), (_last), (_lastaggr)))
 #define	ath_hal_setuplasttxdesc(_ah, _ds, _ds0) \
 	((*(_ah)->ah_setupLastTxDesc)((_ah), (_ds), (_ds0)))
+
 #define	ath_hal_set11nratescenario(_ah, _ds, _dur, _rt, _series, _ns, _flags) \
 	((*(_ah)->ah_set11nRateScenario)((_ah), (_ds), (_dur), (_rt), \
 	(_series), (_ns), (_flags)))
-#define	ath_hal_set11naggrmiddle(_ah, _ds, _num) \
-	((*(_ah)->ah_set11nAggrMiddle((_ah), (_ds), (_num))))
+
+#define	ath_hal_set11n_aggr_first(_ah, _ds, _len, _num) \
+	((*(_ah)->ah_set11nAggrFirst)((_ah), (_ds), (_len), (_num)))
+#define	ath_hal_set11n_aggr_middle(_ah, _ds, _num) \
+	((*(_ah)->ah_set11nAggrMiddle)((_ah), (_ds), (_num)))
+#define	ath_hal_set11n_aggr_last(_ah, _ds) \
+	((*(_ah)->ah_set11nAggrLast)((_ah), (_ds)))
+
 #define	ath_hal_set11nburstduration(_ah, _ds, _dur) \
 	((*(_ah)->ah_set11nBurstDuration)((_ah), (_ds), (_dur)))
+#define	ath_hal_clr11n_aggr(_ah, _ds) \
+	((*(_ah)->ah_clr11nAggr)((_ah), (_ds)))
+#define	ath_hal_set11n_virtmorefrag(_ah, _ds, _v) \
+	((*(_ah)->ah_set11nVirtMoreFrag)((_ah), (_ds), (_v)))
 
+#define	ath_hal_gpioCfgOutput(_ah, _gpio, _type) \
+	((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio), (_type)))
+#define	ath_hal_gpioset(_ah, _gpio, _b) \
+	((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b)))
+#define	ath_hal_gpioget(_ah, _gpio) \
+	((*(_ah)->ah_gpioGet)((_ah), (_gpio)))
+#define	ath_hal_gpiosetintr(_ah, _gpio, _b) \
+	((*(_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
@@ -715,21 +1419,57 @@
 	((*(_ah)->ah_enableDfs)((_ah), (_param)))
 #define	ath_hal_getdfsthresh(_ah, _param) \
 	((*(_ah)->ah_getDfsThresh)((_ah), (_param)))
+#define	ath_hal_getdfsdefaultthresh(_ah, _param) \
+	((*(_ah)->ah_getDfsDefaultThresh)((_ah), (_param)))
 #define	ath_hal_procradarevent(_ah, _rxs, _fulltsf, _buf, _event) \
-	((*(_ah)->ah_procRadarEvent)((_ah), (_rxs), (_fulltsf), (_buf), (_event)))
+	((*(_ah)->ah_procRadarEvent)((_ah), (_rxs), (_fulltsf), \
+	(_buf), (_event)))
 #define	ath_hal_is_fast_clock_enabled(_ah) \
 	((*(_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)))
+#define	ath_hal_setchainmasks(_ah, _txchainmask, _rxchainmask) \
+	((*(_ah)->ah_setChainMasks)((_ah), (_txchainmask), (_rxchainmask)))
 
-#define ath_hal_gpioCfgOutput(_ah, _gpio, _type) \
-        ((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio), (_type)))
-#define ath_hal_gpioset(_ah, _gpio, _b) \
-        ((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b)))
-#define ath_hal_gpioget(_ah, _gpio) \
-        ((*(_ah)->ah_gpioGet)((_ah), (_gpio)))
-#define ath_hal_gpiosetintr(_ah, _gpio, _b) \
-        ((*(_ah)->ah_gpioSetIntr)((_ah), (_gpio), (_b)))
+#define	ath_hal_spectral_supported(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_SPECTRAL_SCAN, 0, NULL) == HAL_OK)
+#define	ath_hal_spectral_get_config(_ah, _p) \
+	((*(_ah)->ah_spectralGetConfig)((_ah), (_p)))
+#define	ath_hal_spectral_configure(_ah, _p) \
+	((*(_ah)->ah_spectralConfigure)((_ah), (_p)))
+#define	ath_hal_spectral_start(_ah) \
+	((*(_ah)->ah_spectralStart)((_ah)))
+#define	ath_hal_spectral_stop(_ah) \
+	((*(_ah)->ah_spectralStop)((_ah)))
 
-#define ath_hal_radar_wait(_ah, _chan) \
-	((*(_ah)->ah_radarWait)((_ah), (_chan)))
+#define	ath_hal_btcoex_supported(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_BT_COEX, 0, NULL) == HAL_OK)
+#define	ath_hal_btcoex_set_info(_ah, _info) \
+	((*(_ah)->ah_btCoexSetInfo)((_ah), (_info)))
+#define	ath_hal_btcoex_set_config(_ah, _cfg) \
+	((*(_ah)->ah_btCoexSetConfig)((_ah), (_cfg)))
+#define	ath_hal_btcoex_set_qcu_thresh(_ah, _qcuid) \
+	((*(_ah)->ah_btCoexSetQcuThresh)((_ah), (_qcuid)))
+#define	ath_hal_btcoex_set_weights(_ah, _weight) \
+	((*(_ah)->ah_btCoexSetWeights)((_ah), (_weight)))
+#define	ath_hal_btcoex_set_weights(_ah, _weight) \
+	((*(_ah)->ah_btCoexSetWeights)((_ah), (_weight)))
+#define	ath_hal_btcoex_set_bmiss_thresh(_ah, _thr) \
+	((*(_ah)->ah_btCoexSetBmissThresh)((_ah), (_thr)))
+#define	ath_hal_btcoex_set_parameter(_ah, _attrib, _val) \
+	((*(_ah)->ah_btCoexSetParameter)((_ah), (_attrib), (_val)))
+#define	ath_hal_btcoex_enable(_ah) \
+	((*(_ah)->ah_btCoexEnable)((_ah)))
+#define	ath_hal_btcoex_disable(_ah) \
+	((*(_ah)->ah_btCoexDisable)((_ah)))
 
+#define	ath_hal_div_comb_conf_get(_ah, _conf) \
+	((*(_ah)->ah_divLnaConfGet)((_ah), (_conf)))
+#define	ath_hal_div_comb_conf_set(_ah, _conf) \
+	((*(_ah)->ah_divLnaConfSet)((_ah), (_conf)))
+
 #endif /* _DEV_ATH_ATHVAR_H */



More information about the Midnightbsd-cvs mailing list