[Midnightbsd-cvs] src: dev/ath:

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Tue Nov 25 11:40:45 EST 2008


Log Message:
-----------


Modified Files:
--------------
    src/sys/dev/ath:
        if_ath.c (r1.2 -> r1.3)
        if_ath_pci.c (r1.1.1.1 -> r1.2)
        if_athioctl.h (r1.1.1.2 -> r1.2)
        if_athrate.h (r1.1.1.1 -> r1.2)
        if_athvar.h (r1.1.1.2 -> r1.2)

-------------- next part --------------
Index: if_athvar.h
===================================================================
RCS file: /home/cvs/src/sys/dev/ath/if_athvar.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/dev/ath/if_athvar.h -L sys/dev/ath/if_athvar.h -u -r1.1.1.2 -r1.2
--- sys/dev/ath/if_athvar.h
+++ sys/dev/ath/if_athvar.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -12,13 +12,6 @@
  *    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.
- * 3. Neither the names of the above-listed copyright holders nor the names
- *    of any contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * NO WARRANTY
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@@ -33,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: src/sys/dev/ath/if_athvar.h,v 1.27.2.5 2006/02/24 19:51:11 sam Exp $
+ * $FreeBSD: src/sys/dev/ath/if_athvar.h,v 1.62 2007/06/11 03:36:49 sam Exp $
  */
 
 /*
@@ -42,9 +35,8 @@
 #ifndef _DEV_ATH_ATHVAR_H
 #define _DEV_ATH_ATHVAR_H
 
-#include <sys/taskqueue.h>
-
 #include <contrib/dev/ath/ah.h>
+#include <contrib/dev/ath/ah_desc.h>
 #include <net80211/ieee80211_radiotap.h>
 #include <dev/ath/if_athioctl.h>
 #include <dev/ath/if_athrate.h>
@@ -55,7 +47,7 @@
 #define	ATH_RXBUF	40		/* number of RX buffers */
 #endif
 #ifndef ATH_TXBUF
-#define	ATH_TXBUF	100		/* number of TX buffers */
+#define	ATH_TXBUF	200		/* number of TX buffers */
 #endif
 #define	ATH_TXDESC	10		/* number of descriptors per buffer */
 #define	ATH_TXMAXTRY	11		/* max number of transmit attempts */
@@ -77,10 +69,19 @@
 #define	ATH_KEYMAX	128		/* max key cache size we handle */
 #define	ATH_KEYBYTES	(ATH_KEYMAX/NBBY)	/* storage space in bytes */
 
+#define	ATH_FF_TXQMIN	2		/* min txq depth for staging */
+#define	ATH_FF_TXQMAX	50		/* maximum # of queued frames allowed */
+#define	ATH_FF_STAGEMAX	5		/* max waiting period for staged frame*/
+
+struct taskqueue;
+struct kthread;
+struct ath_buf;
+
 /* driver-specific node state */
 struct ath_node {
 	struct ieee80211_node an_node;	/* base class */
 	u_int32_t	an_avgrssi;	/* average rssi over all rx frames */
+	struct ath_buf	*an_ff_buf[WME_NUM_AC]; /* ff staging area */
 	/* variable-length rate control state follows */
 };
 #define	ATH_NODE(ni)	((struct ath_node *)(ni))
@@ -99,9 +100,12 @@
 
 struct ath_buf {
 	STAILQ_ENTRY(ath_buf)	bf_list;
+	TAILQ_ENTRY(ath_buf)	bf_stagelist;	/* stage queue list */
+	u_int32_t		bf_age;		/* age when placed on stageq */
 	int			bf_nseg;
 	int			bf_flags;	/* tx descriptor flags */
 	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 */
 	bus_dmamap_t		bf_dmamap;	/* DMA map for mbuf chain */
 	struct mbuf		*bf_m;		/* mbuf for buf */
@@ -119,7 +123,7 @@
 	const char*		dd_name;
 	struct ath_desc		*dd_desc;	/* descriptors */
 	bus_addr_t		dd_desc_paddr;	/* physical addr of dd_desc */
-	bus_addr_t		dd_desc_len;	/* size of dd_desc */
+	bus_size_t		dd_desc_len;	/* size of dd_desc */
 	bus_dma_segment_t	dd_dseg;
 	bus_dma_tag_t		dd_dmat;	/* bus DMA tag */
 	bus_dmamap_t		dd_dmamap;	/* DMA map for descriptors */
@@ -143,13 +147,20 @@
 	STAILQ_HEAD(, ath_buf)	axq_q;		/* transmit queue */
 	struct mtx		axq_lock;	/* lock on q and link */
 	char			axq_name[12];	/* e.g. "ath0_txq4" */
+	/*
+	 * Fast-frame state.  The staging queue holds awaiting
+	 * a fast-frame pairing.  Buffers on this queue are
+	 * assigned an ``age'' and flushed when they wait too long.
+	 */
+	TAILQ_HEAD(axq_headtype, ath_buf) axq_stageq;
+	u_int32_t		axq_curage;	/* queue age */
 };
 
 #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, "ath_txq", MTX_DEF); \
-} while (0);
+	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)
@@ -158,6 +169,7 @@
 #define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \
 	STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \
 	(_tq)->axq_depth++; \
+	(_tq)->axq_curage++; \
 } while (0)
 #define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \
 	STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \
@@ -171,22 +183,22 @@
 	struct ifnet		*sc_ifp;	/* interface common */
 	struct ath_stats	sc_stats;	/* interface statistics */
 	struct ieee80211com	sc_ic;		/* IEEE 802.11 common */
-	int			sc_countrycode;
 	int			sc_debug;
+	u_int32_t		sc_countrycode;
+	u_int32_t		sc_regdomain;
 	void			(*sc_recv_mgmt)(struct ieee80211com *,
 					struct mbuf *,
 					struct ieee80211_node *,
-					int, int, u_int32_t);
+					int, int, int, u_int32_t);
 	int			(*sc_newstate)(struct ieee80211com *,
 					enum ieee80211_state, int);
 	void 			(*sc_node_free)(struct ieee80211_node *);
 	device_t		sc_dev;
-	bus_space_tag_t		sc_st;		/* bus space tag */
-	bus_space_handle_t	sc_sh;		/* bus space handle */
+	HAL_BUS_TAG		sc_st;		/* bus space tag */
+	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 taskqueue	*sc_tq;		/* private task queue */
-	struct proc		*sc_tqproc;
 	struct ath_hal		*sc_ah;		/* Atheros HAL */
 	struct ath_ratectrl	*sc_rc;		/* tx rate control support */
 	struct ath_tx99		*sc_tx99;	/* tx99 adjunct state */
@@ -201,15 +213,23 @@
 				sc_ledstate: 1,	/* LED on/off state */
 				sc_blinking: 1,	/* LED blink operation active */
 				sc_mcastkey: 1,	/* mcast key cache search */
+				sc_scanning: 1,	/* scanning active */
 				sc_syncbeacon:1,/* sync/resync beacon timers */
-				sc_hasclrkey:1;	/* CLR key supported */
+				sc_hasclrkey:1,	/* CLR key supported */
+				sc_xchanmode: 1,/* extended channel mode */
+				sc_outdoor  : 1,/* outdoor operation */
+				sc_dturbo  : 1;	/* dynamic turbo in use */
 						/* rate tables */
-	const HAL_RATE_TABLE	*sc_rates[IEEE80211_MODE_MAX];
+#define	IEEE80211_MODE_HALF	(IEEE80211_MODE_MAX+0)
+#define	IEEE80211_MODE_QUARTER	(IEEE80211_MODE_MAX+1)
+	const HAL_RATE_TABLE	*sc_rates[IEEE80211_MODE_MAX+2];
 	const HAL_RATE_TABLE	*sc_currates;	/* current rate table */
 	enum ieee80211_phymode	sc_curmode;	/* current phy mode */
 	HAL_OPMODE		sc_opmode;	/* current operating mode */
 	u_int16_t		sc_curtxpow;	/* current tx power limit */
+	u_int16_t		sc_curaid;	/* current association id */
 	HAL_CHANNEL		sc_curchan;	/* current h/w channel */
+	u_int8_t		sc_curbssid[IEEE80211_ADDR_LEN];
 	u_int8_t		sc_rixmap[256];	/* IEEE to h/w rate table ix */
 	struct {
 		u_int8_t	ieeerate;	/* IEEE rate */
@@ -221,7 +241,10 @@
 	u_int8_t		sc_minrateix;	/* min h/w rate index */
 	u_int8_t		sc_mcastrix;	/* mcast h/w rate index */
 	u_int8_t		sc_protrix;	/* protection rate index */
+	u_int8_t		sc_lastdatarix;	/* last data frame rate index */
 	u_int			sc_mcastrate;	/* ieee rate for mcastrateix */
+	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 */
 	u_int			sc_keymax;	/* size of key cache */
@@ -252,14 +275,12 @@
 	int			sc_rx_th_len;
 	u_int			sc_monpass;	/* frames to pass in mon.mode */
 
-	struct task		sc_fataltask;	/* fatal int processing */
-
 	struct ath_descdma	sc_rxdma;	/* RX descriptos */
 	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 */
 	struct task		sc_rxorntask;	/* rxorn int processing */
-	struct task		sc_radartask;	/* radar processing */
 	u_int8_t		sc_defant;	/* current default antenna */
 	u_int8_t		sc_rxotherant;	/* rx's on non-default antenna*/
 	u_int64_t		sc_lastrx;	/* tsf at last rx'd frame */
@@ -268,7 +289,6 @@
 	ath_bufhead		sc_txbuf;	/* transmit buffer */
 	struct mtx		sc_txbuflock;	/* txbuf lock */
 	char			sc_txname[12];	/* e.g. "ath0_buf" */
-	int			sc_tx_timer;	/* transmit timeout */
 	u_int			sc_txqsetup;	/* h/w queues setup */
 	u_int			sc_txintrperiod;/* tx interrupt batching */
 	struct ath_txq		sc_txq[HAL_NUM_TX_QUEUES];
@@ -289,12 +309,12 @@
 		UPDATE,				/* update pending */
 		COMMIT				/* beacon sent, commit change */
 	} sc_updateslot;			/* slot time update fsm */
+	struct ath_txq		sc_mcastq;	/* mcast xmits w/ ps sta's */
 
 	struct callout		sc_cal_ch;	/* callout handle for cals */
 	int			sc_calinterval;	/* current polling interval */
 	int			sc_caltries;	/* cals at current interval */
 	HAL_NODE_STATS		sc_halstats;	/* station-mode rssi stats */
-	struct callout		sc_scan_ch;	/* callout handle for scan */
 	struct callout		sc_dfs_ch;	/* callout handle for dfs */
 };
 #define	sc_tx_th		u_tx_rt.th
@@ -302,7 +322,7 @@
 
 #define	ATH_LOCK_INIT(_sc) \
 	mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
-		 MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE)
+		 NULL, MTX_DEF | MTX_RECURSE)
 #define	ATH_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_mtx)
 #define	ATH_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
 #define	ATH_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
@@ -313,7 +333,7 @@
 #define	ATH_TXBUF_LOCK_INIT(_sc) do { \
 	snprintf((_sc)->sc_txname, sizeof((_sc)->sc_txname), "%s_buf", \
 		device_get_nameunit((_sc)->sc_dev)); \
-	mtx_init(&(_sc)->sc_txbuflock, (_sc)->sc_txname, "ath_buf", MTX_DEF); \
+	mtx_init(&(_sc)->sc_txbuflock, (_sc)->sc_txname, NULL, MTX_DEF); \
 } while (0)
 #define	ATH_TXBUF_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->sc_txbuflock)
 #define	ATH_TXBUF_LOCK(_sc)		mtx_lock(&(_sc)->sc_txbuflock)
@@ -420,6 +440,8 @@
 #define	ath_hal_getdiagstate(_ah, _id, _indata, _insize, _outdata, _outsize) \
 	((*(_ah)->ah_getDiagState)((_ah), (_id), \
 		(_indata), (_insize), (_outdata), (_outsize)))
+#define	ath_hal_getfatalstate(_ah, _outdata, _outsize) \
+	ath_hal_getdiagstate(_ah, 29, NULL, 0, (_outdata), _outsize)
 #define	ath_hal_setuptxqueue(_ah, _type, _irq) \
 	((*(_ah)->ah_setupTxQueue)((_ah), (_type), (_irq)))
 #define	ath_hal_resettxqueue(_ah, _q) \
@@ -464,8 +486,12 @@
 	((*(_ah)->ah_setRegulatoryDomain)((_ah), (_rd), NULL))
 #define	ath_hal_getcountrycode(_ah, _pcc) \
 	(*(_pcc) = (_ah)->ah_countryCode)
-#define	ath_hal_tkipsplit(_ah) \
+#define	ath_hal_hastkipsplit(_ah) \
 	(ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK)
+#define	ath_hal_gettkipsplit(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, NULL) == HAL_OK)
+#define	ath_hal_settkipsplit(_ah, _v) \
+	ath_hal_setcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, _v, NULL)
 #define	ath_hal_hwphycounters(_ah) \
 	(ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK)
 #define	ath_hal_hasdiversity(_ah) \
@@ -474,6 +500,10 @@
 	(ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 1, NULL) == HAL_OK)
 #define	ath_hal_setdiversity(_ah, _v) \
 	ath_hal_setcapability(_ah, HAL_CAP_DIVERSITY, 1, _v, NULL)
+#define	ath_hal_getantennaswitch(_ah) \
+	((*(_ah)->ah_getAntennaSwitch)((_ah)))
+#define	ath_hal_setantennaswitch(_ah, _v) \
+	((*(_ah)->ah_setAntennaSwitch)((_ah), (_v)))
 #define	ath_hal_getdiag(_ah, _pv) \
 	(ath_hal_getcapability(_ah, HAL_CAP_DIAG, 0, _pv) == HAL_OK)
 #define	ath_hal_setdiag(_ah, _v) \
@@ -510,6 +540,8 @@
 #else
 #define	ath_hal_getmcastkeysearch(_ah)	0
 #endif
+#define	ath_hal_hasfastframes(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_FASTFRAME, 0, NULL) == HAL_OK)
 #define	ath_hal_hasrfsilent(_ah) \
 	(ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 0, NULL) == HAL_OK)
 #define	ath_hal_getrfkill(_ah) \
@@ -528,15 +560,8 @@
 	(ath_hal_getcapability(_ah, HAL_CAP_TPC_CTS, 0, _ptpcts) == HAL_OK)
 #define	ath_hal_settpcts(_ah, _tpcts) \
 	ath_hal_setcapability(_ah, HAL_CAP_TPC_CTS, 0, _tpcts, NULL)
-#if HAL_ABI_VERSION < 0x05120700
-#define	ath_hal_process_noisefloor(_ah)
-#define	ath_hal_getchannoise(_ah, _c)	(-96)
-#define	HAL_CAP_TPC_ACK	100
-#define	HAL_CAP_TPC_CTS	101
-#else
 #define	ath_hal_getchannoise(_ah, _c) \
 	((*(_ah)->ah_getChanNoise)((_ah), (_c)))
-#endif
 #if HAL_ABI_VERSION < 0x05122200
 #define	HAL_TXQ_TXOKINT_ENABLE	TXQ_FLAG_TXOKINT_ENABLE
 #define	HAL_TXQ_TXERRINT_ENABLE	TXQ_FLAG_TXERRINT_ENABLE
@@ -544,11 +569,29 @@
 #define	HAL_TXQ_TXEOLINT_ENABLE	TXQ_FLAG_TXEOLINT_ENABLE
 #define	HAL_TXQ_TXURNINT_ENABLE	TXQ_FLAG_TXURNINT_ENABLE
 #endif
+#if HAL_ABI_VERSION < 0x06102501
+#define	ath_hal_ispublicsafetysku(ah) \
+	(((ah)->ah_regdomain == 0 && (ah)->ah_countryCode == 842) || \
+	 (ah)->ah_regdomain == 0x12)
+#endif
+#if HAL_ABI_VERSION < 0x06122400
+/* XXX yech, can't get to regdomain so just hack a compat shim */
+#define	ath_hal_isgsmsku(ah) \
+	((ah)->ah_countryCode == 843)
+#endif
+#if HAL_ABI_VERSION < 0x07050400
+/* compat shims so code compilers--it won't work though */
+#define	CHANNEL_HT20		0x10000
+#define	CHANNEL_HT40PLUS 	0x20000
+#define	CHANNEL_HT40MINUS 	0x40000
+#define	HAL_MODE_11NG_HT20	0x008000
+#define HAL_MODE_11NA_HT20  	0x010000
+#endif
 
 #define	ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \
 	((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq)))
-#define	ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext) \
-	((*(_ah)->ah_procRxDesc)((_ah), (_ds), (_dspa), (_dsnext), 0))
+#define	ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext, _rs) \
+	((*(_ah)->ah_procRxDesc)((_ah), (_ds), (_dspa), (_dsnext), 0, (_rs)))
 #define	ath_hal_setuptxdesc(_ah, _ds, _plen, _hlen, _atype, _txpow, \
 		_txr0, _txtr0, _keyix, _ant, _flags, \
 		_rtsrate, _rtsdura) \
@@ -561,8 +604,8 @@
 		(_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_txprocdesc(_ah, _ds) \
-	((*(_ah)->ah_procTxDesc)((_ah), (_ds)))
+#define	ath_hal_txprocdesc(_ah, _ds, _ts) \
+	((*(_ah)->ah_procTxDesc)((_ah), (_ds), (_ts)))
 #define	ath_hal_gettxintrtxqs(_ah, _txqs) \
 	((*(_ah)->ah_getTxIntrQueue)((_ah), (_txqs)))
 
@@ -575,12 +618,6 @@
 #define ath_hal_gpiosetintr(_ah, _gpio, _b) \
         ((*(_ah)->ah_gpioSetIntr)((_ah), (_gpio), (_b)))
 
-#define ath_hal_radar_event(_ah) \
-	((*(_ah)->ah_radarHaveEvent)((_ah)))
-#define ath_hal_procdfs(_ah, _chan) \
-	((*(_ah)->ah_processDfs)((_ah), (_chan)))
-#define ath_hal_checknol(_ah, _chan, _nchans) \
-	((*(_ah)->ah_dfsNolCheck)((_ah), (_chan), (_nchans)))
 #define ath_hal_radar_wait(_ah, _chan) \
 	((*(_ah)->ah_radarWait)((_ah), (_chan)))
 
Index: if_ath.c
===================================================================
RCS file: /home/cvs/src/sys/dev/ath/if_ath.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -L sys/dev/ath/if_ath.c -L sys/dev/ath/if_ath.c -u -r1.2 -r1.3
--- sys/dev/ath/if_ath.c
+++ sys/dev/ath/if_ath.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -12,13 +12,6 @@
  *    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.
- * 3. Neither the names of the above-listed copyright holders nor the names
- *    of any contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * NO WARRANTY
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@@ -35,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/dev/ath/if_ath.c,v 1.94.2.16.2.2 2006/05/02 17:08:34 sam Exp $");
+__FBSDID("$FreeBSD: src/sys/dev/ath/if_ath.c,v 1.177 2007/09/17 19:07:23 sam Exp $");
 
 /*
  * Driver for the Atheros Wireless LAN controller.
@@ -117,7 +110,6 @@
 static void	ath_fatal_proc(void *, int);
 static void	ath_rxorn_proc(void *, int);
 static void	ath_bmiss_proc(void *, int);
-static void	ath_radar_proc(void *, int);
 static int	ath_key_alloc(struct ieee80211com *,
 			const struct ieee80211_key *,
 			ieee80211_keyix *, ieee80211_keyix *);
@@ -132,6 +124,7 @@
 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 ieee80211com *, int item);
 static void	ath_beacon_setup(struct ath_softc *, struct ath_buf *);
 static void	ath_beacon_proc(void *, int);
 static void	ath_bstuck_proc(void *, int);
@@ -143,18 +136,22 @@
 static void	ath_desc_free(struct ath_softc *);
 static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *);
 static void	ath_node_free(struct ieee80211_node *);
-static u_int8_t	ath_node_getrssi(const struct ieee80211_node *);
+static int8_t	ath_node_getrssi(const 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 ieee80211com *ic, struct mbuf *m,
 			struct ieee80211_node *ni,
-			int subtype, int rssi, u_int32_t rstamp);
+			int subtype, int rssi, int noise, u_int32_t rstamp);
 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 void	ath_freetx(struct mbuf *);
 static int	ath_tx_start(struct ath_softc *, struct ieee80211_node *,
 			     struct ath_buf *, struct mbuf *);
 static void	ath_tx_proc_q0(void *, int);
@@ -165,13 +162,15 @@
 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_next_scan(void *);
+static void	ath_scan_start(struct ieee80211com *);
+static void	ath_scan_end(struct ieee80211com *);
+static void	ath_set_channel(struct ieee80211com *);
 static void	ath_calibrate(void *);
 static int	ath_newstate(struct ieee80211com *, enum ieee80211_state, int);
 static void	ath_setup_stationkey(struct ieee80211_node *);
 static void	ath_newassoc(struct ieee80211_node *, int);
-static int	ath_getchannels(struct ath_softc *, u_int cc,
-			HAL_BOOL outdoor, HAL_BOOL xchanmode);
+static int	ath_getchannels(struct ath_softc *,
+			HAL_REG_DOMAIN, HAL_CTRY_CODE, HAL_BOOL, HAL_BOOL);
 static void	ath_led_event(struct ath_softc *, int);
 static void	ath_update_txpow(struct ath_softc *);
 
@@ -179,28 +178,27 @@
 static void	ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
 
 static void	ath_sysctlattach(struct ath_softc *);
+static int	ath_raw_xmit(struct ieee80211_node *,
+			struct mbuf *, const struct ieee80211_bpf_params *);
 static void	ath_bpfattach(struct ath_softc *);
 static void	ath_announce(struct ath_softc *);
 
 SYSCTL_DECL(_hw_ath);
 
 /* XXX validate sysctl values */
-static	int ath_dwelltime = 200;		/* 5 channels/second */
-SYSCTL_INT(_hw_ath, OID_AUTO, dwell, CTLFLAG_RW, &ath_dwelltime,
-	    0, "channel dwell time (ms) for AP/station scanning");
 static	int ath_calinterval = 30;		/* calibrate every 30 secs */
 SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval,
 	    0, "chip calibration interval (secs)");
 static	int ath_outdoor = AH_TRUE;		/* outdoor operation */
-SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RD, &ath_outdoor,
+SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RW, &ath_outdoor,
 	    0, "outdoor operation");
 TUNABLE_INT("hw.ath.outdoor", &ath_outdoor);
 static	int ath_xchanmode = AH_TRUE;		/* extended channel use */
-SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RD, &ath_xchanmode,
+SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RW, &ath_xchanmode,
 	    0, "extended channel mode");
 TUNABLE_INT("hw.ath.xchanmode", &ath_xchanmode);
 static	int ath_countrycode = CTRY_DEFAULT;	/* country code */
-SYSCTL_INT(_hw_ath, OID_AUTO, countrycode, CTLFLAG_RD, &ath_countrycode,
+SYSCTL_INT(_hw_ath, OID_AUTO, countrycode, CTLFLAG_RW, &ath_countrycode,
 	    0, "country code");
 TUNABLE_INT("hw.ath.countrycode", &ath_countrycode);
 static	int ath_regdomain = 0;			/* regulatory domain */
@@ -208,11 +206,11 @@
 	    0, "regulatory domain");
 
 static	int ath_rxbuf = ATH_RXBUF;		/* # rx buffers to allocate */
-SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RD, &ath_rxbuf,
+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 */
-SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RD, &ath_txbuf,
+SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf,
 	    0, "tx buffers allocated");
 TUNABLE_INT("hw.ath.txbuf", &ath_txbuf);
 
@@ -254,10 +252,10 @@
 } while (0)
 #define	KEYPRINTF(sc, ix, hk, mac) do {				\
 	if (sc->sc_debug & ATH_DEBUG_KEYCACHE)			\
-		ath_keyprint(__func__, ix, hk, mac);		\
+		ath_keyprint(sc, __func__, ix, hk, mac);	\
 } while (0)
-static	void ath_printrxbuf(struct ath_buf *bf, u_int ix, int);
-static	void ath_printtxbuf(struct ath_buf *bf, u_int qnum, u_int ix, int done);
+static	void ath_printrxbuf(const struct ath_buf *bf, u_int ix, int);
+static	void ath_printtxbuf(const struct ath_buf *bf, u_int qnum, u_int ix, int done);
 #else
 #define	IFF_DUMPPKTS(sc, m) \
 	((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
@@ -342,18 +340,6 @@
 	 */
 	for (i = 0; i < sc->sc_keymax; i++)
 		ath_hal_keyreset(ah, i);
-	/*
-	 * Mark key cache slots associated with global keys
-	 * as in use.  If we knew TKIP was not to be used we
-	 * could leave the +32, +64, and +32+64 slots free.
-	 * XXX only for splitmic.
-	 */
-	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
-		setbit(sc->sc_keymap, i);
-		setbit(sc->sc_keymap, i+32);
-		setbit(sc->sc_keymap, i+64);
-		setbit(sc->sc_keymap, i+32+64);
-	}
 
 	/*
 	 * Collect the channel list using the default country
@@ -361,8 +347,8 @@
 	 * is resposible for filtering this list based on settings
 	 * like the phy mode.
 	 */
-	error = ath_getchannels(sc, ath_countrycode,
-			ath_outdoor, ath_xchanmode);
+	error = ath_getchannels(sc, ath_regdomain, ath_countrycode,
+			ath_outdoor != 0, ath_xchanmode != 0);
 	if (error != 0)
 		goto bad;
 
@@ -374,6 +360,12 @@
 	ath_rate_setup(sc, IEEE80211_MODE_11G);
 	ath_rate_setup(sc, IEEE80211_MODE_TURBO_A);
 	ath_rate_setup(sc, IEEE80211_MODE_TURBO_G);
+	ath_rate_setup(sc, IEEE80211_MODE_STURBO_A);
+	ath_rate_setup(sc, IEEE80211_MODE_11NA);
+	ath_rate_setup(sc, IEEE80211_MODE_11NG);
+	ath_rate_setup(sc, IEEE80211_MODE_HALF);
+	ath_rate_setup(sc, IEEE80211_MODE_QUARTER);
+
 	/* NB: setup here so ath_rate_update is happy */
 	ath_setcurmode(sc, IEEE80211_MODE_11A);
 
@@ -385,24 +377,20 @@
 		if_printf(ifp, "failed to allocate descriptors: %d\n", error);
 		goto bad;
 	}
-	callout_init(&sc->sc_scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0);
 	callout_init(&sc->sc_cal_ch, CALLOUT_MPSAFE);
 	callout_init(&sc->sc_dfs_ch, CALLOUT_MPSAFE);
 
 	ATH_TXBUF_LOCK_INIT(sc);
 
 	sc->sc_tq = taskqueue_create("ath_taskq", M_NOWAIT,
-		taskqueue_thread_enqueue, &sc->sc_tq, &sc->sc_tqproc);
-	/* XXX return error */
-	kthread_create(taskqueue_thread_loop, &sc->sc_tq, &sc->sc_tqproc,
-		0, 0, "%s taskq", ifp->if_xname);
+		taskqueue_thread_enqueue, &sc->sc_tq);
+	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_rxorntask, 0, ath_rxorn_proc, sc);
-	TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, 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_radartask, 0, ath_radar_proc, sc);
 
 	/*
 	 * Allocate hardware transmit queues: one queue for
@@ -424,6 +412,8 @@
 		error = EIO;
 		goto bad2;
 	}
+	/* NB: s/w q, qnum used only by WITNESS */
+	ath_txq_init(sc, &sc->sc_mcastq, HAL_NUM_TX_QUEUES+1);
 	/* NB: insure BK queue is the lowest priority h/w queue */
 	if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) {
 		if_printf(ifp, "unable to setup xmit queue for %s traffic!\n",
@@ -434,7 +424,7 @@
 	if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) ||
 	    !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) ||
 	    !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) {
-		/* 
+		/*
 		 * Not enough hardware tx queues to properly do WME;
 		 * just punt and assign them all to the same h/w queue.
 		 * We could do a better job of this if, for example,
@@ -450,7 +440,7 @@
 		sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK];
 	}
 
-	/* 
+	/*
 	 * Special case certain configurations.  Note the
 	 * CAB queue is handled by these specially so don't
 	 * include them when checking the txq setup mask.
@@ -522,6 +512,8 @@
 		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
 		| IEEE80211_C_SHSLOT		/* short slot time supported */
 		| IEEE80211_C_WPA		/* capable of WPA1+WPA2 */
+		| IEEE80211_C_BGSCAN		/* capable of bg scanning */
+		| IEEE80211_C_TXFRAG		/* handle tx frags */
 		;
 	/*
 	 * Query the hal to figure out h/w crypto support.
@@ -543,12 +535,30 @@
 		 */
 		if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC))
 			ic->ic_caps |= IEEE80211_C_TKIPMIC;
-		if (ath_hal_tkipsplit(ah))
+		/*
+		 * If the h/w supports storing tx+rx MIC keys
+		 * in one cache slot automatically enable use.
+		 */
+		if (ath_hal_hastkipsplit(ah) ||
+		    !ath_hal_settkipsplit(ah, AH_FALSE))
 			sc->sc_splitmic = 1;
 	}
 	sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR);
 	sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah);
 	/*
+	 * Mark key cache slots associated with global keys
+	 * as in use.  If we knew TKIP was not to be used we
+	 * could leave the +32, +64, and +32+64 slots free.
+	 */
+	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+		setbit(sc->sc_keymap, i);
+		setbit(sc->sc_keymap, i+64);
+		if (sc->sc_splitmic) {
+			setbit(sc->sc_keymap, i+32);
+			setbit(sc->sc_keymap, i+32+64);
+		}
+	}
+	/*
 	 * TPC support can be done either with a global cap or
 	 * per-packet support.  The latter is not available on
 	 * all parts.  We're a bit pedantic here as all parts
@@ -568,6 +578,10 @@
 	 */
 	if (ath_hal_hasbursting(ah))
 		ic->ic_caps |= IEEE80211_C_BURST;
+	if (ath_hal_hasfastframes(ah))
+		ic->ic_caps |= IEEE80211_C_FF;
+	if (ath_hal_getwirelessmodes(ah, ath_countrycode) & (HAL_MODE_108G|HAL_MODE_TURBO))
+		ic->ic_caps |= IEEE80211_C_TURBOP;
 
 	/*
 	 * Indicate we need the 802.11 header padded to a
@@ -597,16 +611,22 @@
 	sc->sc_node_free = ic->ic_node_free;
 	ic->ic_node_free = ath_node_free;
 	ic->ic_node_getrssi = ath_node_getrssi;
+	ic->ic_node_getsignal = ath_node_getsignal;
 	sc->sc_recv_mgmt = ic->ic_recv_mgmt;
 	ic->ic_recv_mgmt = ath_recv_mgmt;
 	sc->sc_newstate = ic->ic_newstate;
 	ic->ic_newstate = ath_newstate;
+	ic->ic_scan_start = ath_scan_start;
+	ic->ic_scan_end = ath_scan_end;
+	ic->ic_set_channel = ath_set_channel;
 	ic->ic_crypto.cs_max_keyix = sc->sc_keymax;
 	ic->ic_crypto.cs_key_alloc = ath_key_alloc;
 	ic->ic_crypto.cs_key_delete = ath_key_delete;
 	ic->ic_crypto.cs_key_set = ath_key_set;
 	ic->ic_crypto.cs_key_update_begin = ath_key_update_begin;
 	ic->ic_crypto.cs_key_update_end = ath_key_update_end;
+	ic->ic_raw_xmit = ath_raw_xmit;
+	ic->ic_update_beacon = ath_beacon_update;
 	/* complete initialization */
 	ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
 
@@ -732,8 +752,10 @@
 	}
 	if (!ath_hal_intrpend(ah))		/* shared irq, not for us */
 		return;
-	if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags &
-	    IFF_DRV_RUNNING))) {
+	if ((ifp->if_flags & IFF_UP) == 0 ||
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+		HAL_INT status;
+
 		DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n",
 			__func__, ifp->if_flags);
 		ath_hal_getisr(ah, &status);	/* clear ISR */
@@ -750,15 +772,9 @@
 	DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status);
 	status &= sc->sc_imask;			/* discard unasked for bits */
 	if (status & HAL_INT_FATAL) {
-		/*
-		 * Fatal errors are unrecoverable.  Typically
-		 * these are caused by DMA errors.  Unfortunately
-		 * the exact reason is not (presently) returned
-		 * by the hal.
-		 */
 		sc->sc_stats.ast_hardware++;
 		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
-		taskqueue_enqueue(sc->sc_tq, &sc->sc_fataltask);
+		ath_fatal_proc(sc, 0);
 	} else if (status & HAL_INT_RXORN) {
 		sc->sc_stats.ast_rxorn++;
 		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
@@ -817,8 +833,23 @@
 {
 	struct ath_softc *sc = arg;
 	struct ifnet *ifp = sc->sc_ifp;
+	u_int32_t *state;
+	u_int32_t len;
+	void *sp;
 
 	if_printf(ifp, "hardware error; resetting\n");
+	/*
+	 * Fatal errors are unrecoverable.  Typically these
+	 * are caused by DMA errors.  Collect h/w state from
+	 * the hal so we can diagnose what's going on.
+	 */
+	if (ath_hal_getfatalstate(sc->sc_ah, &sp, &len)) {
+		KASSERT(len >= 6*sizeof(u_int32_t), ("len %u bytes", len));
+		state = sp;
+		if_printf(ifp, "0x%08x 0x%08x 0x%08x, 0x%08x 0x%08x 0x%08x\n",
+		    state[0], state[1] , state[2], state[3],
+		    state[4], state[5]);
+	}
 	ath_reset(ifp);
 }
 
@@ -859,51 +890,52 @@
 		 * truly a bmiss we'll get another interrupt soon and that'll
 		 * be dispatched up for processing.
 		 */
-		if (tsf - lastrx > bmisstimeout) {
-			NET_LOCK_GIANT();
+		if (tsf - lastrx > bmisstimeout)
 			ieee80211_beacon_miss(ic);
-			NET_UNLOCK_GIANT();
-		} else
+		else
 			sc->sc_stats.ast_bmiss_phantom++;
 	}
 }
 
+/*
+ * Convert net80211 channel to a HAL channel with the flags
+ * constrained to reflect the current operating mode and
+ * the frequency possibly mapped for GSM channels.
+ */
 static void
-ath_radar_proc(void *arg, int pending)
-{
-	struct ath_softc *sc = arg;
-	struct ifnet *ifp = sc->sc_ifp;
-	struct ath_hal *ah = sc->sc_ah;
-	HAL_CHANNEL hchan;
-
-	if (ath_hal_procdfs(ah, &hchan)) {
-		if_printf(ifp, "radar detected on channel %u/0x%x/0x%x\n",
-			hchan.channel, hchan.channelFlags, hchan.privFlags);
-		/*
-		 * Initiate channel change.
-		 */
-		/* XXX not yet */
-	}
-}
-
-static u_int
-ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan)
+ath_mapchan(HAL_CHANNEL *hc, const struct ieee80211_channel *chan)
 {
 #define	N(a)	(sizeof(a) / sizeof(a[0]))
-	static const u_int modeflags[] = {
+	static const u_int modeflags[IEEE80211_MODE_MAX] = {
 		0,			/* IEEE80211_MODE_AUTO */
 		CHANNEL_A,		/* IEEE80211_MODE_11A */
 		CHANNEL_B,		/* IEEE80211_MODE_11B */
 		CHANNEL_PUREG,		/* IEEE80211_MODE_11G */
 		0,			/* IEEE80211_MODE_FH */
-		CHANNEL_ST,		/* IEEE80211_MODE_TURBO_A */
-		CHANNEL_108G		/* IEEE80211_MODE_TURBO_G */
+		CHANNEL_108A,		/* IEEE80211_MODE_TURBO_A */
+		CHANNEL_108G,		/* IEEE80211_MODE_TURBO_G */
+		CHANNEL_ST,		/* IEEE80211_MODE_STURBO_A */
+		CHANNEL_A,		/* IEEE80211_MODE_11NA */
+		CHANNEL_PUREG,		/* IEEE80211_MODE_11NG */
 	};
-	enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan);
+	enum ieee80211_phymode mode = ieee80211_chan2mode(chan);
 
 	KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode));
 	KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode));
-	return modeflags[mode];
+	hc->channelFlags = modeflags[mode];
+	if (IEEE80211_IS_CHAN_HALF(chan))
+		hc->channelFlags |= CHANNEL_HALF;
+	if (IEEE80211_IS_CHAN_QUARTER(chan))
+		hc->channelFlags |= CHANNEL_QUARTER;
+	if (IEEE80211_IS_CHAN_HT20(chan))
+		hc->channelFlags |= CHANNEL_HT20;
+	if (IEEE80211_IS_CHAN_HT40D(chan))
+		hc->channelFlags |= CHANNEL_HT40MINUS;
+	if (IEEE80211_IS_CHAN_HT40U(chan))
+		hc->channelFlags |= CHANNEL_HT40PLUS;
+
+	hc->channel = IEEE80211_IS_CHAN_GSM(chan) ?
+		2422 + (922 - chan->ic_freq) : chan->ic_freq;
 #undef N
 }
 
@@ -933,8 +965,7 @@
 	 * be followed by initialization of the appropriate bits
 	 * and then setup of the interrupt mask.
 	 */
-	sc->sc_curchan.channel = ic->ic_curchan->ic_freq;
-	sc->sc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_curchan);
+	ath_mapchan(&sc->sc_curchan, ic->ic_curchan);
 	if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
 		if_printf(ifp, "unable to reset hardware; hal status %u\n",
 			status);
@@ -1092,16 +1123,13 @@
 	struct ath_softc *sc = ifp->if_softc;
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ath_hal *ah = sc->sc_ah;
-	struct ieee80211_channel *c;
 	HAL_STATUS status;
 
 	/*
 	 * Convert to a HAL channel description with the flags
 	 * constrained to reflect the current operating mode.
 	 */
-	c = ic->ic_curchan;
-	sc->sc_curchan.channel = c->ic_freq;
-	sc->sc_curchan.channelFlags = ath_chan2flags(ic, c);
+	ath_mapchan(&sc->sc_curchan, ic->ic_curchan);
 
 	ath_hal_intrset(ah, 0);		/* disable interrupts */
 	ath_draintxq(sc);		/* stop xmit side */
@@ -1114,14 +1142,14 @@
 	sc->sc_diversity = ath_hal_getdiversity(ah);
 	sc->sc_calinterval = 1;
 	sc->sc_caltries = 0;
+	if (ath_startrecv(sc) != 0)	/* restart recv */
+		if_printf(ifp, "%s: unable to start recv logic\n", __func__);
 	/*
 	 * We may be doing a reset in response to an ioctl
 	 * that changes the channel so update any state that
 	 * might change as a result.
 	 */
-	ath_chan_change(sc, c);
-	if (ath_startrecv(sc) != 0)	/* restart recv */
-		if_printf(ifp, "%s: unable to start recv logic\n", __func__);
+	ath_chan_change(sc, ic->ic_curchan);
 	if (ic->ic_state == IEEE80211_S_RUN)
 		ath_beacon_config(sc);	/* restart beacons */
 	ath_hal_intrset(ah, sc->sc_imask);
@@ -1130,6 +1158,369 @@
 	return 0;
 }
 
+static int 
+ath_ff_always(struct ath_txq *txq, struct ath_buf *bf)
+{
+	return 0;
+}
+
+#if 0
+static int 
+ath_ff_ageflushtestdone(struct ath_txq *txq, struct ath_buf *bf)
+{
+	return (txq->axq_curage - bf->bf_age) < ATH_FF_STAGEMAX;
+}
+#endif
+
+/*
+ * Flush FF staging queue.
+ */
+static void
+ath_ff_stageq_flush(struct ath_softc *sc, struct ath_txq *txq,
+	int (*ath_ff_flushdonetest)(struct ath_txq *txq, struct ath_buf *bf))
+{
+	struct ath_buf *bf;
+	struct ieee80211_node *ni;
+	int pktlen, pri;
+	
+	for (;;) {
+		ATH_TXQ_LOCK(txq);
+		/*
+		 * Go from the back (oldest) to front so we can
+		 * stop early based on the age of the entry.
+		 */
+		bf = TAILQ_LAST(&txq->axq_stageq, axq_headtype);
+		if (bf == NULL || ath_ff_flushdonetest(txq, bf)) {
+			ATH_TXQ_UNLOCK(txq);
+			break;
+		}
+
+		ni = bf->bf_node;
+		pri = M_WME_GETAC(bf->bf_m);
+		KASSERT(ATH_NODE(ni)->an_ff_buf[pri],
+			("no bf on staging queue %p", bf));
+		ATH_NODE(ni)->an_ff_buf[pri] = NULL;
+		TAILQ_REMOVE(&txq->axq_stageq, bf, bf_stagelist);
+		
+		ATH_TXQ_UNLOCK(txq);
+
+		DPRINTF(sc, ATH_DEBUG_FF, "%s: flush frame, age %u\n",
+			__func__, bf->bf_age);
+
+		sc->sc_stats.ast_ff_flush++;
+		
+		/* encap and xmit */
+		bf->bf_m = ieee80211_encap(&sc->sc_ic, bf->bf_m, ni);
+		if (bf->bf_m == NULL) {
+			DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
+				"%s: discard, encapsulation failure\n",
+				__func__);
+			sc->sc_stats.ast_tx_encap++;
+			goto bad;
+		}
+		pktlen = bf->bf_m->m_pkthdr.len; /* NB: don't reference below */
+		if (ath_tx_start(sc, ni, bf, bf->bf_m) == 0) {
+#if 0 /*XXX*/
+			ifp->if_opackets++;
+#endif
+			continue;
+		}
+	bad:
+		if (ni != NULL)
+			ieee80211_free_node(ni);
+		bf->bf_node = NULL;
+		if (bf->bf_m != NULL) {
+			m_freem(bf->bf_m);
+			bf->bf_m = NULL;
+		}
+
+		ATH_TXBUF_LOCK(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ATH_TXBUF_UNLOCK(sc);
+	}
+}
+
+static __inline u_int32_t
+ath_ff_approx_txtime(struct ath_softc *sc, struct ath_node *an, struct mbuf *m)
+{
+	u_int32_t framelen;
+	struct ath_buf *bf;
+
+	/*
+	 * Approximate the frame length to be transmitted. A swag to add
+	 * the following maximal values to the skb payload:
+	 *   - 32: 802.11 encap + CRC
+	 *   - 24: encryption overhead (if wep bit)
+	 *   - 4 + 6: fast-frame header and padding
+	 *   - 16: 2 LLC FF tunnel headers
+	 *   - 14: 1 802.3 FF tunnel header (skb already accounts for 2nd)
+	 */
+	framelen = m->m_pkthdr.len + 32 + 4 + 6 + 16 + 14;
+	if (sc->sc_ic.ic_flags & IEEE80211_F_PRIVACY)
+		framelen += 24;
+	bf = an->an_ff_buf[M_WME_GETAC(m)];
+	if (bf != NULL)
+		framelen += bf->bf_m->m_pkthdr.len;
+	return ath_hal_computetxtime(sc->sc_ah, sc->sc_currates, framelen,
+			sc->sc_lastdatarix, AH_FALSE);
+}
+
+/*
+ * Determine if a data frame may be aggregated via ff tunnelling.
+ * Note the caller is responsible for checking if the destination
+ * supports fast frames.
+ *
+ *  NB: allowing EAPOL frames to be aggregated with other unicast traffic.
+ *      Do 802.1x EAPOL frames proceed in the clear? Then they couldn't
+ *      be aggregated with other types of frames when encryption is on?
+ *
+ *  NB: assumes lock on an_ff_buf effectively held by txq lock mechanism.
+ */
+static __inline int 
+ath_ff_can_aggregate(struct ath_softc *sc,
+	struct ath_node *an, struct mbuf *m, int *flushq)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_txq *txq;
+	u_int32_t txoplimit;
+	u_int pri;
+
+	*flushq = 0;
+
+	/*
+	 * If there is no frame to combine with and the txq has
+	 * fewer frames than the minimum required; then do not
+	 * attempt to aggregate this frame.
+	 */
+	pri = M_WME_GETAC(m);
+	txq = sc->sc_ac2q[pri];
+	if (an->an_ff_buf[pri] == NULL && txq->axq_depth < sc->sc_fftxqmin)
+		return 0;
+	/*
+	 * When not in station mode never aggregate a multicast
+	 * frame; this insures, for example, that a combined frame
+	 * does not require multiple encryption keys when using
+	 * 802.1x/WPA.
+	 */
+	if (ic->ic_opmode != IEEE80211_M_STA &&
+	    ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost))
+		return 0;		
+	/*
+	 * Consult the max bursting interval to insure a combined
+	 * frame fits within the TxOp window.
+	 */
+	txoplimit = IEEE80211_TXOP_TO_US(
+		ic->ic_wme.wme_chanParams.cap_wmeParams[pri].wmep_txopLimit);
+	if (txoplimit != 0 && ath_ff_approx_txtime(sc, an, m) > txoplimit) {
+		DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
+			"%s: FF TxOp violation\n", __func__);
+		if (an->an_ff_buf[pri] != NULL)
+			*flushq = 1;
+		return 0;
+	}
+	return 1;		/* try to aggregate */
+}
+
+/*
+ * Check if the supplied frame can be partnered with an existing
+ * or pending frame.  Return a reference to any frame that should be
+ * sent on return; otherwise return NULL.
+ */
+static struct mbuf *
+ath_ff_check(struct ath_softc *sc, struct ath_txq *txq,
+	struct ath_buf *bf, struct mbuf *m, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_buf *bfstaged;
+	int ff_flush, pri;
+
+	/*
+	 * Check if the supplied frame can be aggregated.
+	 *
+	 * NB: we use the txq lock to protect references to
+	 *     an->an_ff_txbuf in ath_ff_can_aggregate().
+	 */
+	ATH_TXQ_LOCK(txq);
+	pri = M_WME_GETAC(m);
+	if (ath_ff_can_aggregate(sc, an, m, &ff_flush)) {
+		struct ath_buf *bfstaged = an->an_ff_buf[pri];
+		if (bfstaged != NULL) {
+			/*
+			 * A frame is available for partnering; remove
+			 * it, chain it to this one, and encapsulate.
+			 */
+			an->an_ff_buf[pri] = NULL;
+			TAILQ_REMOVE(&txq->axq_stageq, bfstaged, bf_stagelist);
+			ATH_TXQ_UNLOCK(txq);
+
+			/* 
+			 * Chain mbufs and add FF magic.
+			 */
+			DPRINTF(sc, ATH_DEBUG_FF,
+				"[%s] aggregate fast-frame, age %u\n",
+				ether_sprintf(ni->ni_macaddr), txq->axq_curage);
+			m->m_nextpkt = NULL;
+			bfstaged->bf_m->m_nextpkt = m;
+			m = bfstaged->bf_m;
+			bfstaged->bf_m = NULL;
+			m->m_flags |= M_FF;
+			/*
+			 * Release the node reference held while
+			 * the packet sat on an_ff_buf[]
+			 */
+			bfstaged->bf_node = NULL;
+			ieee80211_free_node(ni);
+
+			/*
+			 * Return bfstaged to the free list.
+			 */
+			ATH_TXBUF_LOCK(sc);
+			STAILQ_INSERT_TAIL(&sc->sc_txbuf, bfstaged, bf_list);
+			ATH_TXBUF_UNLOCK(sc);
+
+			return m;		/* ready to go */
+		} else {
+			/*
+			 * No frame available, queue this frame to wait
+			 * for a partner.  Note that we hold the buffer
+			 * and a reference to the node; we need the
+			 * buffer in particular so we're certain we
+			 * can flush the frame at a later time.
+			 */
+			DPRINTF(sc, ATH_DEBUG_FF,
+				"[%s] stage fast-frame, age %u\n",
+				ether_sprintf(ni->ni_macaddr), txq->axq_curage);
+
+			bf->bf_m = m;
+			bf->bf_node = ni;	/* NB: held reference */
+			bf->bf_age = txq->axq_curage;
+			an->an_ff_buf[pri] = bf;
+			TAILQ_INSERT_HEAD(&txq->axq_stageq, bf, bf_stagelist);
+			ATH_TXQ_UNLOCK(txq);
+
+			return NULL;		/* consumed */
+		}
+	}
+	/*
+	 * Frame could not be aggregated, it needs to be returned
+	 * to the caller for immediate transmission.  In addition
+	 * we check if we should first flush a frame from the
+	 * staging queue before sending this one.
+	 *
+	 * NB: ath_ff_can_aggregate only marks ff_flush if a frame
+	 *     is present to flush.
+	 */
+	if (ff_flush) {
+		int pktlen;
+
+		bfstaged = an->an_ff_buf[pri];
+		an->an_ff_buf[pri] = NULL;
+		TAILQ_REMOVE(&txq->axq_stageq, bfstaged, bf_stagelist);
+		ATH_TXQ_UNLOCK(txq);
+
+		DPRINTF(sc, ATH_DEBUG_FF, "[%s] flush staged frame\n",
+			ether_sprintf(an->an_node.ni_macaddr));
+
+		/* encap and xmit */
+		bfstaged->bf_m = ieee80211_encap(ic, bfstaged->bf_m, ni);
+		if (bfstaged->bf_m == NULL) {
+			DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
+				"%s: discard, encap failure\n", __func__);
+			sc->sc_stats.ast_tx_encap++;
+			goto ff_flushbad;
+		}
+		pktlen = bfstaged->bf_m->m_pkthdr.len;
+		if (ath_tx_start(sc, ni, bfstaged, bfstaged->bf_m)) {
+			DPRINTF(sc, ATH_DEBUG_XMIT,
+				"%s: discard, xmit failure\n", __func__);
+	ff_flushbad:
+			/*
+			 * Unable to transmit frame that was on the staging
+			 * queue.  Reclaim the node reference and other
+			 * resources.
+			 */
+			if (ni != NULL)
+				ieee80211_free_node(ni);
+			bfstaged->bf_node = NULL;
+			if (bfstaged->bf_m != NULL) {
+				m_freem(bfstaged->bf_m);
+				bfstaged->bf_m = NULL;
+			}
+
+			ATH_TXBUF_LOCK(sc);
+			STAILQ_INSERT_TAIL(&sc->sc_txbuf, bfstaged, bf_list);
+			ATH_TXBUF_UNLOCK(sc);
+		} else {
+#if 0
+			ifp->if_opackets++;
+#endif
+		}
+	} else {
+		if (an->an_ff_buf[pri] != NULL) {
+			/* 
+			 * XXX: out-of-order condition only occurs for AP
+			 * mode and multicast.  There may be no valid way
+			 * to get this condition.
+			 */
+			DPRINTF(sc, ATH_DEBUG_FF, "[%s] out-of-order frame\n",
+				ether_sprintf(an->an_node.ni_macaddr));
+			/* XXX stat */
+		}
+		ATH_TXQ_UNLOCK(txq);
+	}
+	return m;
+}
+
+/*
+ * Cleanup driver resources when we run out of buffers
+ * while processing fragments; return the tx buffers
+ * allocated and drop node references.
+ */
+static void
+ath_txfrag_cleanup(struct ath_softc *sc,
+	ath_bufhead *frags, struct ieee80211_node *ni)
+{
+	struct ath_buf *bf, *next;
+
+	ATH_TXBUF_LOCK_ASSERT(sc);
+
+	STAILQ_FOREACH_SAFE(bf, frags, bf_list, next) {
+		/* NB: bf assumed clean */
+		STAILQ_REMOVE_HEAD(frags, bf_list);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ieee80211_node_decref(ni);
+	}
+}
+
+/*
+ * Setup xmit of a fragmented frame.  Allocate a buffer
+ * for each frag and bump the node reference count to
+ * reflect the held reference to be setup by ath_tx_start.
+ */
+static int
+ath_txfrag_setup(struct ath_softc *sc, ath_bufhead *frags,
+	struct mbuf *m0, struct ieee80211_node *ni)
+{
+	struct mbuf *m;
+	struct ath_buf *bf;
+
+	ATH_TXBUF_LOCK(sc);
+	for (m = m0->m_nextpkt; m != NULL; m = m->m_nextpkt) {
+		bf = STAILQ_FIRST(&sc->sc_txbuf);
+		if (bf == NULL) {	/* out of buffers, cleanup */
+			ath_txfrag_cleanup(sc, frags, ni);
+			break;
+		}
+		STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+		ieee80211_node_incref(ni);
+		STAILQ_INSERT_TAIL(frags, bf, bf_list);
+	}
+	ATH_TXBUF_UNLOCK(sc);
+
+	return !STAILQ_EMPTY(frags);
+}
+
 static void
 ath_start(struct ifnet *ifp)
 {
@@ -1138,9 +1529,12 @@
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ieee80211_node *ni;
 	struct ath_buf *bf;
-	struct mbuf *m;
+	struct mbuf *m, *next;
 	struct ieee80211_frame *wh;
 	struct ether_header *eh;
+	struct ath_txq *txq;
+	ath_bufhead frags;
+	int pri;
 
 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid)
 		return;
@@ -1187,7 +1581,14 @@
 				ATH_TXBUF_UNLOCK(sc);
 				break;
 			}
-			/* 
+			/*
+			 * Cancel any background scan.
+			 */
+			if (ic->ic_flags & IEEE80211_F_SCAN)
+				ieee80211_cancel_scan(ic);
+
+			STAILQ_INIT(&frags);
+			/*
 			 * Find the node for the destination so we can do
 			 * things like power save and fast frames aggregation.
 			 */
@@ -1211,7 +1612,7 @@
 				 * to the 802.11 layer and continue.  We'll get
 				 * the frame back when the time is right.
 				 */
-				ieee80211_pwrsave(ic, ni, m);
+				ieee80211_pwrsave(ni, m);
 				goto reclaim;
 			}
 			/* calculate priority so we can find the tx queue */
@@ -1222,6 +1623,28 @@
 				m_freem(m);
 				goto bad;
 			}
+			pri = M_WME_GETAC(m);
+			txq = sc->sc_ac2q[pri];
+			if (ni->ni_ath_flags & IEEE80211_NODE_FF) {
+				/*
+				 * Check queue length; if too deep drop this
+				 * frame (tail drop considered good).
+				 */
+				if (txq->axq_depth >= sc->sc_fftxqmax) {
+					DPRINTF(sc, ATH_DEBUG_FF,
+					    "[%s] tail drop on q %u depth %u\n",
+					    ether_sprintf(ni->ni_macaddr),
+					    txq->axq_qnum, txq->axq_depth);
+					sc->sc_stats.ast_tx_qfull++;
+					m_freem(m);
+					goto reclaim;
+				}
+				m = ath_ff_check(sc, txq, bf, m, ni);
+				if (m == NULL) {
+					/* NB: ni ref & bf held on stageq */
+					continue;
+				}
+			}
 			ifp->if_opackets++;
 			BPF_MTAP(ifp, m);
 			/*
@@ -1235,6 +1658,20 @@
 				sc->sc_stats.ast_tx_encap++;
 				goto bad;
 			}
+			/*
+			 * 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...
+			 */
+			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__);
+				ic->ic_stats.is_tx_nobuf++;	/* XXX */
+				ath_freetx(m);
+				goto bad;
+			}
 		} else {
 			/*
 			 * Hack!  The referenced node pointer is in the
@@ -1265,20 +1702,63 @@
 			sc->sc_stats.ast_tx_mgmt++;
 		}
 
+	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.
+		 */
+		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_TAIL(&sc->sc_txbuf, bf, bf_list);
+			ath_txfrag_cleanup(sc, &frags, ni);
 			ATH_TXBUF_UNLOCK(sc);
 			if (ni != NULL)
 				ieee80211_free_node(ni);
 			continue;
 		}
+		if (next != NULL) {
+			/*
+			 * Beware of state changing between frags.
+			 * XXX check sta power-save state?
+			 */
+			if (ic->ic_state != IEEE80211_S_RUN) {
+				DPRINTF(sc, ATH_DEBUG_XMIT,
+				    "%s: flush fragmented packet, state %s\n",
+				    __func__,
+				    ieee80211_state_name[ic->ic_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;
+		}
 
-		sc->sc_tx_timer = 5;
-		ifp->if_timer = 1;
+		ifp->if_timer = 5;
+		ic->ic_lastdata = ticks;
+#if 0
+		/*
+		 * Flush stale frames from the fast-frame staging queue.
+		 */
+		if (ic->ic_opmode != IEEE80211_M_STA)
+			ath_ff_stageq_flush(sc, txq, ath_ff_ageflushtestdone);
+#endif
 	}
 }
 
@@ -1304,7 +1784,7 @@
 		} else
 			sc->sc_opmode = ic->ic_opmode;
 		if (IS_UP(ifp))
-			ath_init(ifp->if_softc);	/* XXX lose error */
+			ath_init(sc);		/* XXX lose error */
 		error = 0;
 	}
 	return error;
@@ -1313,7 +1793,7 @@
 
 #ifdef ATH_DEBUG
 static void
-ath_keyprint(const char *tag, u_int ix,
+ath_keyprint(struct ath_softc *sc, const char *tag, u_int ix,
 	const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
 {
 	static const char *ciphers[] = {
@@ -1331,9 +1811,16 @@
 		printf("%02x", hk->kv_val[i]);
 	printf(" mac %s", ether_sprintf(mac));
 	if (hk->kv_type == HAL_CIPHER_TKIP) {
-		printf(" mic ");
+		printf(" %s ", sc->sc_splitmic ? "mic" : "rxmic");
 		for (i = 0; i < sizeof(hk->kv_mic); i++)
 			printf("%02x", hk->kv_mic[i]);
+#if HAL_ABI_VERSION > 0x06052200
+		if (!sc->sc_splitmic) {
+			printf(" txmic ");
+			for (i = 0; i < sizeof(hk->kv_txmic); i++)
+				printf("%02x", hk->kv_txmic[i]);
+		}
+#endif
 	}
 	printf("\n");
 }
@@ -1354,22 +1841,35 @@
 
 	KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP,
 		("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher));
-	KASSERT(sc->sc_splitmic, ("key cache !split"));
 	if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
-		/*
-		 * TX key goes at first index, RX key at the rx index.
-		 * The hal handles the MIC keys at index+64.
-		 */
-		memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
-		KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
-		if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid))
-			return 0;
-
-		memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
-		KEYPRINTF(sc, k->wk_keyix+32, hk, mac);
-		/* XXX delete tx key on failure? */
-		return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac);
-	} else if (k->wk_flags & IEEE80211_KEY_XR) {
+		if (sc->sc_splitmic) {
+			/*
+			 * TX key goes at first index, RX key at the rx index.
+			 * The hal handles the MIC keys at index+64.
+			 */
+			memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
+			KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
+			if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid))
+				return 0;
+
+			memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
+			KEYPRINTF(sc, k->wk_keyix+32, hk, mac);
+			/* XXX delete tx key on failure? */
+			return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac);
+		} else {
+			/*
+			 * Room for both TX+RX MIC keys in one key cache
+			 * slot, just set key at the first index; the hal
+			 * will handle the reset.
+			 */
+			memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
+#if HAL_ABI_VERSION > 0x06052200
+			memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic));
+#endif
+			KEYPRINTF(sc, k->wk_keyix, hk, mac);
+			return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
+		}
+	} else if (k->wk_flags & IEEE80211_KEY_XR) {
 		/*
 		 * TX/RX key goes at first index.
 		 * The hal handles the MIC keys are index+64.
@@ -1437,8 +1937,7 @@
 		mac = mac0;
 
 	if (hk.kv_type == HAL_CIPHER_TKIP &&
-	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
-	    sc->sc_splitmic) {
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
 		return ath_keyset_tkip(sc, k, &hk, mac);
 	} else {
 		KEYPRINTF(sc, k->wk_keyix, &hk, mac);
@@ -1503,6 +2002,54 @@
 }
 
 /*
+ * Allocate tx/rx key slots for TKIP.  We allocate two slots for
+ * each key, one for decrypt/encrypt and the other for the MIC.
+ */
+static u_int16_t
+key_alloc_pair(struct ath_softc *sc,
+	ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	u_int i, keyix;
+
+	KASSERT(!sc->sc_splitmic, ("key cache split"));
+	/* XXX could optimize */
+	for (i = 0; i < N(sc->sc_keymap)/4; i++) {
+		u_int8_t b = sc->sc_keymap[i];
+		if (b != 0xff) {
+			/*
+			 * One or more slots in this byte are free.
+			 */
+			keyix = i*NBBY;
+			while (b & 1) {
+		again:
+				keyix++;
+				b >>= 1;
+			}
+			if (isset(sc->sc_keymap, keyix+64)) {
+				/* full pair unavailable */
+				/* XXX statistic */
+				if (keyix == (i+1)*NBBY) {
+					/* no slots were appropriate, advance */
+					continue;
+				}
+				goto again;
+			}
+			setbit(sc->sc_keymap, keyix);
+			setbit(sc->sc_keymap, keyix+64);
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+				"%s: key pair %u,%u\n",
+				__func__, keyix, keyix+64);
+			*txkeyix = *rxkeyix = keyix;
+			return 1;
+		}
+	}
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__);
+	return 0;
+#undef N
+}
+
+/*
  * Allocate a single key cache slot.
  */
 static int
@@ -1587,8 +2134,11 @@
 	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
 		return key_alloc_single(sc, keyix, rxkeyix);
 	} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
-	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
-		return key_alloc_2pair(sc, keyix, rxkeyix);
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
+		if (sc->sc_splitmic)
+			return key_alloc_2pair(sc, keyix, rxkeyix);
+		else
+			return key_alloc_pair(sc, keyix, rxkeyix);
 	} else {
 		return key_alloc_single(sc, keyix, rxkeyix);
 	}
@@ -1621,11 +2171,13 @@
 		 */
 		clrbit(sc->sc_keymap, keyix);
 		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
-		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
-		    sc->sc_splitmic) {
+		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
 			clrbit(sc->sc_keymap, keyix+64);	/* TX key MIC */
-			clrbit(sc->sc_keymap, keyix+32);	/* RX key */
-			clrbit(sc->sc_keymap, keyix+32+64);	/* RX key MIC */
+			if (sc->sc_splitmic) {
+				/* +32 for RX key, +32+64 for RX key MIC */
+				clrbit(sc->sc_keymap, keyix+32);
+				clrbit(sc->sc_keymap, keyix+32+64);
+			}
 		}
 	}
 	return 1;
@@ -1692,9 +2244,11 @@
  *   - when operating in station mode for collecting rssi data when
  *     the station is otherwise quiet, or
  *   - when scanning
+ * o accept control frames:
+ *   - when in monitor mode
  */
 static u_int32_t
-ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state)
+ath_calcrxfilter(struct ath_softc *sc)
 {
 #define	RX_FILTER_PRESERVE	(HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR)
 	struct ieee80211com *ic = &sc->sc_ic;
@@ -1711,8 +2265,10 @@
 		rfilt |= HAL_RX_FILTER_PROM;
 	if (ic->ic_opmode == IEEE80211_M_STA ||
 	    ic->ic_opmode == IEEE80211_M_IBSS ||
-	    state == IEEE80211_S_SCAN)
+	    sc->sc_scanning)
 		rfilt |= HAL_RX_FILTER_BEACON;
+	if (ic->ic_opmode == IEEE80211_M_MONITOR)
+		rfilt |= HAL_RX_FILTER_CONTROL;
 	return rfilt;
 #undef RX_FILTER_PRESERVE
 }
@@ -1728,7 +2284,7 @@
 	struct ifmultiaddr *ifma;
 
 	/* configure rx filter */
-	rfilt = ath_calcrxfilter(sc, ic->ic_state);
+	rfilt = ath_calcrxfilter(sc);
 	ath_hal_setrxfilter(ah, rfilt);
 
 	/* configure operational mode */
@@ -1778,11 +2334,28 @@
 {
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ath_hal *ah = sc->sc_ah;
+	u_int usec;
 
-	if (ic->ic_flags & IEEE80211_F_SHSLOT)
-		ath_hal_setslottime(ah, HAL_SLOT_TIME_9);
-	else
-		ath_hal_setslottime(ah, HAL_SLOT_TIME_20);
+	if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan))
+		usec = 13;
+	else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan))
+		usec = 21;
+	else if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
+		/* honor short/long slot time only in 11g */
+		/* XXX shouldn't honor on pure g or turbo g channel */
+		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+			usec = HAL_SLOT_TIME_9;
+		else
+			usec = HAL_SLOT_TIME_20;
+	} else
+		usec = HAL_SLOT_TIME_9;
+
+	DPRINTF(sc, ATH_DEBUG_RESET,
+	    "%s: chan %u MHz flags 0x%x %s slot, %u usec\n",
+	    __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags,
+	    ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec);
+
+	ath_hal_setslottime(ah, usec);
 	sc->sc_updateslot = OK;
 }
 
@@ -1871,7 +2444,6 @@
 static int
 ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
 {
-	struct ieee80211com *ic = ni->ni_ic;
 	struct ath_buf *bf;
 	struct mbuf *m;
 	int error;
@@ -1887,7 +2459,7 @@
 	 * we assume the mbuf routines will return us something
 	 * with this alignment (perhaps should assert).
 	 */
-	m = ieee80211_beacon_alloc(ic, ni, &sc->sc_boff);
+	m = ieee80211_beacon_alloc(ni, &sc->sc_boff);
 	if (m == NULL) {
 		DPRINTF(sc, ATH_DEBUG_BEACON, "%s: cannot get mbuf\n",
 			__func__);
@@ -1924,7 +2496,7 @@
 	const HAL_RATE_TABLE *rt;
 	u_int8_t rix, rate;
 
-	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: m %p len %u\n",
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n",
 		__func__, m, m->m_len);
 
 	/* setup descriptors */
@@ -1982,6 +2554,29 @@
 #undef USE_SHPREAMBLE
 }
 
+static void
+ath_beacon_update(struct ieee80211com *ic, int item)
+{
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+	struct ieee80211_beacon_offsets *bo = &sc->sc_boff;
+
+	setbit(bo->bo_flags, item);
+}
+
+/*
+ * Append the contents of src to dst; both queues
+ * are assumed to be locked.
+ */
+static void
+ath_txqmove(struct ath_txq *dst, struct ath_txq *src)
+{
+	STAILQ_CONCAT(&dst->axq_q, &src->axq_q);
+	dst->axq_link = src->axq_link;
+	src->axq_link = NULL;
+	dst->axq_depth += src->axq_depth;
+	src->axq_depth = 0;
+}
+
 /*
  * Transmit a beacon frame at SWBA.  Dynamic updates to the
  * frame contents are done as needed and the slot time is
@@ -1995,8 +2590,9 @@
 	struct ieee80211_node *ni = bf->bf_node;
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ath_hal *ah = sc->sc_ah;
+	struct ath_txq *cabq = sc->sc_cabq;
 	struct mbuf *m;
-	int ncabq, error, otherant;
+	int ncabq, nmcastq, error, otherant;
 
 	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n",
 		__func__, pending);
@@ -2010,14 +2606,14 @@
 	}
 	/*
 	 * Check if the previous beacon has gone out.  If
-	 * not don't 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.
+	 * 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_PROC,
+		DPRINTF(sc, ATH_DEBUG_BEACON,
 			"%s: missed %u consecutive beacons\n",
 			__func__, sc->sc_bmisscount);
 		if (sc->sc_bmisscount > 3)		/* NB: 3 is a guess */
@@ -2038,8 +2634,9 @@
 	 * of the TIM bitmap).
 	 */
 	m = bf->bf_m;
-	ncabq = sc->sc_cabq->axq_depth;
-	if (ieee80211_beacon_update(ic, bf->bf_node, &sc->sc_boff, m, ncabq)) {
+	nmcastq = sc->sc_mcastq.axq_depth;
+	ncabq = ath_hal_numtxpending(ah, cabq->axq_qnum);
+	if (ieee80211_beacon_update(bf->bf_node, &sc->sc_boff, m, ncabq+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,
@@ -2052,6 +2649,18 @@
 			return;
 		}
 	}
+	if (ncabq && (sc->sc_boff.bo_tim[4] & 1)) {
+		/*
+		 * CABQ traffic from the previous DTIM is still pending.
+		 * This is ok for now but when there are multiple vap's
+		 * and we are using staggered beacons we'll want to drain
+		 * the cabq before loading frames for the different vap.
+		 */
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+		    "%s: cabq did not drain, mcastq %u cabq %u/%u\n",
+		    __func__, nmcastq, ncabq, cabq->axq_depth);
+		sc->sc_stats.ast_cabq_busy++;
+	}
 
 	/*
 	 * Handle slot time change when a non-ERP station joins/leaves
@@ -2098,8 +2707,31 @@
 	 * Enable the CAB queue before the beacon queue to
 	 * insure cab frames are triggered by this beacon.
 	 */
-	if (sc->sc_boff.bo_tim[4] & 1)		/* NB: only at DTIM */
-		ath_hal_txstart(ah, sc->sc_cabq->axq_qnum);
+	if (sc->sc_boff.bo_tim_len && (sc->sc_boff.bo_tim[4] & 1)) {
+		/* NB: only at DTIM */
+		ATH_TXQ_LOCK(cabq);
+		ATH_TXQ_LOCK(&sc->sc_mcastq);
+		if (nmcastq) {
+			struct ath_buf *bfm;
+
+			/*
+			 * Move frames from the s/w mcast q to the h/w cab q.
+			 */
+			bfm = STAILQ_FIRST(&sc->sc_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, &sc->sc_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(&sc->sc_mcastq);
+	}
 	ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
 	ath_hal_txstart(ah, sc->sc_bhalq);
 	DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
@@ -2235,9 +2867,7 @@
 #endif
 		/*
 		 * Calculate the number of consecutive beacons to miss
-		 * before taking a BMISS interrupt.  The configuration
-		 * is specified in ms, so we need to convert that to
-		 * TU's and then calculate based on the beacon interval.
+		 * before taking a BMISS interrupt.
 		 * Note that we clamp the result to at most 10 beacons.
 		 */
 		bs.bs_bmissthreshold = ic->ic_bmissthreshold;
@@ -2260,7 +2890,7 @@
 		if (bs.bs_sleepduration > bs.bs_dtimperiod)
 			bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
 
-		DPRINTF(sc, ATH_DEBUG_BEACON, 
+		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
@@ -2359,14 +2989,14 @@
 	/*
 	 * Setup DMA descriptor area.
 	 */
-	error = bus_dma_tag_create(NULL,	/* parent */
+	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev),	/* parent */
 		       PAGE_SIZE, 0,		/* alignment, bounds */
 		       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
 		       BUS_SPACE_MAXADDR,	/* highaddr */
 		       NULL, NULL,		/* filter, filterarg */
 		       dd->dd_desc_len,		/* maxsize */
 		       1,			/* nsegments */
-		       BUS_SPACE_MAXADDR,	/* maxsegsize */
+		       dd->dd_desc_len,		/* maxsegsize */
 		       BUS_DMA_ALLOCNOW,	/* flags */
 		       NULL,			/* lockfunc */
 		       NULL,			/* lockarg */
@@ -2385,7 +3015,8 @@
 	}
 
 	error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc,
-				 BUS_DMA_NOWAIT, &dd->dd_dmamap);
+				 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);
@@ -2552,7 +3183,7 @@
 	sc->sc_node_free(ni);
 }
 
-static u_int8_t
+static int8_t
 ath_node_getrssi(const struct ieee80211_node *ni)
 {
 #define	HAL_EP_RND(x, mul) \
@@ -2568,11 +3199,26 @@
 		rssi = HAL_EP_RND(avgrssi, HAL_RSSI_EP_MULTIPLIER);
 	else
 		rssi = ni->ni_rssi;
-	/* NB: theoretically we shouldn't need this, but be paranoid */
 	return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
 #undef HAL_EP_RND
 }
 
+static void
+ath_node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_CHANNEL hchan;
+
+	*rssi = ath_node_getrssi(ni);
+	if (ni->ni_chan != IEEE80211_CHAN_ANYC) {
+		ath_mapchan(&hchan, ni->ni_chan);
+		*noise = ath_hal_getchannoise(ah, &hchan);
+	} else
+		*noise = -95;		/* nominally correct */
+}
+
 static int
 ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
 {
@@ -2635,7 +3281,6 @@
 	ds = bf->bf_desc;
 	ds->ds_link = bf->bf_daddr;	/* link to self */
 	ds->ds_data = bf->bf_segs[0].ds_addr;
-	ds->ds_vdata = mtod(m, void *);	/* for radar */
 	ath_hal_setuprxdesc(ah, ds
 		, m->m_len		/* buffer size */
 		, 0
@@ -2666,7 +3311,7 @@
 static void
 ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
 	struct ieee80211_node *ni,
-	int subtype, int rssi, u_int32_t rstamp)
+	int subtype, int rssi, int noise, u_int32_t rstamp)
 {
 	struct ath_softc *sc = ic->ic_ifp->if_softc;
 
@@ -2674,7 +3319,7 @@
 	 * Call up first so subsequent work can use information
 	 * potentially stored in the node (e.g. for ibss merge).
 	 */
-	sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, rstamp);
+	sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, noise, rstamp);
 	switch (subtype) {
 	case IEEE80211_FC0_SUBTYPE_BEACON:
 		/* update rssi statistics for use by the hal */
@@ -2733,8 +3378,9 @@
 
 static int
 ath_rx_tap(struct ath_softc *sc, struct mbuf *m,
-	const struct ath_desc *ds, u_int64_t tsf, int16_t nf)
+	const struct ath_rx_status *rs, u_int64_t tsf, int16_t nf)
 {
+#define	CHANNEL_HT	(CHANNEL_HT20|CHANNEL_HT40PLUS|CHANNEL_HT40MINUS)
 	u_int8_t rix;
 
 	KASSERT(sc->sc_drvbpf != NULL, ("no tap"));
@@ -2748,21 +3394,41 @@
 		sc->sc_stats.ast_rx_tooshort++;
 		return 0;
 	}
-	sc->sc_rx_th.wr_tsf = htole64(
-		ath_extend_tsf(ds->ds_rxstat.rs_tstamp, tsf));
-	rix = ds->ds_rxstat.rs_rate;
+	rix = 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;
-	if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
+#if HAL_ABI_VERSION >= 0x07050400
+	if (sc->sc_curchan.channelFlags & CHANNEL_HT) {
+		/*
+		 * For HT operation we must specify the channel
+		 * attributes for each packet since they vary.
+		 * We deduce this by from HT40 bit in the rx
+		 * status and the MCS/legacy rate bit.
+		 */
+		sc->sc_rx_th.wr_chan_flags &= ~IEEE80211_CHAN_HT;
+		if (sc->sc_rx_th.wr_rate & 0x80) {	/* HT rate */
+			/* XXX 40U/40D */
+			sc->sc_rx_th.wr_chan_flags |=
+			    (rs->rs_flags & HAL_RX_2040) ?
+				IEEE80211_CHAN_HT40U : IEEE80211_CHAN_HT20;
+			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(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_rate = sc->sc_hwmap[rix].ieeerate;
-	sc->sc_rx_th.wr_antsignal = ds->ds_rxstat.rs_rssi + nf;
+	sc->sc_rx_th.wr_antsignal = rs->rs_rssi + nf;
 	sc->sc_rx_th.wr_antnoise = nf;
-	sc->sc_rx_th.wr_antenna = ds->ds_rxstat.rs_antenna;
+	sc->sc_rx_th.wr_antenna = rs->rs_antenna;
 
 	bpf_mtap2(sc->sc_drvbpf, &sc->sc_rx_th, sc->sc_rx_th_len, m);
 
 	return 1;
+#undef CHANNEL_HT
 }
 
 static void
@@ -2777,6 +3443,7 @@
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ath_hal *ah = sc->sc_ah;
 	struct ath_desc *ds;
+	struct ath_rx_status *rs;
 	struct mbuf *m;
 	struct ieee80211_node *ni;
 	struct ath_node *an;
@@ -2786,7 +3453,6 @@
 	int16_t nf;
 	u_int64_t tsf;
 
-	NET_LOCK_GIANT();		/* XXX */
 
 	DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
 	ngood = 0;
@@ -2826,39 +3492,28 @@
 		 * 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));
+				bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs);
 #ifdef ATH_DEBUG
 		if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
-			ath_printrxbuf(bf, 0, status == HAL_OK); 
+			ath_printrxbuf(bf, 0, status == HAL_OK);
 #endif
 		if (status == HAL_EINPROGRESS)
 			break;
 		STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
-		if (ds->ds_rxstat.rs_more) {
-			/*
-			 * Frame spans multiple descriptors; this
-			 * cannot happen yet as we don't support
-			 * jumbograms.  If not in monitor mode,
-			 * discard the frame.
-			 */
-			if (ic->ic_opmode != IEEE80211_M_MONITOR) {
-				sc->sc_stats.ast_rx_toobig++;
-				goto rx_next;
-			}
-			/* fall thru for monitor mode handling... */
-		} else if (ds->ds_rxstat.rs_status != 0) {
-			if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
+		if (rs->rs_status != 0) {
+			if (rs->rs_status & HAL_RXERR_CRC)
 				sc->sc_stats.ast_rx_crcerr++;
-			if (ds->ds_rxstat.rs_status & HAL_RXERR_FIFO)
+			if (rs->rs_status & HAL_RXERR_FIFO)
 				sc->sc_stats.ast_rx_fifoerr++;
-			if (ds->ds_rxstat.rs_status & HAL_RXERR_PHY) {
+			if (rs->rs_status & HAL_RXERR_PHY) {
 				sc->sc_stats.ast_rx_phyerr++;
-				phyerr = ds->ds_rxstat.rs_phyerr & 0x1f;
+				phyerr = rs->rs_phyerr & 0x1f;
 				sc->sc_stats.ast_rx_phy[phyerr]++;
-				goto rx_next;
+				goto rx_error;	/* NB: don't count in ierrors */
 			}
-			if (ds->ds_rxstat.rs_status & HAL_RXERR_DECRYPT) {
+			if (rs->rs_status & HAL_RXERR_DECRYPT) {
 				/*
 				 * Decrypt error.  If the error occurred
 				 * because there was no hardware key, then
@@ -2869,18 +3524,18 @@
 				 *
 				 * XXX do key cache faulting
 				 */
-				if (ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID)
+				if (rs->rs_keyix == HAL_RXKEYIX_INVALID)
 					goto rx_accept;
 				sc->sc_stats.ast_rx_badcrypt++;
 			}
-			if (ds->ds_rxstat.rs_status & HAL_RXERR_MIC) {
+			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 notifcation.
 				 */
 				/* XXX frag's and qos frames */
-				len = ds->ds_rxstat.rs_datalen;
+				len = rs->rs_datalen;
 				if (len >= sizeof (struct ieee80211_frame)) {
 					bus_dmamap_sync(sc->sc_dmat,
 					    bf->bf_dmamap,
@@ -2888,26 +3543,33 @@
 					ieee80211_notify_michael_failure(ic,
 					    mtod(m, struct ieee80211_frame *),
 					    sc->sc_splitmic ?
-					        ds->ds_rxstat.rs_keyix-32 :
-					        ds->ds_rxstat.rs_keyix
+					        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 (sc->sc_drvbpf != NULL &&
-			    (ds->ds_rxstat.rs_status & sc->sc_monpass)) {
+			if (bpf_peers_present(sc->sc_drvbpf) &&
+			    (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 = ds->ds_rxstat.rs_datalen;
+				len = rs->rs_datalen;
 				m->m_pkthdr.len = m->m_len = len;
-				(void) ath_rx_tap(sc, m, ds, tsf, nf);
+				(void) ath_rx_tap(sc, m, rs, tsf, nf);
 			}
 			/* XXX pass MIC errors up for s/w reclaculation */
 			goto rx_next;
@@ -2916,7 +3578,7 @@
 		/*
 		 * Sync and unmap the frame.  At this point we're
 		 * committed to passing the mbuf somewhere so clear
-		 * bf_m; this means a new sk_buff must be allocated
+		 * bf_m; this means a new mbuf must be allocated
 		 * when the rx descriptor is setup again to receive
 		 * another frame.
 		 */
@@ -2925,13 +3587,47 @@
 		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
 		bf->bf_m = NULL;
 
-		m->m_pkthdr.rcvif = ifp;
-		len = ds->ds_rxstat.rs_datalen;
-		m->m_pkthdr.len = m->m_len = len;
+		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;
+		}
 
-		sc->sc_stats.ast_ant_rx[ds->ds_rxstat.rs_antenna]++;
+		sc->sc_stats.ast_ant_rx[rs->rs_antenna]++;
 
-		if (sc->sc_drvbpf != NULL && !ath_rx_tap(sc, m, ds, tsf, nf)) {
+		if (bpf_peers_present(sc->sc_drvbpf) &&
+		    !ath_rx_tap(sc, m, rs, tsf, nf)) {
 			m_freem(m);		/* XXX reclaim */
 			goto rx_next;
 		}
@@ -2949,9 +3645,9 @@
 		}
 
 		if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
-			ieee80211_dump_pkt(mtod(m, caddr_t), len,
-				   sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate,
-				   ds->ds_rxstat.rs_rssi);
+			ieee80211_dump_pkt(ic, mtod(m, caddr_t), len,
+				   sc->sc_hwmap[rs->rs_rate].ieeerate,
+				   rs->rs_rssi);
 		}
 
 		m_adj(m, -IEEE80211_CRC_LEN);
@@ -2963,19 +3659,19 @@
 		 */
 		ni = ieee80211_find_rxnode_withkey(ic,
 			mtod(m, const struct ieee80211_frame_min *),
-			ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID ?
-				IEEE80211_KEYIX_NONE : ds->ds_rxstat.rs_keyix);
+			rs->rs_keyix == HAL_RXKEYIX_INVALID ?
+				IEEE80211_KEYIX_NONE : rs->rs_keyix);
 		/*
 		 * Track rx rssi and do any rx antenna management.
 		 */
 		an = ATH_NODE(ni);
-		ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi);
-		ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, ds->ds_rxstat.rs_rssi);
+		ATH_RSSI_LPF(an->an_avgrssi, rs->rs_rssi);
+		ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi);
 		/*
 		 * Send frame up for processing.
 		 */
 		type = ieee80211_input(ic, m, ni,
-			ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
+			rs->rs_rssi, nf, rs->rs_tstamp);
 		ieee80211_free_node(ni);
 		if (sc->sc_diversity) {
 			/*
@@ -2983,10 +3679,9 @@
 			 * antenna if diversity chooses the other antenna 3
 			 * times in a row.
 			 */
-			if (sc->sc_defant != ds->ds_rxstat.rs_antenna) {
+			if (sc->sc_defant != rs->rs_antenna) {
 				if (++sc->sc_rxotherant >= 3)
-					ath_setdefantenna(sc,
-						ds->ds_rxstat.rs_antenna);
+					ath_setdefantenna(sc, rs->rs_antenna);
 			} else
 				sc->sc_rxotherant = 0;
 		}
@@ -2998,7 +3693,7 @@
 			 * periodic beacon frames to trigger the poll event.
 			 */
 			if (type == IEEE80211_FC0_TYPE_DATA) {
-				sc->sc_rxrate = ds->ds_rxstat.rs_rate;
+				sc->sc_rxrate = rs->rs_rate;
 				ath_led_event(sc, ATH_LED_RX);
 			} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
 				ath_led_event(sc, ATH_LED_POLL);
@@ -3009,7 +3704,7 @@
 		 * This assumes the rx key is always setup when associated.
 		 */
 		if (ic->ic_opmode == IEEE80211_M_STA &&
-		    ds->ds_rxstat.rs_keyix != HAL_RXKEYIX_INVALID)
+		    rs->rs_keyix != HAL_RXKEYIX_INVALID)
 			ngood++;
 rx_next:
 		STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
@@ -3017,15 +3712,30 @@
 
 	/* rx signal state monitoring */
 	ath_hal_rxmonitor(ah, &sc->sc_halstats, &sc->sc_curchan);
-	if (ath_hal_radar_event(ah))
-		taskqueue_enqueue(sc->sc_tq, &sc->sc_radartask);
 	if (ngood)
 		sc->sc_lastrx = tsf;
 
-	NET_UNLOCK_GIANT();		/* XXX */
+	/* NB: may want to check mgtq too */
+	if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 &&
+	    !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_depth = 0;
+	txq->axq_intrcnt = 0;
+	txq->axq_link = NULL;
+	STAILQ_INIT(&txq->axq_q);
+	ATH_TXQ_LOCK_INIT(sc, txq);
+	TAILQ_INIT(&txq->axq_stageq);
+	txq->axq_curage = 0;
+}
+
 /*
  * Setup a h/w transmit queue.
  */
@@ -3071,14 +3781,7 @@
 		return NULL;
 	}
 	if (!ATH_TXQ_SETUP(sc, qnum)) {
-		struct ath_txq *txq = &sc->sc_txq[qnum];
-
-		txq->axq_qnum = qnum;
-		txq->axq_depth = 0;
-		txq->axq_intrcnt = 0;
-		txq->axq_link = NULL;
-		STAILQ_INIT(&txq->axq_q);
-		ATH_TXQ_LOCK_INIT(sc, txq);
+		ath_txq_init(sc, &sc->sc_txq[qnum], qnum);
 		sc->sc_txqsetup |= 1<<qnum;
 	}
 	return &sc->sc_txq[qnum];
@@ -3131,7 +3834,7 @@
 	ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi);
 	qi.tqi_aifs = wmep->wmep_aifsn;
 	qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
-	qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);	
+	qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
 	qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit);
 
 	if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) {
@@ -3185,6 +3888,7 @@
 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
 		if (ATH_TXQ_SETUP(sc, i))
 			ath_tx_cleanupq(sc, &sc->sc_txq[i]);
+	ATH_TXQ_LOCK_DESTROY(&sc->sc_mcastq);
 }
 
 /*
@@ -3285,6 +3989,142 @@
 	return 0;		/* NB: lowest rate */
 }
 
+/*
+ * Reclaim mbuf resources.  For fragmented frames we
+ * need to claim each frag chained with m_nextpkt.
+ */
+static void
+ath_freetx(struct mbuf *m)
+{
+	struct mbuf *next;
+
+	do {
+		next = m->m_nextpkt;
+		m->m_nextpkt = NULL;
+		m_freem(m);
+	} while ((m = next) != NULL);
+}
+
+static int
+ath_tx_dmasetup(struct ath_softc *sc, struct ath_buf *bf, struct mbuf *m0)
+{
+	struct mbuf *m;
+	int error;
+
+	/*
+	 * Load the DMA map so any coalescing is done.  This
+	 * also calculates the number of descriptors we need.
+	 */
+	error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
+				     bf->bf_segs, &bf->bf_nseg,
+				     BUS_DMA_NOWAIT);
+	if (error == EFBIG) {
+		/* XXX packet requires too many descriptors */
+		bf->bf_nseg = ATH_TXDESC+1;
+	} else if (error != 0) {
+		sc->sc_stats.ast_tx_busdma++;
+		ath_freetx(m0);
+		return error;
+	}
+	/*
+	 * Discard null packets and check for packets that
+	 * 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 */
+		sc->sc_stats.ast_tx_linear++;
+		m = ath_defrag(m0, M_DONTWAIT, ATH_TXDESC);
+		if (m == NULL) {
+			ath_freetx(m0);
+			sc->sc_stats.ast_tx_nombuf++;
+			return ENOMEM;
+		}
+		m0 = m;
+		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
+					     bf->bf_segs, &bf->bf_nseg,
+					     BUS_DMA_NOWAIT);
+		if (error != 0) {
+			sc->sc_stats.ast_tx_busdma++;
+			ath_freetx(m0);
+			return error;
+		}
+		KASSERT(bf->bf_nseg <= ATH_TXDESC,
+		    ("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++;
+		ath_freetx(m0);
+		return EIO;
+	}
+	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n",
+		__func__, m0, m0->m_pkthdr.len);
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+	bf->bf_m = m0;
+
+	return 0;
+}
+
+static void
+ath_tx_handoff(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_desc *ds, *ds0;
+	int i;
+
+	/*
+	 * 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;
+		if (i == bf->bf_nseg - 1)
+			ds->ds_link = 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 */
+			, i == bf->bf_nseg - 1	/* last segment */
+			, ds0			/* first descriptor */
+		);
+		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]);
+	}
+	/*
+	 * Insert the frame on the outbound list and pass it on
+	 * to the hardware.  Multicast frames buffered for power
+	 * save stations and transmit from the CAB queue are stored
+	 * on a s/w only queue and loaded on to the CAB queue in
+	 * the SWBA handler since frames only go out on DTIM and
+	 * to avoid possible races.
+	 */
+	ATH_TXQ_LOCK(txq);
+	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+	if (txq != &sc->sc_mcastq) {
+		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);
+		}
+		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)
+			*txq->axq_link = bf->bf_daddr;
+		txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
+	}
+	ATH_TXQ_UNLOCK(txq);
+}
+
 static int
 ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf,
     struct mbuf *m0)
@@ -3293,11 +4133,11 @@
 	struct ath_hal *ah = sc->sc_ah;
 	struct ifnet *ifp = sc->sc_ifp;
 	const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
-	int i, error, iswep, ismcast, ismrr;
+	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 */
-	struct ath_desc *ds, *ds0;
+	struct ath_desc *ds;
 	struct ath_txq *txq;
 	struct ieee80211_frame *wh;
 	u_int subtype, flags, ctsduration;
@@ -3305,12 +4145,12 @@
 	const HAL_RATE_TABLE *rt;
 	HAL_BOOL shortPreamble;
 	struct ath_node *an;
-	struct mbuf *m;
 	u_int pri;
 
 	wh = mtod(m0, struct ieee80211_frame *);
 	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
 	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+	isfrag = m0->m_flags & M_FRAG;
 	hdrlen = ieee80211_anyhdrsize(wh);
 	/*
 	 * Packet length must not include any
@@ -3335,21 +4175,22 @@
 			 * 802.11 layer counts failures and provides
 			 * debugging/diagnostics.
 			 */
-			m_freem(m0);
+			ath_freetx(m0);
 			return EIO;
 		}
 		/*
 		 * Adjust the packet + header lengths for the crypto
 		 * additions and calculate the h/w key index.  When
 		 * a s/w mic is done the frame will have had any mic
-		 * added to it prior to entry so m0->m_pkthdr.len above will
+		 * added to it prior to entry so m0->m_pkthdr.len will
 		 * account for it. Otherwise we need to add it to the
 		 * packet length.
 		 */
 		cip = k->wk_cipher;
 		hdrlen += cip->ic_header;
 		pktlen += cip->ic_header + cip->ic_trailer;
-		if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
+		/* NB: frags always have any TKIP MIC done in s/w */
+		if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && !isfrag)
 			pktlen += cip->ic_miclen;
 		keyix = k->wk_keyix;
 
@@ -3371,60 +4212,22 @@
 	 * Load the DMA map so any coalescing is done.  This
 	 * also calculates the number of descriptors we need.
 	 */
-	error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
-				     bf->bf_segs, &bf->bf_nseg,
-				     BUS_DMA_NOWAIT);
-	if (error == EFBIG) {
-		/* XXX packet requires too many descriptors */
-		bf->bf_nseg = ATH_TXDESC+1;
-	} else if (error != 0) {
-		sc->sc_stats.ast_tx_busdma++;
-		m_freem(m0);
+	error = ath_tx_dmasetup(sc, bf, m0);
+	if (error != 0)
 		return error;
-	}
+	bf->bf_node = ni;			/* NB: held reference */
+	m0 = bf->bf_m;				/* NB: may have changed */
+	wh = mtod(m0, struct ieee80211_frame *);
+
+	/* setup descriptors */
+	ds = bf->bf_desc;
+	rt = sc->sc_currates;
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
 	/*
-	 * Discard null packets and check for packets that
-	 * 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 */
-		sc->sc_stats.ast_tx_linear++;
-		m = ath_defrag(m0, M_DONTWAIT, ATH_TXDESC);
-		if (m == NULL) {
-			m_freem(m0);
-			sc->sc_stats.ast_tx_nombuf++;
-			return ENOMEM;
-		}
-		m0 = m;
-		error = bus_dmamap_load_mbuf_sg(sc->sc_dmat, bf->bf_dmamap, m0,
-					     bf->bf_segs, &bf->bf_nseg,
-					     BUS_DMA_NOWAIT);
-		if (error != 0) {
-			sc->sc_stats.ast_tx_busdma++;
-			m_freem(m0);
-			return error;
-		}
-		KASSERT(bf->bf_nseg <= ATH_TXDESC,
-		    ("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++;
-		m_freem(m0);
-		return EIO;
-	}
-	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", __func__, m0, pktlen);
-	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
-	bf->bf_m = m0;
-	bf->bf_node = ni;			/* NB: held reference */
-
-	/* setup descriptors */
-	ds = bf->bf_desc;
-	rt = sc->sc_currates;
-	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
-
-	/*
-	 * NB: the 802.11 layer marks whether or not we should
-	 * use short preamble based on the current mode and
-	 * negotiated parameters.
+	 * NB: the 802.11 layer marks whether or not we should
+	 * use short preamble based on the current mode and
+	 * negotiated parameters.
 	 */
 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
 	    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
@@ -3506,6 +4309,7 @@
 			ath_rate_findrate(sc, an, shortPreamble, pktlen,
 				&rix, &try0, &txrate);
 			sc->sc_txrate = txrate;		/* for LED blinking */
+			sc->sc_lastdatarix = rix;	/* for fast frames */
 			if (try0 != ATH_TXMAXTRY)
 				ismrr = 1;
 		}
@@ -3517,18 +4321,19 @@
 		if_printf(ifp, "bogus frame type 0x%x (%s)\n",
 			wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
 		/* XXX statistic */
-		m_freem(m0);
+		ath_freetx(m0);
 		return EIO;
 	}
 	txq = sc->sc_ac2q[pri];
 
 	/*
 	 * When servicing one or more stations in power-save mode
-	 * multicast frames must be buffered until after the beacon.
-	 * We use the CAB queue for that.
+	 * (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.
 	 */
-	if (ismcast && ic->ic_ps_sta) {
-		txq = sc->sc_cabq;
+	if (ismcast && (ic->ic_ps_sta || sc->sc_mcastq.axq_depth)) {
+		txq = &sc->sc_mcastq;
 		/* XXX? more bit in 802.11 frame header */
 	}
 
@@ -3537,7 +4342,8 @@
 	 */
 	if (ismcast) {
 		flags |= HAL_TXDESC_NOACK;	/* no ack on broad/multicast */
-	} else if (pktlen > ic->ic_rtsthreshold) {
+	} else if (pktlen > ic->ic_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++;
@@ -3558,7 +4364,17 @@
 			flags |= HAL_TXDESC_RTSENA;
 		else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
 			flags |= HAL_TXDESC_CTSENA;
-		cix = rt->info[sc->sc_protrix].controlRate;
+		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++;
 	}
 
@@ -3569,13 +4385,31 @@
 	if ((flags & HAL_TXDESC_NOACK) == 0 &&
 	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
 		u_int16_t dur;
-		/*
-		 * XXX not right with fragmentation.
-		 */
 		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);
 	}
 
@@ -3625,19 +4459,28 @@
 	} 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.
+	 */
+	m0->m_nextpkt = NULL;
+
 	if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
-		ieee80211_dump_pkt(mtod(m0, caddr_t), m0->m_len,
+		ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len,
 			sc->sc_hwmap[txrate].ieeerate, -1);
 
-	if (ic->ic_rawbpf)
+	if (bpf_peers_present(ic->ic_rawbpf))
 		bpf_mtap(ic->ic_rawbpf, m0);
-	if (sc->sc_drvbpf) {
+	if (bpf_peers_present(sc->sc_drvbpf)) {
 		u_int64_t tsf = ath_hal_gettsf64(ah);
 
 		sc->sc_tx_th.wt_tsf = htole64(tsf);
 		sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags;
 		if (iswep)
 			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+		if (isfrag)
+			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
 		sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate;
 		sc->sc_tx_th.wt_txpower = ni->ni_txpower;
 		sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
@@ -3646,7 +4489,7 @@
 			&sc->sc_tx_th, sc->sc_tx_th_len, m0);
 	}
 
-	/* 
+	/*
 	 * 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
@@ -3695,55 +4538,7 @@
 	if (ismrr)
 		ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix);
 
-	/*
-	 * Fillin the remainder of the descriptor info.
-	 */
-	ds0 = ds;
-	for (i = 0; i < bf->bf_nseg; i++, ds++) {
-		ds->ds_data = bf->bf_segs[i].ds_addr;
-		if (i == bf->bf_nseg - 1)
-			ds->ds_link = 0;
-		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 */
-			, i == bf->bf_nseg - 1	/* last segment */
-			, ds0			/* first descriptor */
-		);
-		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]);
-	}
-	/*
-	 * Insert the frame on the outbound list and
-	 * pass it on to the hardware.
-	 */
-	ATH_TXQ_LOCK(txq);
-	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);
-	}
-	txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
-	/*
-	 * The CAB queue is started from the SWBA handler since
-	 * frames only go out on DTIM and to avoid possible races.
-	 */
-	if (txq != sc->sc_cabq)
-		ath_hal_txstart(ah, txq->axq_qnum);
-	ATH_TXQ_UNLOCK(txq);
-
+	ath_tx_handoff(sc, txq, bf);
 	return 0;
 }
 
@@ -3757,6 +4552,7 @@
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ath_buf *bf;
 	struct ath_desc *ds, *ds0;
+	struct ath_tx_status *ts;
 	struct ieee80211_node *ni;
 	struct ath_node *an;
 	int sr, lr, pri, nacked;
@@ -3777,7 +4573,8 @@
 		}
 		ds0 = &bf->bf_desc[0];
 		ds = &bf->bf_desc[bf->bf_nseg - 1];
-		status = ath_hal_txprocdesc(ah, ds);
+		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(bf, txq->axq_qnum, 0, status == HAL_OK);
@@ -3794,46 +4591,54 @@
 		ni = bf->bf_node;
 		if (ni != NULL) {
 			an = ATH_NODE(ni);
-			if (ds->ds_txstat.ts_status == 0) {
-				u_int8_t txant = ds->ds_txstat.ts_antenna;
+			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 (ds->ds_txstat.ts_rate & HAL_TXSTAT_ALTRATE)
+				if (ts->ts_rate & HAL_TXSTAT_ALTRATE)
 					sc->sc_stats.ast_tx_altrate++;
-				sc->sc_stats.ast_tx_rssi =
-					ds->ds_txstat.ts_rssi;
+				sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
 				ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi,
-					ds->ds_txstat.ts_rssi);
+					ts->ts_rssi);
 				pri = M_WME_GETAC(bf->bf_m);
 				if (pri >= WME_AC_VO)
 					ic->ic_wme.wme_hipri_traffic++;
 				ni->ni_inact = ni->ni_inact_reload;
 			} else {
-				if (ds->ds_txstat.ts_status & HAL_TXERR_XRETRY)
+				if (ts->ts_status & HAL_TXERR_XRETRY)
 					sc->sc_stats.ast_tx_xretries++;
-				if (ds->ds_txstat.ts_status & HAL_TXERR_FIFO)
+				if (ts->ts_status & HAL_TXERR_FIFO)
 					sc->sc_stats.ast_tx_fifoerr++;
-				if (ds->ds_txstat.ts_status & HAL_TXERR_FILT)
+				if (ts->ts_status & HAL_TXERR_FILT)
 					sc->sc_stats.ast_tx_filtered++;
+				if (bf->bf_m->m_flags & M_FF)
+					sc->sc_stats.ast_ff_txerr++;
 			}
-			sr = ds->ds_txstat.ts_shortretry;
-			lr = ds->ds_txstat.ts_longretry;
+			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 ((ds->ds_txstat.ts_status & HAL_TXERR_FILT) == 0 &&
+			if ((ts->ts_status & HAL_TXERR_FILT) == 0 &&
 			    (bf->bf_flags & HAL_TXDESC_NOACK) == 0) {
 				/*
 				 * If frame was ack'd update the last rx time
 				 * used to workaround phantom bmiss interrupts.
 				 */
-				if (ds->ds_txstat.ts_status == 0)
+				if (ts->ts_status == 0)
 					nacked++;
-				ath_rate_tx_complete(sc, an, ds, ds0);
+				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,
+					ts->ts_status);
+			/*
 			 * Reclaim reference to node.
 			 *
 			 * NB: the node may be reclaimed here if, for example
@@ -3845,6 +4650,7 @@
 		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
 		    BUS_DMASYNC_POSTWRITE);
 		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+
 		m_freem(bf->bf_m);
 		bf->bf_m = NULL;
 		bf->bf_node = NULL;
@@ -3853,6 +4659,11 @@
 		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
 		ATH_TXBUF_UNLOCK(sc);
 	}
+	/*
+	 * Flush fast-frame staging queue when traffic slows.
+	 */
+	if (txq->axq_depth <= 1)
+		ath_ff_stageq_flush(sc, txq, ath_ff_always);
 	return nacked;
 }
 
@@ -3879,7 +4690,7 @@
 	if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum))
 		ath_tx_processq(sc, sc->sc_cabq);
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-	sc->sc_tx_timer = 0;
+	ifp->if_timer = 0;
 
 	if (sc->sc_softled)
 		ath_led_event(sc, ATH_LED_TX);
@@ -3916,7 +4727,7 @@
 		sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
 
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-	sc->sc_tx_timer = 0;
+	ifp->if_timer = 0;
 
 	if (sc->sc_softled)
 		ath_led_event(sc, ATH_LED_TX);
@@ -3945,7 +4756,7 @@
 		sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
 
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-	sc->sc_tx_timer = 0;
+	ifp->if_timer = 0;
 
 	if (sc->sc_softled)
 		ath_led_event(sc, ATH_LED_TX);
@@ -3978,21 +4789,28 @@
 		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
 		ATH_TXQ_UNLOCK(txq);
 #ifdef ATH_DEBUG
-		if (sc->sc_debug & ATH_DEBUG_RESET)
+		if (sc->sc_debug & ATH_DEBUG_RESET) {
 			ath_printtxbuf(bf, txq->axq_qnum, ix,
-				ath_hal_txprocdesc(ah, bf->bf_desc) == HAL_OK);
+				ath_hal_txprocdesc(ah, bf->bf_desc,
+				    &bf->bf_status.ds_txstat) == HAL_OK);
+			ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, caddr_t),
+				bf->bf_m->m_len, 0, -1);
+		}
 #endif /* ATH_DEBUG */
 		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-		m_freem(bf->bf_m);
-		bf->bf_m = NULL;
 		ni = bf->bf_node;
 		bf->bf_node = NULL;
 		if (ni != NULL) {
 			/*
-			 * Reclaim node reference.
+			 * 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;
+
 		ATH_TXBUF_LOCK(sc);
 		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
 		ATH_TXBUF_UNLOCK(sc);
@@ -4004,11 +4822,11 @@
 {
 	struct ath_hal *ah = sc->sc_ah;
 
-	(void) ath_hal_stoptxdma(ah, txq->axq_qnum);
 	DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n",
 	    __func__, txq->axq_qnum,
 	    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum),
 	    txq->axq_link);
+	(void) ath_hal_stoptxdma(ah, txq->axq_qnum);
 }
 
 /*
@@ -4024,10 +4842,11 @@
 	/* XXX return value */
 	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",
+		    __func__, sc->sc_bhalq,
+		    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq),
+		    NULL);
 		(void) ath_hal_stoptxdma(ah, sc->sc_bhalq);
-		DPRINTF(sc, ATH_DEBUG_RESET,
-		    "%s: beacon queue %p\n", __func__,
-		    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq));
 		for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
 			if (ATH_TXQ_SETUP(sc, i))
 				ath_tx_stopdma(sc, &sc->sc_txq[i]);
@@ -4035,8 +4854,21 @@
 	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
 		if (ATH_TXQ_SETUP(sc, i))
 			ath_tx_draintxq(sc, &sc->sc_txq[i]);
+	ath_tx_draintxq(sc, &sc->sc_mcastq);
+#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(bf, sc->sc_bhalq, 0,
+				ath_hal_txprocdesc(ah, bf->bf_desc,
+				    &bf->bf_status.ds_txstat) == HAL_OK);
+			ieee80211_dump_pkt(&sc->sc_ic, mtod(bf->bf_m, caddr_t),
+				bf->bf_m->m_len, 0, -1);
+		}
+	}
+#endif /* ATH_DEBUG */
 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
-	sc->sc_tx_timer = 0;
+	ifp->if_timer = 0;
 }
 
 /*
@@ -4064,14 +4896,19 @@
 		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));
+				bf->bf_daddr, PA2DESC(sc, ds->ds_link), rs);
 			if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL))
 				ath_printrxbuf(bf, ix, status == HAL_OK);
 			ix++;
 		}
 	}
 #endif
+	if (sc->sc_rxpending != NULL) {
+		m_freem(sc->sc_rxpending);
+		sc->sc_rxpending = NULL;
+	}
 	sc->sc_rxlink = NULL;		/* just in case */
 #undef PA2DESC
 }
@@ -4086,6 +4923,7 @@
 	struct ath_buf *bf;
 
 	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) {
@@ -4110,35 +4948,29 @@
 static void
 ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
 	enum ieee80211_phymode mode;
-	u_int16_t flags;
 
 	/*
 	 * Change channels and update the h/w rate map
 	 * if we're switching; e.g. 11a to 11b/g.
 	 */
-	mode = ieee80211_chan2mode(ic, chan);
+	if (IEEE80211_IS_CHAN_HALF(chan))
+		mode = IEEE80211_MODE_HALF;
+	else if (IEEE80211_IS_CHAN_QUARTER(chan))
+		mode = IEEE80211_MODE_QUARTER;
+	else
+		mode = ieee80211_chan2mode(chan);
 	if (mode != sc->sc_curmode)
 		ath_setcurmode(sc, mode);
-	/*
-	 * Update BPF state.  NB: ethereal et. al. don't handle
-	 * merged flags well so pick a unique mode for their use.
-	 */
-	if (IEEE80211_IS_CHAN_A(chan))
-		flags = IEEE80211_CHAN_A;
-	/* XXX 11g schizophrenia */
-	else if (IEEE80211_IS_CHAN_G(chan) ||
-	    IEEE80211_IS_CHAN_PUREG(chan))
-		flags = IEEE80211_CHAN_G;
-	else
-		flags = IEEE80211_CHAN_B;
-	if (IEEE80211_IS_CHAN_T(chan))
-		flags |= IEEE80211_CHAN_TURBO;
-	sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
-		htole16(chan->ic_freq);
-	sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
-		htole16(flags);
+
+	sc->sc_rx_th.wr_chan_flags = htole32(chan->ic_flags);
+	sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags;
+	sc->sc_rx_th.wr_chan_freq = htole16(chan->ic_freq);
+	sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq;
+	sc->sc_rx_th.wr_chan_ieee = chan->ic_ieee;
+	sc->sc_tx_th.wt_chan_ieee = sc->sc_rx_th.wr_chan_ieee;
+	sc->sc_rx_th.wr_chan_maxpow = chan->ic_maxregpower;
+	sc->sc_tx_th.wt_chan_maxpow = sc->sc_rx_th.wr_chan_maxpow;
 }
 
 /*
@@ -4195,8 +5027,7 @@
 	 * the flags constrained to reflect the current
 	 * operating mode.
 	 */
-	hchan.channel = chan->ic_freq;
-	hchan.channelFlags = ath_chan2flags(ic, chan);
+	ath_mapchan(&hchan, chan);
 
 	DPRINTF(sc, ATH_DEBUG_RESET,
 	    "%s: %u (%u MHz, hal flags 0x%x) -> %u (%u MHz, hal flags 0x%x)\n",
@@ -4245,7 +5076,6 @@
 		 * Change channels and update the h/w rate map
 		 * if we're switching; e.g. 11a to 11b/g.
 		 */
-		ic->ic_ibss_chan = chan;
 		ath_chan_change(sc, chan);
 
 		/*
@@ -4275,16 +5105,6 @@
 	return 0;
 }
 
-static void
-ath_next_scan(void *arg)
-{
-	struct ath_softc *sc = arg;
-	struct ieee80211com *ic = &sc->sc_ic;
-
-	if (ic->ic_state == IEEE80211_S_SCAN)
-		ieee80211_next_scan(ic);
-}
-
 /*
  * Periodically recalibrate the PHY to account
  * for temperature/environment changes.
@@ -4320,7 +5140,7 @@
 	ath_hal_process_noisefloor(ah);
 	/*
 	 * Poll more frequently when the IQ calibration is in
-	 * progress to speedup loading the final settings. 
+	 * progress to speedup loading the final settings.
 	 * We temper this aggressive polling with an exponential
 	 * back off after 4 tries up to ath_calinterval.
 	 */
@@ -4344,6 +5164,63 @@
 		ath_calibrate, sc);
 }
 
+static void
+ath_scan_start(struct ieee80211com *ic)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ath_softc *sc = ifp->if_softc;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int32_t rfilt;
+
+	/* XXX calibration timer? */
+
+	sc->sc_scanning = 1;
+	sc->sc_syncbeacon = 0;
+	rfilt = ath_calcrxfilter(sc);
+	ath_hal_setrxfilter(ah, rfilt);
+	ath_hal_setassocid(ah, ifp->if_broadcastaddr, 0);
+
+	DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0\n",
+		 __func__, rfilt, ether_sprintf(ifp->if_broadcastaddr));
+}
+
+static void
+ath_scan_end(struct ieee80211com *ic)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ath_softc *sc = ifp->if_softc;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int32_t rfilt;
+
+	sc->sc_scanning = 0;
+	rfilt = ath_calcrxfilter(sc);
+	ath_hal_setrxfilter(ah, rfilt);
+	ath_hal_setassocid(ah, sc->sc_curbssid, sc->sc_curaid);
+
+	ath_hal_process_noisefloor(ah);
+
+	DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n",
+		 __func__, rfilt, ether_sprintf(sc->sc_curbssid),
+		 sc->sc_curaid);
+}
+
+static void
+ath_set_channel(struct ieee80211com *ic)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ath_softc *sc = ifp->if_softc;
+
+	(void) ath_chan_set(sc, ic->ic_curchan);
+	/*
+	 * If we are returning to our bss channel then mark state
+	 * so the next recv'd beacon's tsf will be used to sync the
+	 * beacon timers.  Note that since we only hear beacons in
+	 * sta/ibss mode this has no effect in other operating modes.
+	 */
+	if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan)
+		sc->sc_syncbeacon = 1;
+}
+
 static int
 ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
 {
@@ -4351,59 +5228,69 @@
 	struct ath_softc *sc = ifp->if_softc;
 	struct ath_hal *ah = sc->sc_ah;
 	struct ieee80211_node *ni;
-	int i, error;
-	const u_int8_t *bssid;
+	int i, error, stamode;
 	u_int32_t rfilt;
 	static const HAL_LED_STATE leds[] = {
 	    HAL_LED_INIT,	/* IEEE80211_S_INIT */
 	    HAL_LED_SCAN,	/* IEEE80211_S_SCAN */
 	    HAL_LED_AUTH,	/* IEEE80211_S_AUTH */
 	    HAL_LED_ASSOC, 	/* IEEE80211_S_ASSOC */
+	    HAL_LED_RUN, 	/* IEEE80211_S_CAC */
 	    HAL_LED_RUN, 	/* IEEE80211_S_RUN */
+	    HAL_LED_RUN, 	/* IEEE80211_S_CSA */
+	    HAL_LED_RUN, 	/* IEEE80211_S_SLEEP */
 	};
 
 	DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__,
 		ieee80211_state_name[ic->ic_state],
 		ieee80211_state_name[nstate]);
 
-	callout_stop(&sc->sc_scan_ch);
 	callout_stop(&sc->sc_cal_ch);
 	callout_stop(&sc->sc_dfs_ch);
 	ath_hal_setledstate(ah, leds[nstate]);	/* set LED */
 
 	if (nstate == IEEE80211_S_INIT) {
-		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
 		/*
-		 * NB: disable interrupts so we don't rx frames.
+		 * Shutdown host/driver operation:
+		 * o disable interrupts so we don't rx frames
+		 * o clean any pending items on the task q
+		 * o notify the rate control algorithm
 		 */
+		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
 		ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL);
-		/*
-		 * Notify the rate control algorithm.
-		 */
+#if 0
+		/* XXX can't use taskqueue_drain 'cuz we're holding sc_mtx */
+		taskqueue_drain(sc->sc_tq, &sc->sc_rxtask);
+		taskqueue_drain(sc->sc_tq, &sc->sc_rxorntask);
+		taskqueue_drain(sc->sc_tq, &sc->sc_bmisstask);
+		taskqueue_drain(sc->sc_tq, &sc->sc_bstucktask);
+#endif
 		ath_rate_newstate(sc, nstate);
 		goto done;
 	}
 	ni = ic->ic_bss;
-	error = ath_chan_set(sc, ic->ic_curchan);
-	if (error != 0)
-		goto bad;
-	rfilt = ath_calcrxfilter(sc, nstate);
-	if (nstate == IEEE80211_S_SCAN)
-		bssid = ifp->if_broadcastaddr;
-	else
-		bssid = ni->ni_bssid;
+
+	rfilt = ath_calcrxfilter(sc);
+	stamode = (sc->sc_opmode == HAL_M_STA || sc->sc_opmode == HAL_M_IBSS);
+	if (stamode && nstate == IEEE80211_S_RUN) {
+		sc->sc_curaid = ni->ni_associd;
+		IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid);
+	} else
+		sc->sc_curaid = 0;
+
+	DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s aid 0x%x\n",
+		 __func__, rfilt, ether_sprintf(sc->sc_curbssid),
+		 sc->sc_curaid);
+
 	ath_hal_setrxfilter(ah, rfilt);
-	DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s\n",
-		 __func__, rfilt, ether_sprintf(bssid));
+	if (stamode)
+		ath_hal_setassocid(ah, sc->sc_curbssid, ni->ni_associd);
 
-	if (nstate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA)
-		ath_hal_setassocid(ah, bssid, ni->ni_associd);
-	else
-		ath_hal_setassocid(ah, bssid, 0);
-	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+	if (ic->ic_opmode != IEEE80211_M_STA &&
+	    (ic->ic_flags & IEEE80211_F_PRIVACY)) {
 		for (i = 0; i < IEEE80211_WEP_NKID; i++)
 			if (ath_hal_keyisvalid(ah, i))
-				ath_hal_keysetmac(ah, i, bssid);
+				ath_hal_keysetmac(ah, i, ni->ni_bssid);
 	}
 
 	/*
@@ -4412,9 +5299,7 @@
 	 */
 	ath_rate_newstate(sc, nstate);
 
-	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
-		/* nothing to do */;
-	} else if (nstate == IEEE80211_S_RUN) {
+	if (nstate == IEEE80211_S_RUN) {
 		DPRINTF(sc, ATH_DEBUG_STATE,
 			"%s(RUN): ic_flags=0x%08x iv=%d bssid=%s "
 			"capinfo=0x%04x chan=%d\n"
@@ -4472,7 +5357,6 @@
 		default:
 			break;
 		}
-
 		/*
 		 * Let the hal process statistics collected during a
 		 * scan so it can provide calibrated noise floor data.
@@ -4486,7 +5370,7 @@
 		sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
 	} else {
 		ath_hal_intrset(ah,
-			sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
+		    sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
 		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
 	}
 done:
@@ -4501,10 +5385,6 @@
 		/* start periodic recalibration timer */
 		callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
 			ath_calibrate, sc);
-	} else if (nstate == IEEE80211_S_SCAN) {
-		/* start ap/neighbor scan timer */
-		callout_reset(&sc->sc_scan_ch, (ath_dwelltime * hz) / 1000,
-			ath_next_scan, sc);
 	}
 bad:
 	return error;
@@ -4563,15 +5443,15 @@
 }
 
 static int
-ath_getchannels(struct ath_softc *sc, u_int cc,
-	HAL_BOOL outdoor, HAL_BOOL xchanmode)
+ath_getchannels(struct ath_softc *sc,
+    HAL_REG_DOMAIN rd, HAL_CTRY_CODE cc, HAL_BOOL outdoor, HAL_BOOL xchanmode)
 {
-#define	COMPAT	(CHANNEL_ALL_NOTURBO|CHANNEL_PASSIVE)
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ifnet *ifp = sc->sc_ifp;
 	struct ath_hal *ah = sc->sc_ah;
 	HAL_CHANNEL *chans;
-	int i, ix, nchan;
+	int i, nchan;
+	u_int32_t regdomain;
 
 	chans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL),
 			M_TEMP, M_NOWAIT);
@@ -4580,59 +5460,58 @@
 		return ENOMEM;
 	}
 	if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
-	    NULL, 0, NULL,
-	    cc, HAL_MODE_ALL, outdoor, xchanmode)) {
-		u_int32_t rd;
-
-		ath_hal_getregdomain(ah, &rd);
+	    NULL, 0, NULL, cc, HAL_MODE_ALL, outdoor, xchanmode)) {
+		(void) ath_hal_getregdomain(ah, &regdomain);
 		if_printf(ifp, "unable to collect channel list from hal; "
-			"regdomain likely %u country code %u\n", rd, cc);
+			"regdomain likely %u country code %u\n", regdomain, cc);
 		free(chans, M_TEMP);
 		return EINVAL;
 	}
 
 	/*
-	 * Convert HAL channels to ieee80211 ones and insert
-	 * them in the table according to their channel number.
+	 * Convert HAL channels to ieee80211 ones.
 	 */
+	memset(ic->ic_channels, 0, sizeof(ic->ic_channels));
 	for (i = 0; i < nchan; i++) {
 		HAL_CHANNEL *c = &chans[i];
-		u_int16_t flags;
+		struct ieee80211_channel *ichan = &ic->ic_channels[i];
 
-		ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags);
-		if (ix > IEEE80211_CHAN_MAX) {
-			if_printf(ifp, "bad hal channel %d (%u/%x) ignored\n",
-				ix, c->channel, c->channelFlags);
-			continue;
-		}
-		if (ix < 0) {
-			/* XXX can't handle stuff <2400 right now */
-			if (bootverbose)
-				if_printf(ifp, "hal channel %d (%u/%x) "
-				    "cannot be handled; ignored\n",
-				    ix, c->channel, c->channelFlags);
-			continue;
-		}
-		/*
-		 * Calculate net80211 flags; most are compatible
-		 * but some need massaging.  Note the static turbo
-		 * conversion can be removed once net80211 is updated
-		 * to understand static vs. dynamic turbo.
-		 */
-		flags = c->channelFlags & COMPAT;
-		if (c->channelFlags & CHANNEL_STURBO)
-			flags |= IEEE80211_CHAN_TURBO;
-		if (ic->ic_channels[ix].ic_freq == 0) {
-			ic->ic_channels[ix].ic_freq = c->channel;
-			ic->ic_channels[ix].ic_flags = flags;
+		ichan->ic_ieee = ath_hal_mhz2ieee(ah, c->channel,
+					c->channelFlags);
+		if (bootverbose)
+			if_printf(ifp, "hal channel %u/%x -> %u\n",
+			    c->channel, c->channelFlags, ichan->ic_ieee);
+		ichan->ic_freq = c->channel;
+
+		if ((c->channelFlags & CHANNEL_PUREG) == CHANNEL_PUREG) {
+			/*
+			 * Except for AR5211, HAL's PUREG means mixed
+			 * DSSS and OFDM.
+			 */
+			ichan->ic_flags = c->channelFlags &~ CHANNEL_PUREG;
+			ichan->ic_flags |= IEEE80211_CHAN_G;
 		} else {
-			/* channels overlap; e.g. 11g and 11b */
-			ic->ic_channels[ix].ic_flags |= flags;
+			ichan->ic_flags = c->channelFlags;
+		}
+
+		if (ath_hal_isgsmsku(ah)) {
+			/* remap to true frequencies */
+			ichan->ic_freq = 922 + (2422 - ichan->ic_freq);
+			ichan->ic_flags |= IEEE80211_CHAN_GSM;
+			ichan->ic_ieee = ieee80211_mhz2ieee(ichan->ic_freq,
+						    ichan->ic_flags);
 		}
+		ichan->ic_maxregpower = c->maxRegTxPower;	/* dBm */
+		ichan->ic_maxpower = c->maxTxPower;		/* 1/2 dBm */
+		ichan->ic_minpower = c->minTxPower;		/* 1/2 dBm */
 	}
+	ic->ic_nchans = nchan;
 	free(chans, M_TEMP);
+	(void) ath_hal_getregdomain(ah, &sc->sc_regdomain);
+	ath_hal_getcountrycode(ah, &sc->sc_countrycode);
+	sc->sc_xchanmode = xchanmode;
+	sc->sc_outdoor = outdoor;
 	return 0;
-#undef COMPAT
 }
 
 static void
@@ -4702,45 +5581,32 @@
 	if (sc->sc_curtxpow != ic->ic_txpowlimit) {
 		ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
 		/* read back in case value is clamped */
-		ath_hal_gettxpowlimit(ah, &txpow);
-		ic->ic_txpowlimit = sc->sc_curtxpow = txpow;
+		if (ath_hal_gettxpowlimit(ah, &txpow))
+			ic->ic_txpowlimit = sc->sc_curtxpow = txpow;
 	}
-	/* 
+	/*
 	 * Fetch max tx power level for status requests.
 	 */
-	ath_hal_getmaxtxpow(sc->sc_ah, &txpow);
-	ic->ic_bss->ni_txpower = txpow;
-}
-
-static void
-rate_setup(struct ath_softc *sc,
-	const HAL_RATE_TABLE *rt, struct ieee80211_rateset *rs)
-{
-	int i, maxrates;
-
-	if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
-		DPRINTF(sc, ATH_DEBUG_ANY,
-			"%s: rate table too small (%u > %u)\n",
-		       __func__, rt->rateCount, IEEE80211_RATE_MAXSIZE);
-		maxrates = IEEE80211_RATE_MAXSIZE;
-	} else
-		maxrates = rt->rateCount;
-	for (i = 0; i < maxrates; i++)
-		rs->rs_rates[i] = rt->info[i].dot11Rate;
-	rs->rs_nrates = maxrates;
+	if (ath_hal_getmaxtxpow(sc->sc_ah, &txpow))
+		ic->ic_bss->ni_txpower = txpow;
 }
 
 static int
 ath_rate_setup(struct ath_softc *sc, u_int mode)
 {
 	struct ath_hal *ah = sc->sc_ah;
-	struct ieee80211com *ic = &sc->sc_ic;
 	const HAL_RATE_TABLE *rt;
 
 	switch (mode) {
 	case IEEE80211_MODE_11A:
 		rt = ath_hal_getratetable(ah, HAL_MODE_11A);
 		break;
+	case IEEE80211_MODE_HALF:
+		rt = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE);
+		break;
+	case IEEE80211_MODE_QUARTER:
+		rt = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE);
+		break;
 	case IEEE80211_MODE_11B:
 		rt = ath_hal_getratetable(ah, HAL_MODE_11B);
 		break;
@@ -4748,23 +5614,31 @@
 		rt = ath_hal_getratetable(ah, HAL_MODE_11G);
 		break;
 	case IEEE80211_MODE_TURBO_A:
-		/* XXX until static/dynamic turbo is fixed */
-		rt = ath_hal_getratetable(ah, HAL_MODE_TURBO);
+		rt = ath_hal_getratetable(ah, HAL_MODE_108A);
+#if HAL_ABI_VERSION < 0x07013100
+		if (rt == NULL)		/* XXX bandaid for old hal's */
+			rt = ath_hal_getratetable(ah, HAL_MODE_TURBO);
+#endif
 		break;
 	case IEEE80211_MODE_TURBO_G:
 		rt = ath_hal_getratetable(ah, HAL_MODE_108G);
 		break;
+	case IEEE80211_MODE_STURBO_A:
+		rt = ath_hal_getratetable(ah, HAL_MODE_TURBO);
+		break;
+	case IEEE80211_MODE_11NA:
+		rt = ath_hal_getratetable(ah, HAL_MODE_11NA_HT20);
+		break;
+	case IEEE80211_MODE_11NG:
+		rt = ath_hal_getratetable(ah, HAL_MODE_11NG_HT20);
+		break;
 	default:
 		DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n",
 			__func__, mode);
 		return 0;
 	}
 	sc->sc_rates[mode] = rt;
-	if (rt != NULL) {
-		rate_setup(sc, rt, &ic->ic_sup_rates[mode]);
-		return 1;
-	} else
-		return 0;
+	return (rt != NULL);
 }
 
 static void
@@ -4791,6 +5665,7 @@
 		{   4, 267,  66 },
 		{   2, 400, 100 },
 		{   0, 500, 130 },
+		/* XXX half/quarter rates */
 	};
 	const HAL_RATE_TABLE *rt;
 	int i, j;
@@ -4810,6 +5685,8 @@
 		}
 		sc->sc_hwmap[i].ieeerate =
 			rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
+		if (rt->info[ix].phy == IEEE80211_T_HT)
+			sc->sc_hwmap[i].ieeerate |= 0x80;	/* MCS */
 		sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD;
 		if (rt->info[ix].shortPreamble ||
 		    rt->info[ix].phy == IEEE80211_T_OFDM)
@@ -4850,35 +5727,37 @@
 
 #ifdef ATH_DEBUG
 static void
-ath_printrxbuf(struct ath_buf *bf, u_int ix, int done)
+ath_printrxbuf(const struct ath_buf *bf, u_int ix, int done)
 {
-	struct ath_desc *ds;
+	const struct ath_rx_status *rs = &bf->bf_status.ds_rxstat;
+	const struct ath_desc *ds;
 	int i;
 
 	for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
 		printf("R[%2u] (DS.V:%p DS.P:%p) L:%08x D:%08x%s\n"
 		       "      %08x %08x %08x %08x\n",
-		    ix, ds, (struct ath_desc *)bf->bf_daddr + i,
+		    ix, ds, (const struct ath_desc *)bf->bf_daddr + i,
 		    ds->ds_link, ds->ds_data,
-		    !done ? "" : (ds->ds_rxstat.rs_status == 0) ? " *" : " !",
+		    !done ? "" : (rs->rs_status == 0) ? " *" : " !",
 		    ds->ds_ctl0, ds->ds_ctl1,
 		    ds->ds_hw[0], ds->ds_hw[1]);
 	}
 }
 
 static void
-ath_printtxbuf(struct ath_buf *bf, u_int qnum, u_int ix, int done)
+ath_printtxbuf(const struct ath_buf *bf, u_int qnum, u_int ix, int done)
 {
-	struct ath_desc *ds;
+	const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
+	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, (struct ath_desc *)bf->bf_daddr + i,
+		    ds, (const struct ath_desc *)bf->bf_daddr + i,
 		    ds->ds_link, ds->ds_data, bf->bf_flags,
-		    !done ? "" : (ds->ds_txstat.ts_status == 0) ? " *" : " !",
+		    !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]);
 	}
@@ -4889,21 +5768,13 @@
 ath_watchdog(struct ifnet *ifp)
 {
 	struct ath_softc *sc = ifp->if_softc;
-	struct ieee80211com *ic = &sc->sc_ic;
 
-	ifp->if_timer = 0;
-	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid)
-		return;
-	if (sc->sc_tx_timer) {
-		if (--sc->sc_tx_timer == 0) {
-			if_printf(ifp, "device timeout\n");
-			ath_reset(ifp);
-			ifp->if_oerrors++;
-			sc->sc_stats.ast_watchdog++;
-		} else
-			ifp->if_timer = 1;
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && !sc->sc_invalid) {
+		if_printf(ifp, "device timeout\n");
+		ath_reset(ifp);
+		ifp->if_oerrors++;
+		sc->sc_stats.ast_watchdog++;
 	}
-	ieee80211_watchdog(ic);
 }
 
 #ifdef ATH_DIAGAPI
@@ -5018,7 +5889,9 @@
 		/* NB: embed these numbers to get a consistent view */
 		sc->sc_stats.ast_tx_packets = ifp->if_opackets;
 		sc->sc_stats.ast_rx_packets = ifp->if_ipackets;
-		sc->sc_stats.ast_rx_rssi = ieee80211_getrssi(ic);
+		ieee80211_getsignal(ic, &sc->sc_stats.ast_rx_rssi,
+			&sc->sc_stats.ast_rx_noise);
+		sc->sc_stats.ast_tx_rate = sc->sc_hwmap[sc->sc_txrate].ieeerate;
 		ATH_UNLOCK(sc);
 		/*
 		 * NB: Drop the softc lock in case of a page fault;
@@ -5115,6 +5988,50 @@
 }
 
 static int
+ath_sysctl_ledpin(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	int ledpin = sc->sc_ledpin;
+	int error;
+
+	error = sysctl_handle_int(oidp, &ledpin, 0, req);
+	if (error || !req->newptr)
+		return error;
+	if (ledpin != sc->sc_ledpin) {
+		sc->sc_ledpin = ledpin;
+		if (sc->sc_softled) {
+			ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin);
+			ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin,
+				!sc->sc_ledon);
+		}
+	}
+	return 0;
+}
+
+static int
+ath_sysctl_txantenna(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int txantenna = ath_hal_getantennaswitch(sc->sc_ah);
+	int error;
+
+	error = sysctl_handle_int(oidp, &txantenna, 0, req);
+	if (!error && req->newptr) {
+		/* XXX assumes 2 antenna ports */
+		if (txantenna < HAL_ANT_VARIABLE || txantenna > HAL_ANT_FIXED_B)
+			return EINVAL;
+		ath_hal_setantennaswitch(sc->sc_ah, txantenna);
+		/*
+		 * NB: with the switch locked this isn't meaningful,
+		 *     but set it anyway so things like radiotap get
+		 *     consistent info in their data.
+		 */
+		sc->sc_txantenna = txantenna;
+	}
+	return error;
+}
+
+static int
 ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS)
 {
 	struct ath_softc *sc = arg1;
@@ -5166,11 +6083,12 @@
 	u_int32_t scale;
 	int error;
 
-	ath_hal_gettpscale(sc->sc_ah, &scale);
+	(void) ath_hal_gettpscale(sc->sc_ah, &scale);
 	error = sysctl_handle_int(oidp, &scale, 0, req);
 	if (error || !req->newptr)
 		return error;
-	return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : ath_reset(ifp);
+	return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL :
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0;
 }
 
 static int
@@ -5190,6 +6108,7 @@
 ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS)
 {
 	struct ath_softc *sc = arg1;
+	struct ifnet *ifp = sc->sc_ifp;
 	struct ath_hal *ah = sc->sc_ah;
 	u_int rfkill = ath_hal_getrfkill(ah);
 	int error;
@@ -5199,10 +6118,9 @@
 		return error;
 	if (rfkill == ath_hal_getrfkill(ah))	/* unchanged */
 		return 0;
-	if (!ath_hal_setrfkill(ah, rfkill) || ath_reset(sc->sc_ifp) != 0)
+	if (!ath_hal_setrfkill(ah, rfkill))
 		return EINVAL;
-	else
-		return 0;
+	return (ifp->if_drv_flags & IFF_DRV_RUNNING) ? ath_reset(ifp) : 0;
 }
 
 static int
@@ -5212,7 +6130,7 @@
 	u_int rfsilent;
 	int error;
 
-	ath_hal_getrfsilent(sc->sc_ah, &rfsilent);
+	(void) ath_hal_getrfsilent(sc->sc_ah, &rfsilent);
 	error = sysctl_handle_int(oidp, &rfsilent, 0, req);
 	if (error || !req->newptr)
 		return error;
@@ -5224,18 +6142,45 @@
 }
 
 static int
+ath_sysctl_countrycode(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int32_t cc = sc->sc_countrycode;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int error;
+
+	error = sysctl_handle_int(oidp, &cc, 0, req);
+	if (error || !req->newptr)
+		return error;
+	error = ath_getchannels(sc, sc->sc_regdomain, cc,
+			sc->sc_outdoor != 0, sc->sc_xchanmode != 0);
+	if (error != 0)
+		return error;
+	ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
+	/* setcurmode? */
+	return 0;
+}
+
+static int
 ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS)
 {
 	struct ath_softc *sc = arg1;
-	u_int32_t rd;
+	u_int32_t rd = sc->sc_regdomain;
+	struct ieee80211com *ic = &sc->sc_ic;
 	int error;
 
-	if (!ath_hal_getregdomain(sc->sc_ah, &rd))
-		return EINVAL;
 	error = sysctl_handle_int(oidp, &rd, 0, req);
 	if (error || !req->newptr)
 		return error;
-	return !ath_hal_setregdomain(sc->sc_ah, rd) ? EINVAL : 0;
+	if (!ath_hal_setregdomain(sc->sc_ah, rd))
+		return EINVAL;
+	error = ath_getchannels(sc, rd, sc->sc_countrycode,
+			sc->sc_outdoor != 0, sc->sc_xchanmode != 0);
+	if (error != 0)
+		return error;
+	ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
+	/* setcurmode? */
+	return 0;
 }
 
 static int
@@ -5245,7 +6190,7 @@
 	u_int32_t tpack;
 	int error;
 
-	ath_hal_gettpack(sc->sc_ah, &tpack);
+	(void) ath_hal_gettpack(sc->sc_ah, &tpack);
 	error = sysctl_handle_int(oidp, &tpack, 0, req);
 	if (error || !req->newptr)
 		return error;
@@ -5259,7 +6204,7 @@
 	u_int32_t tpcts;
 	int error;
 
-	ath_hal_gettpcts(sc->sc_ah, &tpcts);
+	(void) ath_hal_gettpcts(sc->sc_ah, &tpcts);
 	error = sysctl_handle_int(oidp, &tpcts, 0, req);
 	if (error || !req->newptr)
 		return error;
@@ -5273,10 +6218,9 @@
 	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
 	struct ath_hal *ah = sc->sc_ah;
 
-	ath_hal_getcountrycode(sc->sc_ah, &sc->sc_countrycode);
-	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
-		"countrycode", CTLFLAG_RD, &sc->sc_countrycode, 0,
-		"EEPROM country code");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"countrycode", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_countrycode, "I", "country code");
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"regdomain", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
 		ath_sysctl_regdomain, "I", "EEPROM regdomain code");
@@ -5298,18 +6242,18 @@
 	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");
-	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
-		"ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0,
-		"GPIO pin connected to LED");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ledpin", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_ledpin, "I", "GPIO pin connected to LED");
 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"ledon", CTLFLAG_RW, &sc->sc_ledon, 0,
 		"setting to turn LED on");
 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 		"ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
 		"idle time for inactivity LED (ticks)");
-	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
-		"txantenna", CTLFLAG_RW, &sc->sc_txantenna, 0,
-		"tx antenna (0=auto)");
+	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,
 		"rxantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
 		ath_sysctl_rxantenna, "I", "default/rx antenna");
@@ -5338,6 +6282,16 @@
 			"tpcts", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
 			ath_sysctl_tpcts, "I", "tx power for cts frames");
 	}
+	if (ath_hal_hasfastframes(sc->sc_ah)) {
+		sc->sc_fftxqmin = ATH_FF_TXQMIN;
+		SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+			"fftxqmin", CTLFLAG_RW, &sc->sc_fftxqmin, 0,
+			"min frames before fast-frame staging");
+		sc->sc_fftxqmax = ATH_FF_TXQMAX;
+		SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+			"fftxqmax", CTLFLAG_RW, &sc->sc_fftxqmax, 0,
+			"max queued frames before tail drop");
+	}
 	if (ath_hal_hasrfsilent(ah)) {
 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
 			"rfsilent", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
@@ -5378,6 +6332,234 @@
 	sc->sc_rx_th.wr_ihdr.it_present = htole32(ATH_RX_RADIOTAP_PRESENT);
 }
 
+static int
+ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
+	struct ath_buf *bf, struct mbuf *m0,
+	const struct ieee80211_bpf_params *params)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	int error, ismcast, ismrr;
+	int hdrlen, pktlen, try0, txantenna;
+	u_int8_t rix, cix, txrate, ctsrate, rate1, rate2, rate3;
+	struct ath_txq *txq;
+	struct ieee80211_frame *wh;
+	u_int flags, ctsduration;
+	HAL_PKT_TYPE atype;
+	const HAL_RATE_TABLE *rt;
+	struct ath_desc *ds;
+	u_int pri;
+
+	wh = mtod(m0, struct ieee80211_frame *);
+	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+	hdrlen = ieee80211_anyhdrsize(wh);
+	/*
+	 * Packet length must not include any
+	 * pad bytes; deduct them here.
+	 */
+	/* XXX honor IEEE80211_BPF_DATAPAD */
+	pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN;
+
+	error = ath_tx_dmasetup(sc, bf, m0);
+	if (error != 0)
+		return error;
+	m0 = bf->bf_m;				/* NB: may have changed */
+	wh = mtod(m0, struct ieee80211_frame *);
+	bf->bf_node = ni;			/* NB: held reference */
+
+	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)
+		flags |= HAL_TXDESC_CTSENA;
+	/* XXX leave ismcast to injector? */
+	if ((params->ibp_flags & IEEE80211_BPF_NOACK) || ismcast)
+		flags |= HAL_TXDESC_NOACK;
+
+	rt = sc->sc_currates;
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+	rix = ath_tx_findrix(rt, params->ibp_rate0);
+	txrate = rt->info[rix].rateCode;
+	if (params->ibp_flags & IEEE80211_BPF_SHORTPRE)
+		txrate |= rt->info[rix].shortPreamble;
+	sc->sc_txrate = txrate;
+	try0 = params->ibp_try0;
+	ismrr = (params->ibp_try1 != 0);
+	txantenna = params->ibp_pri >> 2;
+	if (txantenna == 0)			/* XXX? */
+		txantenna = sc->sc_txantenna;
+	ctsduration = 0;
+	if (flags & (HAL_TXDESC_CTSENA | HAL_TXDESC_RTSENA)) {
+		cix = ath_tx_findrix(rt, params->ibp_ctsrate);
+		ctsrate = rt->info[cix].rateCode;
+		if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) {
+			ctsrate |= rt->info[cix].shortPreamble;
+			if (flags & HAL_TXDESC_RTSENA)		/* SIFS + CTS */
+				ctsduration += rt->info[cix].spAckDuration;
+			ctsduration += ath_hal_computetxtime(ah,
+				rt, pktlen, rix, AH_TRUE);
+			if ((flags & HAL_TXDESC_NOACK) == 0)	/* SIFS + ACK */
+				ctsduration += rt->info[rix].spAckDuration;
+		} else {
+			if (flags & HAL_TXDESC_RTSENA)		/* SIFS + CTS */
+				ctsduration += rt->info[cix].lpAckDuration;
+			ctsduration += ath_hal_computetxtime(ah,
+				rt, pktlen, rix, AH_FALSE);
+			if ((flags & HAL_TXDESC_NOACK) == 0)	/* SIFS + ACK */
+				ctsduration += rt->info[rix].lpAckDuration;
+		}
+		ismrr = 0;			/* XXX */
+	} else
+		ctsrate = 0;
+	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.
+	 */
+	atype = HAL_PKT_TYPE_PSPOLL;
+
+	if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
+		ieee80211_dump_pkt(ic, mtod(m0, caddr_t), m0->m_len,
+			sc->sc_hwmap[txrate].ieeerate, -1);
+	
+	if (bpf_peers_present(ic->ic_rawbpf))
+		bpf_mtap(ic->ic_rawbpf, m0);
+	if (bpf_peers_present(sc->sc_drvbpf)) {
+		u_int64_t tsf = ath_hal_gettsf64(ah);
+
+		sc->sc_tx_th.wt_tsf = htole64(tsf);
+		sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags;
+		if (wh->i_fc[1] & IEEE80211_FC1_WEP)
+			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+		sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate;
+		sc->sc_tx_th.wt_txpower = ni->ni_txpower;
+		sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
+
+		bpf_mtap2(sc->sc_drvbpf,
+			&sc->sc_tx_th, sc->sc_tx_th_len, m0);
+	}
+
+	/*
+	 * Formulate first tx descriptor with tx controls.
+	 */
+	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 */
+		, HAL_TXKEYIX_INVALID	/* key cache index */
+		, txantenna		/* antenna mode */
+		, flags			/* flags */
+		, ctsrate		/* rts/cts rate */
+		, ctsduration		/* rts/cts duration */
+	);
+	bf->bf_flags = flags;
+
+	if (ismrr) {
+		rix = ath_tx_findrix(rt, 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(rt, 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(rt, 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 */
+		);
+	}
+
+	/*
+	 * 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.
+	 */
+	txq = sc->sc_ac2q[pri];
+	if (ismcast && (ic->ic_ps_sta || sc->sc_mcastq.axq_depth))
+		txq = &sc->sc_mcastq;
+	ath_tx_handoff(sc, txq, bf);
+	return 0;
+}
+
+static int
+ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+	const struct ieee80211_bpf_params *params)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ath_softc *sc = ifp->if_softc;
+	struct ath_buf *bf;
+
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) {
+		ieee80211_free_node(ni);
+		m_freem(m);
+		return ENETDOWN;
+	}
+	/*
+	 * Grab a TX buffer and associated resources.
+	 */
+	ATH_TXBUF_LOCK(sc);
+	bf = STAILQ_FIRST(&sc->sc_txbuf);
+	if (bf != NULL)
+		STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+	ATH_TXBUF_UNLOCK(sc);
+	if (bf == NULL) {
+		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of xmit buffers\n",
+			__func__);
+		sc->sc_stats.ast_tx_qstop++;
+		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+		ieee80211_free_node(ni);
+		m_freem(m);
+		return ENOBUFS;
+	}
+
+	ifp->if_opackets++;
+	sc->sc_stats.ast_tx_raw++;
+
+	if (params == NULL) {
+		/*
+		 * Legacy path; interpret frame contents to decide
+		 * precisely how to send the frame.
+		 */
+		if (ath_tx_start(sc, ni, bf, m))
+			goto bad;
+	} else {
+		/*
+		 * Caller supplied explicit parameters to use in
+		 * sending the frame.
+		 */
+		if (ath_tx_raw_start(sc, ni, bf, m, params))
+			goto bad;
+	}
+	ifp->if_timer = 5;
+
+	return 0;
+bad:
+	ifp->if_oerrors++;
+	ATH_TXBUF_LOCK(sc);
+	STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+	ATH_TXBUF_UNLOCK(sc);
+	ieee80211_free_node(ni);
+	return EIO;		/* XXX */
+}
+
 /*
  * Announce various information on device/driver attach.
  */
Index: if_athrate.h
===================================================================
RCS file: /home/cvs/src/sys/dev/ath/if_athrate.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/dev/ath/if_athrate.h -L sys/dev/ath/if_athrate.h -u -r1.1.1.1 -r1.2
--- sys/dev/ath/if_athrate.h
+++ sys/dev/ath/if_athrate.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
  * Copyright (c) 2004 Video54 Technologies, Inc.
  * All rights reserved.
  *
@@ -8,18 +8,11 @@
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer,
-    without modification.
+ *    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.
- * 3. Neither the names of the above-listed copyright holders nor the names
- *    of any contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * NO WARRANTY
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@@ -34,7 +27,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: src/sys/dev/ath/if_athrate.h,v 1.4 2005/04/02 18:54:30 sam Exp $
+ * $FreeBSD: src/sys/dev/ath/if_athrate.h,v 1.6 2007/06/06 15:49:15 sam Exp $
  */
 #ifndef _ATH_RATECTRL_H_
 #define _ATH_RATECTRL_H_
@@ -135,6 +128,7 @@
  * for packets that were successfully sent and for those that
  * failed (consult the descriptor for details).
  */
+struct ath_buf;
 void	ath_rate_tx_complete(struct ath_softc *, struct ath_node *,
-		const struct ath_desc *last, const struct ath_desc *first);
+		const struct ath_buf *);
 #endif /* _ATH_RATECTRL_H_ */
Index: if_ath_pci.c
===================================================================
RCS file: /home/cvs/src/sys/dev/ath/if_ath_pci.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -L sys/dev/ath/if_ath_pci.c -L sys/dev/ath/if_ath_pci.c -u -r1.1.1.1 -r1.2
--- sys/dev/ath/if_ath_pci.c
+++ sys/dev/ath/if_ath_pci.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -12,13 +12,6 @@
  *    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.
- * 3. Neither the names of the above-listed copyright holders nor the names
- *    of any contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * NO WARRANTY
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@@ -35,7 +28,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/sys/dev/ath/if_ath_pci.c,v 1.12 2005/03/05 19:06:12 imp Exp $");
+__FBSDID("$FreeBSD: src/sys/dev/ath/if_ath_pci.c,v 1.19 2007/06/06 15:49:15 sam Exp $");
 
 /*
  * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver.
@@ -148,8 +141,9 @@
 		device_printf(dev, "cannot map register space\n");
 		goto bad;
 	}
-	sc->sc_st = rman_get_bustag(psc->sc_sr);
-	sc->sc_sh = rman_get_bushandle(psc->sc_sr);
+	/* XXX uintptr_t is a bandaid for ia64; to be fixed */
+	sc->sc_st = (HAL_BUS_TAG)(uintptr_t) rman_get_bustag(psc->sc_sr);
+	sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr);
 	/*
 	 * Mark device invalid so any interrupts (shared or otherwise)
 	 * that arrive before the HAL is setup are discarded.
@@ -168,7 +162,7 @@
 	}
 	if (bus_setup_intr(dev, psc->sc_irq,
 			   INTR_TYPE_NET | INTR_MPSAFE,
-			   ath_intr, sc, &psc->sc_ih)) {
+			   NULL, ath_intr, sc, &psc->sc_ih)) {
 		device_printf(dev, "could not establish interrupt\n");
 		goto bad2;
 	}
@@ -176,14 +170,14 @@
 	/*
 	 * Setup DMA descriptor area.
 	 */
-	if (bus_dma_tag_create(NULL,			/* parent */
+	if (bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
 			       1, 0,			/* alignment, bounds */
 			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
 			       BUS_SPACE_MAXADDR,	/* highaddr */
 			       NULL, NULL,		/* filter, filterarg */
 			       0x3ffff,			/* maxsize XXX */
 			       ATH_MAX_SCATTER,		/* nsegments */
-			       BUS_SPACE_MAXADDR,	/* maxsegsize */
+			       0x3ffff,			/* maxsegsize XXX */
 			       BUS_DMA_ALLOCNOW,	/* flags */
 			       NULL,			/* lockfunc */
 			       NULL,			/* lockarg */
@@ -195,8 +189,8 @@
 	ATH_LOCK_INIT(sc);
 
 	error = ath_attach(pci_get_device(dev), sc);
-	if (error == 0)
-		return error;
+	if (error == 0)					/* success */
+		return 0;
 
 	ATH_LOCK_DESTROY(sc);
 	bus_dma_tag_destroy(sc->sc_dmat);
Index: if_athioctl.h
===================================================================
RCS file: /home/cvs/src/sys/dev/ath/if_athioctl.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -L sys/dev/ath/if_athioctl.h -L sys/dev/ath/if_athioctl.h -u -r1.1.1.2 -r1.2
--- sys/dev/ath/if_athioctl.h
+++ sys/dev/ath/if_athioctl.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -12,13 +12,6 @@
  *    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.
- * 3. Neither the names of the above-listed copyright holders nor the names
- *    of any contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
  *
  * NO WARRANTY
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@@ -33,7 +26,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  * THE POSSIBILITY OF SUCH DAMAGES.
  *
- * $FreeBSD: src/sys/dev/ath/if_athioctl.h,v 1.10.2.3 2006/02/24 19:51:11 sam Exp $
+ * $FreeBSD: src/sys/dev/ath/if_athioctl.h,v 1.19 2007/06/11 03:36:49 sam Exp $
  */
 
 /*
@@ -76,8 +69,8 @@
 	u_int32_t	ast_tx_shortpre;/* tx frames with short preamble */
 	u_int32_t	ast_tx_altrate;	/* tx frames with alternate rate */
 	u_int32_t	ast_tx_protect;	/* tx frames with protection */
-	u_int32_t	ast_unused1;
-	u_int32_t	ast_unused2;
+	u_int32_t	ast_tx_ctsburst;/* tx frames with cts and bursting */
+	u_int32_t	ast_tx_ctsext;	/* tx frames with cts extension */
 	u_int32_t	ast_rx_nombuf;	/* rx setup failed 'cuz no mbuf */
 	u_int32_t	ast_rx_busdma;	/* rx setup failed for dma resrcs */
 	u_int32_t	ast_rx_orn;	/* rx failed 'cuz of desc overrun */
@@ -94,6 +87,7 @@
 	u_int32_t	ast_rx_ctl;	/* rx discarded 'cuz ctl frame */
 	int8_t		ast_tx_rssi;	/* tx rssi of last ack */
 	int8_t		ast_rx_rssi;	/* rx rssi from histogram */
+	u_int8_t	ast_tx_rate;	/* IEEE rate of last unicast tx */
 	u_int32_t	ast_be_xmit;	/* beacons transmitted */
 	u_int32_t	ast_be_nombuf;	/* beacon setup failed 'cuz no mbuf */
 	u_int32_t	ast_per_cal;	/* periodic calibration calls */
@@ -106,7 +100,16 @@
 	u_int32_t	ast_ant_txswitch;/* tx antenna switches */
 	u_int32_t	ast_ant_rx[8];	/* rx frames with antenna */
 	u_int32_t	ast_ant_tx[8];	/* tx frames with antenna */
-	u_int32_t	ast_pad[32];
+	u_int32_t	ast_cabq_xmit;	/* cabq frames transmitted */
+	u_int32_t	ast_cabq_busy;	/* cabq found busy */
+	u_int32_t	ast_tx_raw;	/* tx frames through raw api */
+	u_int32_t	ast_ff_txok;	/* fast frames tx'd successfully */
+	u_int32_t	ast_ff_txerr;	/* fast frames tx'd w/ error */
+	u_int32_t	ast_ff_rx;	/* fast frames rx'd */
+	u_int32_t	ast_ff_flush;	/* fast frames flushed from staging q */
+	u_int32_t	ast_tx_qfull;	/* tx dropped 'cuz of queue limit */
+	int8_t		ast_rx_noise;	/* rx noise floor */
+	u_int32_t	ast_pad[22];
 };
 
 #define	SIOCGATHSTATS	_IOWR('i', 137, struct ifreq)
@@ -133,10 +136,10 @@
 	(1 << IEEE80211_RADIOTAP_TSFT)		| \
 	(1 << IEEE80211_RADIOTAP_FLAGS)		| \
 	(1 << IEEE80211_RADIOTAP_RATE)		| \
-	(1 << IEEE80211_RADIOTAP_CHANNEL)	| \
 	(1 << IEEE80211_RADIOTAP_ANTENNA)	| \
 	(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL)	| \
 	(1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)	| \
+	(1 << IEEE80211_RADIOTAP_XCHANNEL)	| \
 	0)
 
 struct ath_rx_radiotap_header {
@@ -144,20 +147,23 @@
 	u_int64_t	wr_tsf;
 	u_int8_t	wr_flags;
 	u_int8_t	wr_rate;
-	u_int16_t	wr_chan_freq;
-	u_int16_t	wr_chan_flags;
-	u_int8_t	wr_antsignal;
-	u_int8_t	wr_antnoise;
+	int8_t		wr_antsignal;
+	int8_t		wr_antnoise;
 	u_int8_t	wr_antenna;
-};
+	u_int8_t	wr_pad[3];
+	u_int32_t	wr_chan_flags;
+	u_int16_t	wr_chan_freq;
+	u_int8_t	wr_chan_ieee;
+	int8_t		wr_chan_maxpow;
+} __packed;
 
 #define ATH_TX_RADIOTAP_PRESENT (		\
 	(1 << IEEE80211_RADIOTAP_TSFT)		| \
 	(1 << IEEE80211_RADIOTAP_FLAGS)		| \
 	(1 << IEEE80211_RADIOTAP_RATE)		| \
-	(1 << IEEE80211_RADIOTAP_CHANNEL)	| \
 	(1 << IEEE80211_RADIOTAP_DBM_TX_POWER)	| \
 	(1 << IEEE80211_RADIOTAP_ANTENNA)	| \
+	(1 << IEEE80211_RADIOTAP_XCHANNEL)	| \
 	0)
 
 struct ath_tx_radiotap_header {
@@ -165,10 +171,12 @@
 	u_int64_t	wt_tsf;
 	u_int8_t	wt_flags;
 	u_int8_t	wt_rate;
-	u_int16_t	wt_chan_freq;
-	u_int16_t	wt_chan_flags;
 	u_int8_t	wt_txpower;
 	u_int8_t	wt_antenna;
-};
+	u_int32_t	wt_chan_flags;
+	u_int16_t	wt_chan_freq;
+	u_int8_t	wt_chan_ieee;
+	int8_t		wt_chan_maxpow;
+} __packed;
 
 #endif /* _DEV_ATH_ATHIOCTL_H */


More information about the Midnightbsd-cvs mailing list